diff -u --recursive --new-file v1.3.6/linux/CREDITS linux/CREDITS
--- v1.3.6/linux/CREDITS	Tue Jun 27 14:11:30 1995
+++ linux/CREDITS	Wed Jul  5 13:06:26 1995
@@ -639,6 +639,17 @@
 S: FIN-00330 Helsingfors
 S: Finland
 
+N: Jonathan Naylor
+E: jsn@cs.nott.ac.uk
+E: g4klx@amsat.org
+E: G4KLX@GB7DAD (Packet Radio)
+D: AX.25 and NET/ROM protocol suites
+S: 24 Castle View Drive
+S: Cromford
+S: Matlock
+S: Derbyshire DE4 3RL
+S: United Kingdom
+
 N: Michael Neuffer
 E: neuffer@goofy.zdv.uni-mainz.de
 D: developer and maintainer of the eata_dma SCSI driver 
diff -u --recursive --new-file v1.3.6/linux/Makefile linux/Makefile
--- v1.3.6/linux/Makefile	Fri Jun 30 16:22:25 1995
+++ linux/Makefile	Fri Jun 30 16:23:10 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 6
+SUBLEVEL = 7
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.6/linux/README.modules linux/README.modules
--- v1.3.6/linux/README.modules	Sun Mar 26 11:56:23 1995
+++ linux/README.modules	Sat Jul  1 19:05:58 1995
@@ -42,12 +42,19 @@
 		eexpress, depca,
 		ewrk3, apricot
 
+	Most CDROM drivers:
+		aztcd:     Aztech,Orchid,Okano,Wearnes
+		cm206:     Philips/LMS CM206
+		gscd:      Goldstar GCDR-420
+		mcd, mcdx: Mitsumi LU005, FX001
+		optcd:     Optics Storage Dolphin 8000AT
+		sbpcd:     Matsushita/Panasonic CR52x, CR56x, CD200,
+		           Longshine LCS-7260, TEAC CD-55A
+		sonycd535: Sony CDU-531/535, CDU-510/515
+
 	Some misc modules:
 		lp: line printer
 		binfmt_elf: elf loader
-                sbpcd:     CDROM-driver for Matsushita,Panasonic CR52x,CR56x
-                sonycd535: CDROM-driver for Sony CD535
-                aztcd:     CDROM-driver for Aztech,Orchid,Okano,Wearnes
 
 When you have made the kernel, you create the modules by doing:
 
diff -u --recursive --new-file v1.3.6/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.6/linux/arch/alpha/config.in	Thu Jun 29 19:02:38 1995
+++ linux/arch/alpha/config.in	Thu Jul  6 13:22:04 1995
@@ -27,7 +27,7 @@
  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE n
 fi
 bool 'System V IPC' CONFIG_SYSVIPC y
-bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
 
 comment 'Loadable module support'
 bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
@@ -150,7 +150,7 @@
 	fi
 	bool '3c509/3c579 support' CONFIG_EL3 y
 fi
-bool 'Other ISA cards' CONFIG_NET_ISA n
+bool 'Other ISA cards' CONFIG_NET_ISA y
 if [ "$CONFIG_NET_ISA" = "y" ]; then
 	bool 'Arcnet support' CONFIG_ARCNET n
 	bool 'Cabletron E21xx support' CONFIG_E2100 n
diff -u --recursive --new-file v1.3.6/linux/arch/alpha/lib/checksum.c linux/arch/alpha/lib/checksum.c
--- v1.3.6/linux/arch/alpha/lib/checksum.c	Fri Jun 16 22:02:54 1995
+++ linux/arch/alpha/lib/checksum.c	Thu Jul  6 13:22:04 1995
@@ -4,15 +4,22 @@
  * This file contains network checksum routines that are better done
  * in an architecture-specific manner due to speed..
  */
+ 
+#include <linux/string.h>
 
-/*
- *	This is a version of ip_compute_csum() optimized for IP headers,
- *	which always checksum on 4 octet boundaries.
- */
-unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl)
+#include <asm/byteorder.h>
+
+static inline unsigned short from64to16(unsigned long x)
 {
-	/* not yet */
-	return 0;
+	/* add up 32-bit words for 33 bits */
+	x = (x & 0xffffffff) + (x >> 32);
+	/* add up 16-bit and 17-bit words for 17+c bits */
+	x = (x & 0xffff) + (x >> 16);
+	/* add up 16-bit and 2-bit for 16+c bit */
+	x = (x & 0xffff) + (x >> 16);
+	/* add up carry.. */
+	x = (x & 0xffff) + (x >> 16);
+	return x;
 }
 
 /*
@@ -25,8 +32,84 @@
 				   unsigned short proto,
 				   unsigned int sum)
 {
-	/* not yet */
-	return 0;
+	return ~from64to16(saddr + daddr + sum + (ntohs(len) << 16) + (proto << 8));
+}
+
+/*
+ * Do a 64-bit checksum on an arbitrary memory area..
+ *
+ * This isn't a great routine, but it's not _horrible_ either. The
+ * inner loop could be unrolled a bit further, and there are better
+ * ways to do the carry, but this is reasonable.
+ */
+static inline unsigned long do_csum(unsigned char * buff, int len)
+{
+	int odd, count;
+	unsigned long result = 0;
+
+	if (len <= 0)
+		goto out;
+	odd = 1 & (unsigned long) buff;
+	if (odd) {
+		result = *buff << 8;
+		len--;
+		buff++;
+	}
+	count = len >> 1;		/* nr of 16-bit words.. */
+	if (count) {
+		if (2 & (unsigned long) buff) {
+			result += *(unsigned short *) buff;
+			count--;
+			len -= 2;
+			buff += 2;
+		}
+		count >>= 1;		/* nr of 32-bit words.. */
+		if (count) {
+			if (4 & (unsigned long) buff) {
+				result += *(unsigned int *) buff;
+				count--;
+				len -= 4;
+				buff += 4;
+			}
+			count >>= 1;	/* nr of 64-bit words.. */
+			if (count) {
+				unsigned long carry = 0;
+				do {
+					unsigned long w = *(unsigned long *) buff;
+					count--;
+					buff += 8;
+					result += carry;
+					result += w;
+					carry = (w > result);
+				} while (count);
+				result += carry;
+			}
+			if (len & 4) {
+				result += *(unsigned int *) buff;
+				buff += 4;
+			}
+		}
+		if (len & 2) {
+			result += *(unsigned short *) buff;
+			buff += 2;
+		}
+	}
+	if (len & 1)
+		result += *buff;
+	result = from64to16(result);
+	if (odd)
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+	return result;
+}
+
+/*
+ *	This is a version of ip_compute_csum() optimized for IP headers,
+ *	which always checksum on 4 octet boundaries.
+ */
+unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl)
+{
+	return ~do_csum(iph,ihl*4);
 }
 
 /*
@@ -43,31 +126,42 @@
  */
 unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum)
 {
-	/* not yet */
-	return 0;
+	unsigned long result = do_csum(buff, len);
+
+	/* add in old sum, and carry.. */
+	result += sum;
+	/* 32+c bits -> 32 bits */
+	result = (result & 0xffffffff) + (result >> 32);
+	return result;
 }
 
 /*
- * the same as csum_partial, but copies from fs:src while it
+ * the same as csum_partial, but copies from src while it
  * checksums
  *
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
 
-unsigned int csum_partial_copyffs( char *src, char *dst, int len, int sum)
+unsigned int csum_partial_copy(char *src, char *dst, int len, int sum)
 {
-	/* not yet */
-	return 0;
+	/*
+	 * The whole idea is to do the copy and the checksum at
+	 * the same time, but we do it the easy way now.
+	 *
+	 * At least csum on the source, not destination, for cache
+	 * reasons..
+	 */
+	sum = csum_partial(src, len, sum);
+	memcpy(dst, src, len);
+	return sum;
 }
 
-
 /*
  * this routine is used for miscellaneous IP-like checksums, mainly
  * in icmp.c
  */
 unsigned short ip_compute_csum(unsigned char * buff, int len)
 {
-	/* not yet */
-	return 0;
+	return ~from64to16(do_csum(buff,len));
 }
diff -u --recursive --new-file v1.3.6/linux/arch/alpha/lib/io.c linux/arch/alpha/lib/io.c
--- v1.3.6/linux/arch/alpha/lib/io.c	Thu Jun 29 19:02:38 1995
+++ linux/arch/alpha/lib/io.c	Thu Jul  6 13:22:04 1995
@@ -82,7 +82,7 @@
 
 
 #undef writeb
-void writeb(unsigned short b, unsigned long addr)
+void writeb(unsigned char b, unsigned long addr)
 {
 	__writeb(b, addr);
 }
@@ -264,6 +264,7 @@
  * Copy data from IO memory space to "real" memory space.
  * This needs to be optimized.
  */
+#undef memcpy_fromio
 void memcpy_fromio(void * to, unsigned long from, unsigned long count)
 {
 	while (count) {
@@ -278,6 +279,7 @@
  * Copy data from "real" memory space to IO memory space.
  * This needs to be optimized.
  */
+#undef memcpy_toio
 void memcpy_toio(unsigned long to, void * from, unsigned long count)
 {
 	while (count) {
@@ -292,6 +294,7 @@
  * "memset" on IO memory space.
  * This needs to be optimized.
  */
+#undef memset_io
 void memset_io(unsigned long dst, int c, unsigned long count)
 {
 	while (count) {
diff -u --recursive --new-file v1.3.6/linux/arch/alpha/lib/memcpy.c linux/arch/alpha/lib/memcpy.c
--- v1.3.6/linux/arch/alpha/lib/memcpy.c	Thu Jun  1 13:22:06 1995
+++ linux/arch/alpha/lib/memcpy.c	Wed Jul  5 12:53:22 1995
@@ -5,70 +5,111 @@
  */
 
 /*
- * This is reasonably optimized for the quad-word-aligned case, which
- * happens with page/buffer copies. It's horribly bad for the unaligned
- * case: it could be made much better, but that would require lots of
- * assembly (unaligned 8-byte load + shift + aligned 4-byte store, for
- * example).
+ * This is a reasonably optimized memcpy() routine.
  */
 
-#include <linux/types.h>
+/*
+ * Note that the C code is written to be optimized into good assembly. However,
+ * at this point gcc is unable to sanely compile "if (n >= 0)", resulting in a
+ * explicit compare against 0 (instead of just using the proper "blt reg, xx" or
+ * "bge reg, xx"). I hope alpha-gcc will be fixed to notice this eventually..
+ */
 
-static inline void __memcpy_b(unsigned long d, unsigned long s, long n)
-{
-	while (--n >= 0)
-		*(char *) (d++) = *(char *) (s++);
-}
+#include <linux/types.h>
 
-static inline void __memcpy_q(unsigned long d, unsigned long s, long n)
-{
-	/* this first part could be done in one go with ldq_u*2/mask/stq_u */
-	while (d & 7) {
-		if (--n < 0)
-			return;
-		*(char *) d = *(char *) s;
-		d++;
-		s++;
+/*
+ * This should be done in one go with ldq_u*2/mask/stq_u. Do it
+ * with a macro so that we can fix it up later..
+ */
+#define ALIGN_DEST_TO8(d,s,n) \
+	while (d & 7) { \
+		if (n <= 0) return; \
+		n--; \
+		*(char *) d = *(char *) s; \
+		d++; s++; \
 	}
-	while ((n -= 8) >= 0) {
-		*(unsigned long *) d = *(unsigned long *) s;
-		d += 8;
-		s += 8;
+
+/*
+ * This should similarly be done with ldq_u*2/mask/stq. The destination
+ * is aligned, but we don't fill in a full quad-word
+ */
+#define DO_REST(d,s,n) \
+	while (n > 0) { \
+		n--; \
+		*(char *) d = *(char *) s; \
+		d++; s++; \
 	}
-	/* as could this.. */
-	__memcpy_b(d,s,n+8);
-}	
 
-static inline void __memcpy_l(unsigned long d, unsigned long s, long n)
+/*
+ * This should be done with ldq/mask/stq. The source and destination are
+ * aligned, but we don't fill in a full quad-word
+ */
+#define DO_REST_ALIGNED(d,s,n) DO_REST(d,s,n)
+
+/*
+ * This does unaligned memory copies. We want to avoid storing to
+ * an unaligned address, as that would do a read-modify-write cycle.
+ * We also want to avoid double-reading the unaligned reads.
+ *
+ * Note the ordering to try to avoid load (and address generation) latencies.
+ */
+static inline void __memcpy_unaligned(unsigned long d, unsigned long s, long n)
 {
-	while (d & 3) {
-		if (--n < 0)
-			return;
-		*(char *) d = *(char *) s;
-		d++;
-		s++;
+	ALIGN_DEST_TO8(d,s,n);
+	n -= 8;			/* to avoid compare against 8 in the loop */
+	if (n >= 0) {
+		unsigned long low_word, high_word;
+		__asm__("ldq_u %0,%1":"=r" (low_word):"m" (*(unsigned long *) s));
+		do {
+			unsigned long tmp;
+			__asm__("ldq_u %0,%1":"=r" (high_word):"m" (*(unsigned long *)(s+8)));
+			n -= 8;
+			__asm__("extql %1,%2,%0"
+				:"=r" (low_word)
+				:"r" (low_word), "r" (s));
+			__asm__("extqh %1,%2,%0"
+				:"=r" (tmp)
+				:"r" (high_word), "r" (s));
+			s += 8;
+			*(unsigned long *) d = low_word | tmp;
+			d += 8;
+			low_word = high_word;
+		} while (n >= 0);
 	}
-	while ((n -= 4) >= 0) {
-		*(unsigned int *) d = *(unsigned int *) s;
-		d += 4;
-		s += 4;
+	n += 8;
+	DO_REST(d,s,n);
+}
+
+/*
+ * Hmm.. Strange. The __asm__ here is there to make gcc use a integer register
+ * for the load-store. I don't know why, but it would seem that using a floating
+ * point register for the move seems to slow things down (very small difference,
+ * though).
+ *
+ * Note the ordering to try to avoid load (and address generation) latencies.
+ */
+static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n)
+{
+	ALIGN_DEST_TO8(d,s,n);
+	n -= 8;
+	while (n >= 0) {
+		unsigned long tmp;
+		__asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s));
+		n -= 8;
+		s += 8;
+		*(unsigned long *) d = tmp;
+		d += 8;
 	}
-	__memcpy_b(d,s,n+4);
-}	
+	n += 8;
+	DO_REST_ALIGNED(d,s,n);
+}
 
 void * __memcpy(void * dest, const void *src, size_t n)
 {
-	unsigned long differ;
-	differ = ((unsigned long) dest ^ (unsigned long) src) & 7;
-
-	if (!differ) {
-		__memcpy_q((unsigned long) dest, (unsigned long) src, n);
-		return dest;
-	}
-	if (differ == 4) {
-		__memcpy_l((unsigned long) dest, (unsigned long) src, n);
+	if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) {
+		__memcpy_aligned((unsigned long) dest, (unsigned long) src, n);
 		return dest;
 	}
-	__memcpy_b((unsigned long) dest, (unsigned long) src, n);
+	__memcpy_unaligned((unsigned long) dest, (unsigned long) src, n);
 	return dest;
 }
diff -u --recursive --new-file v1.3.6/linux/arch/alpha/lib/memset.c linux/arch/alpha/lib/memset.c
--- v1.3.6/linux/arch/alpha/lib/memset.c	Mon Jan 30 06:39:28 1995
+++ linux/arch/alpha/lib/memset.c	Wed Jul  5 12:53:22 1995
@@ -10,7 +10,7 @@
 
 #include <linux/types.h>
 
-void * __constant_c_memset(void * s, unsigned long c, long count)
+inline void * __constant_c_memset(void * s, unsigned long c, long count)
 {
 	unsigned long xs = (unsigned long) s;
 
@@ -35,10 +35,6 @@
 
 void * __memset(void * s,char c,size_t count)
 {
-	char *xs = (char *) s;
-
-	while (count--)
-		*xs++ = c;
-
+	__constant_c_memset(s,0x0101010101010101UL * (unsigned char) c, count);
 	return s;
 }
diff -u --recursive --new-file v1.3.6/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v1.3.6/linux/arch/i386/Makefile	Thu Jun 29 19:02:39 1995
+++ linux/arch/i386/Makefile	Mon Jul  3 10:08:51 1995
@@ -29,11 +29,11 @@
 CPP=$(CC) -E -D__ELF__
 OBJDUMP =objdump
 OBJDUMP_FLAGS=-k -q 
-LDFLAGS=-e startup_32 
+ZLDFLAGS=-e startup_32 
 LDFLAGS=-e stext
 ZIMAGE_OFFSET=0x1000
 IMAGE_OFFSET=0x100000
-ZLINKFLAGS =-Ttext $(ZIMAGE_OFFSET) $(LDFLAGS)
+ZLINKFLAGS =-Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS)
 LINKFLAGS =-Ttext $(IMAGE_OFFSET) $(LDFLAGS)
 
 else
diff -u --recursive --new-file v1.3.6/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.6/linux/arch/i386/config.in	Fri Jun 30 16:22:25 1995
+++ linux/arch/i386/config.in	Mon Jul  3 10:10:52 1995
@@ -30,6 +30,9 @@
 fi
 bool 'System V IPC' CONFIG_SYSVIPC y
 bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
+bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF n
+fi
 #bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 n
 #if [ "$CONFIG_M586" = "n" ]; then
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
@@ -209,9 +212,10 @@
 
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
-bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
-bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
-bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
+bool 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n
+bool 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n
+bool 'Experimental Mitsumi [XA/MultiSession, no Audio] support' CONFIG_MCDX n
+bool 'Matsushita/Panasonic CDROM support' CONFIG_SBPCD n
 if [ "$CONFIG_SBPCD" = "y" ]; then
   bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
   if [ "$CONFIG_SBPCD2" = "y" ]; then
@@ -222,7 +226,11 @@
   fi
 fi
 bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
-bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
+bool 'Sony CDU535 CDROM support' CONFIG_CDU535 n
+bool 'Goldstar R420 CDROM support' CONFIG_GSCD n
+bool 'Philips/LMS CM206 CDROM support' CONFIG_CM206 n
+bool 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD n
+bool 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD n
 
 comment 'Filesystems'
 
@@ -238,13 +246,14 @@
 if [ "$CONFIG_INET" = "y" ]; then
 bool 'NFS filesystem support' CONFIG_NFS_FS y
 fi
-if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then
+if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" -o "$CONFIG_GSCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_OPTCD" = "y" -o "$CONFIG_SJCD" = "y" ]; then
 	bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
 else
 	bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
 fi
 bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
 bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
+bool 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n
 
 comment 'character devices'
 
diff -u --recursive --new-file v1.3.6/linux/arch/i386/lib/checksum.c linux/arch/i386/lib/checksum.c
--- v1.3.6/linux/arch/i386/lib/checksum.c	Fri Jun 30 16:22:25 1995
+++ linux/arch/i386/lib/checksum.c	Thu Jul  6 13:35:59 1995
@@ -101,7 +101,7 @@
  * copy from fs while checksumming, otherwise like csum_partial
  */
 
-unsigned int csum_partial_copyffs( char *src, char *dst, 
+unsigned int csum_partial_copy_fromuser( char *src, char *dst, 
 				  int len, int sum) {
     __asm__("
 	testl $2, %%edi		# Check alignment.
@@ -175,15 +175,15 @@
 	jz 7f
 	cmpl $2, %%ecx
 	jb 5f
-	movw %%fs:(%%esi), %%dx
+	movw %%fs:(%%esi), %%cx
 	leal 2(%%esi), %%esi
-	movw %%dx, (%%edi)
+	movw %%cx, (%%edi)
 	leal 2(%%edi), %%edi
 	je 6f
 	shll $16,%%edx
-5:	movb %%fs:(%%esi), %%dl
-	movb %%dl, (%%edi)
-6:	addl %%edx, %%eax
+5:	movb %%fs:(%%esi), %%cl
+	movb %%cl, (%%edi)
+6:	addl %%ecx, %%eax
 	adcl $0, %%eax
 7:
 	"
@@ -192,6 +192,98 @@
 	: "bx", "cx", "dx", "si", "di" );
     return(sum);
 }
+/*
+ * copy from ds while checksumming, otherwise like csum_partial
+ */
 
+unsigned int csum_partial_copy( char *src, char *dst, 
+				  int len, int sum) {
+    __asm__("
+	testl $2, %%edi		# Check alignment.
+	jz 2f			# Jump if alignment is ok.
+	subl $2, %%ecx		# Alignment uses up two bytes.
+	jae 1f			# Jump if we had at least two bytes.
+	addl $2, %%ecx		# ecx was < 2.  Deal with it.
+	jmp 4f
+1:	movw (%%esi), %%bx
+	addl $2, %%esi
+	movw %%bx, (%%edi)
+	addl $2, %%edi
+	addw %%bx, %%ax
+	adcl $0, %%eax
+2:
+	movl %%ecx, %%edx
+	shrl $5, %%ecx
+	jz 2f
+	testl %%esi, %%esi
+1:	movl (%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, (%%edi)
+
+	movl 4(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 4(%%edi)
+
+	movl 8(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 8(%%edi)
+
+	movl 12(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 12(%%edi)
 
+	movl 16(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 16(%%edi)
+
+	movl 20(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 20(%%edi)
+
+	movl 24(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 24(%%edi)
 
+	movl 28(%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, 28(%%edi)
+
+	lea 32(%%esi), %%esi
+	lea 32(%%edi), %%edi
+	dec %%ecx
+	jne 1b
+	adcl $0, %%eax
+2:	movl %%edx, %%ecx
+	andl $28, %%edx
+	je 4f
+	shrl $2, %%edx
+	testl %%esi, %%esi
+3:	movl (%%esi), %%ebx
+	adcl %%ebx, %%eax
+	movl %%ebx, (%%edi)
+	lea 4(%%esi), %%esi
+	lea 4(%%edi), %%edi
+	dec %%edx
+	jne 3b
+	adcl $0, %%eax
+4:	andl $3, %%ecx
+	jz 7f
+	cmpl $2, %%ecx
+	jb 5f
+	movw (%%esi), %%cx
+	leal 2(%%esi), %%esi
+	movw %%cx, (%%edi)
+	leal 2(%%edi), %%edi
+	je 6f
+	shll $16,%%edx
+5:	movb (%%esi), %%cl
+	movb %%cl, (%%edi)
+6:	addl %%ecx, %%eax
+	adcl $0, %%eax
+7:
+	"
+	: "=a" (sum)
+	: "0"(sum), "c"(len), "S"(src), "D" (dst)
+	: "bx", "cx", "dx", "si", "di" );
+    return(sum);
+}
diff -u --recursive --new-file v1.3.6/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.6/linux/drivers/block/Makefile	Tue Jun 27 14:11:32 1995
+++ linux/drivers/block/Makefile	Sat Jul  1 19:05:58 1995
@@ -35,19 +35,21 @@
 ifdef CONFIG_CDU31A
 OBJS := $(OBJS) cdu31a.o
 SRCS := $(SRCS) cdu31a.c
-endif
+endif #CONFIG_CDU31A
 
 ifdef CONFIG_MCD
 OBJS := $(OBJS) mcd.o
 SRCS := $(SRCS) mcd.c
-endif
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) mcd.o
+endif #CONFIG_MCD
 
-ifdef CONFIG_AZTCD
-OBJS := $(OBJS) aztcd.o
-SRCS := $(SRCS) aztcd.c
+ifdef CONFIG_MCDX
+OBJS := $(OBJS) mcdx.o
+SRCS := $(SRCS) mcdx.c
 else
-BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) aztcd.o
-endif
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) mcdx.o
+endif #CONFIG_MCDX
 
 ifdef CONFIG_SBPCD
 OBJS := $(OBJS) sbpcd.o
@@ -71,6 +73,48 @@
 SRCS := $(SRCS) sbpcd4.c
 endif #CONFIG_SBPCD4
 
+ifdef CONFIG_AZTCD
+OBJS := $(OBJS) aztcd.o
+SRCS := $(SRCS) aztcd.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) aztcd.o
+endif #CONFIG_AZTCD
+
+ifdef CONFIG_CDU535
+OBJS := $(OBJS) sonycd535.o
+SRCS := $(SRCS) sonycd535.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sonycd535.o
+endif #CONFIG_CDU535
+
+ifdef CONFIG_GSCD
+OBJS := $(OBJS) gscd.o
+SRCS := $(SRCS) gscd.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) gscd.o
+endif #CONFIG_GSCD
+
+ifdef CONFIG_CM206
+OBJS := $(OBJS) cm206.o
+SRCS := $(SRCS) cm206.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) cm206.o
+endif #CONFIG_CM206
+
+ifdef CONFIG_OPTCD
+OBJS := $(OBJS) optcd.o
+SRCS := $(SRCS) optcd.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) optcd.o
+endif #CONFIG_OPTCD
+
+ifdef CONFIG_SJCD
+OBJS := $(OBJS) sjcd.o
+SRCS := $(SRCS) sjcd.c
+#else
+#BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sjcd.o
+endif #CONFIG_SJCD
+
 ifdef CONFIG_BLK_DEV_HD
 OBJS := $(OBJS) hd.o
 SRCS := $(SRCS) hd.c
@@ -84,13 +128,6 @@
 ifdef CONFIG_BLK_DEV_XD
 OBJS := $(OBJS) xd.o
 SRCS := $(SRCS) xd.c
-endif
-
-ifdef CONFIG_CDU535
-OBJS := $(OBJS) sonycd535.o
-SRCS := $(SRCS) sonycd535.c
-else
-BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sonycd535.o
 endif
 
 all: block.a
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.cdu31a linux/drivers/block/README.cdu31a
--- v1.3.6/linux/drivers/block/README.cdu31a	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.cdu31a	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,160 @@
+   Tips for using cdu31a.c, the driver for Sony CDU-31A and CDU-33A CDROM
+   drives.
+  
+   Corey Minyard (minyard@wf-rch.cirr.com)
+  
+   Colossians 3:17
+  
+   The Sony interface device driver handles Sony interface CDROM
+   drives and provides a complete block-level interface as well as an
+   ioctl() interface compatible with the Sun (as specified in
+   include/linux/cdrom.h).  With this interface, CDROMs can be
+   accessed and standard audio CDs can be played back normally.
+  
+   WARNING - 	All autoprobes have been removed from the driver.
+  		You MUST configure the CDU31A via a LILO config
+  		at boot time or in lilo.conf.  I have the
+  		following in my lilo.conf:
+  
+                  append="cdu31a=0x1f88,0,PAS"
+  
+  		The first number is the I/O base address of the
+  		card.  The second is the interrupt (0 means none).
+  		The third should be "PAS" if on a Pro-Audio
+  		spectrum, or nothing if on something else.
+  
+   This interface is (unfortunately) a polled interface.  This is
+   because most Sony interfaces are set up with DMA and interrupts
+   disables.  Some (like mine) do not even have the capability to
+   handle interrupts or DMA.  For this reason you will see a lot of
+   the following:
+  
+     retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+     while ((retry_count > jiffies) && (! <some condition to wait for))
+     {
+        while (handle_sony_cd_attention())
+           ;
+  
+        sony_sleep();
+     }
+     if (the condition not met)
+     {
+        return an error;
+     }
+  
+   This ugly hack waits for something to happen, sleeping a little
+   between every try.  it also handles attentions, which are
+   asynchronous events from the drive informing the driver that a disk
+   has been inserted, removed, etc.
+  
+   NEWS FLASH - The driver now supports interrupts but they are
+   turned off by default.  Use of interrupts is highly encouraged, it
+   cuts CPU usage down to a reasonable level.  I had DMA in for a while
+   but PC DMA is just too slow.  Better to just insb() it.
+  
+   One thing about these drives: They talk in MSF (Minute Second Frame) format.
+   There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+   disk.  The funny thing is that these are sent to the drive in BCD, but the
+   interface wants to see them in decimal.  A lot of conversion goes on.
+  
+   DRIVER SPECIAL FEATURES
+   -----------------------
+  
+   This section describes features beyond the normal audio and CD-ROM
+   functions of the drive.
+  
+   2048 byte buffer mode
+  
+   If a disk is mounted with -o block=2048, data is copied straight
+   from the drive data port to the buffer.  Otherwise, the readahead
+   buffer must be involved to hold the other 1K of data when a 1K
+   block operation is done.  Note that with 2048 byte blocks you
+   cannot execute files from the CD.
+  
+   XA compatibility
+  
+   The driver should support XA disks for both the CDU31A and CDU33A.
+   It does this transparently, the using program doesn't need to set it.
+  
+   Multi-Session
+  
+   A multi-session disk looks just like a normal disk to the user.
+   Just mount one normally, and all the data should be there.
+   A special thanks to Koen for help with this!
+   
+   Raw sector I/O
+  
+   Using the CDROMREADAUDIO it is possible to read raw audio and data
+   tracks.  Both operations return 2352 bytes per sector.  On the data
+   tracks, the first 12 bytes is not returned by the drive and the value
+   of that data is indeterminate.
+  
+  
+    Copyright (C) 1993  Corey Minyard
+  
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+  
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+  
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  
+  /
+
+/*
+  
+   Setting up the Sony CDU31A/CDU33A drive interface card.  If
+   You have another card, you are on your own.
+   
+        +----------+-----------------+----------------------+
+        |  JP1     |  34 Pin Conn    |                      |
+        |  JP2     +-----------------+                      |
+        |  JP3                                              |
+        |  JP4                                              |
+        |                                                   +--+
+        |                                                   |  +-+
+        |                                                   |  | |  External
+        |                                                   |  | |  Connector
+        |                                                   |  | |
+        |                                                   |  +-+
+        |                                                   +--+
+        |                                                   |
+        |                                          +--------+
+        |                                          |
+        +------------------------------------------+
+   
+      JP1 sets the Base Address, using the following settings:
+   
+        Address         Pin 1           Pin 2
+        -------         -----           -----
+        0x320           Short           Short
+        0x330           Short           Open
+        0x340           Open            Short
+        0x360           Open            Open
+   
+      JP2 and JP3 configure the DMA channel; they must be set the same.
+   
+        DMA             Pin 1           Pin 2           Pin 3
+        ---             -----           -----           -----
+        1               On              Off             On
+        2               Off             On              Off
+        3               Off             Off             On
+   
+      JP4 Configures the IRQ:
+   
+        IRQ     Pin 1           Pin 2           Pin 3           Pin 4
+        ---     -----           -----           -----           -----
+        3       Off             Off             On              Off
+        4       Off             Off*            Off             On
+        5       On              Off             Off             Off
+        6       Off             On              Off             Off
+   
+                  The documentation states to set this for interrupt
+                  4, but I think that is a mistake.
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.cm206 linux/drivers/block/README.cm206
--- v1.3.6/linux/drivers/block/README.cm206	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.cm206	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,171 @@
+This is the readme file for the driver for the Philips/LMS cdrom drive
+cm206 in combination with the cm260 host adapter card. 
+
+				(c) 1995 David A. van Leeuwen
+   
+Features as of version 0.33
+---------------------------
+- Full audio support, that is, both  workman, workbone and cdp work
+  now reasonably. Reading TOC still takes some time. 
+- Made auto-probe code a little better, i hope
+
+Features as of version 0.28
+---------------------------
+- Full speed stransfer rate (300 kB/s).
+- Minimum kernel memory usage for buffering (less than 3 kB).
+- Multisession support.
+- Tray locking.
+- Statistcics of driver accessible to the user.
+- Module support.
+- Auto-probing of adapter card's base port and irq line,
+  also configurable at boot time or module load time.
+
+Features still lacking
+----------------------
+- cm205ms+cm250 support. (I do have cm205ms docs now. I still have to
+  study Kai Petzke's cm205 drives to understand the workings of the
+  cm250 adapter card. Don't bet on me, write a driver yourself!)
+
+
+Decide how you are going to use the driver. There are two
+options:
+
+   (a) installing the driver as a resident part of the kernel
+   (b) compiling the driver as a loadable module
+
+   Further, you must decide if you are going to specify the base port
+   address and the interrupt request line of the adapter card cm260 as
+   boot options for (a), module parameters for (b), use automatic
+   probing of these values, or hard-wire your adaptor cards settings
+   into the source code. If you don't care, you can choose for
+   autoprobing, which is the default. In that case you can move on to
+   the next step.
+
+   If autoprobing does not work, you can hard-wire the default values
+   of the base port address (CM206_BASE) and interrupt request line
+   (CM206_IRQ) into the file ./linux/drivers/block/cm206.c. Uncomment
+   the defines of CM206_IRQ and CM206_BASE, and change there meaning
+   to the appropriate values, if necessary.
+
+Compiling the kernel
+--------------------
+1) move to /usr/src/linux and do a 
+
+	make config
+
+   If you have chosen for option (a), answer yes to CONFIG_CM206 and
+   CONFIG_ISO9660_FS (should be default).
+
+   If you have chosen for option (b), answer yes to CONFIG_MODVERSIONS
+   and no (!) to CONFIG_CM206 and CONFIG_ISO9660_FS. 
+
+2) then do a 
+	
+	make dep; make zImage; make modules
+
+3) do the usual things to install a new image (backup the old one, run
+   `rdev -R zImage 1', copy the new image in place, run lilo).  Might
+   be `make zlilo'.
+
+Using the driver as a module
+----------------------------
+If you will only seldomly use the cd-rom driver, you can choose for
+option (b), install as a loadable module. You may have to re-compile
+the module when you upgrade the kernel to a new version. Read the file
+`README.modules' in /usr/src/linux. To install the module, you use the 
+command, as root
+
+	insmod /usr/src/linux/modules/cm206.o
+
+You can specify the base address on the command line as well as the irq 
+line to be used, e.g.
+
+	insmod /usr/src/linux/modules/cm206.o cm206=0x300,11
+
+You may also have to install the file-system module `iso9660.o', if you
+didn't compile that into the kernel. If you use `tcsh' as shell, you
+might consider defining
+
+	alias listinstalledmodules 'cat /proc/modules | awk \{print\$1\}'
+	complete rmmod		'p/1/`listinstalledmodules`/'
+	alias listcompiledmodules '(cd /usr/src/linux/modules; \ls -o *.o)'
+	alias insmod		'insmod /usr/src/linux/modules/\!:1 \!:2*'
+	complete insmod		'p/1/`listcompiledmodules`/'
+
+which makes typing insmod and rmmod somewhat easier. 
+
+Using the driver as part of the kernel
+--------------------------------------
+If you have chosen for option a, you can specify the base-port
+address and irq on the lilo boot command line, e.g.:
+
+	LILO: linux cm206=0x340,11
+
+This assumes that your linux kernel image keyword is `linux'. 
+If you may specify either IRQ (3--11) or base port (0x300--0x370),
+auto probing is turned off for both settings, thus setting the 
+other value to the compiled-in default.
+
+Mounting the cdrom
+------------------
+1) Make sure that there is the right device installed in /dev.
+
+  	mknod /dev/cm206cd b 32 0
+
+2) Make sure there is a mount point, e.g., /cdrom 
+
+	mkdir /cdrom
+
+3) mount using a command like this (run as root):
+
+	mount -rt iso9660 /dev/cm206cd /cdrom
+
+4) For user-mounts, add a line in /etc/fstab
+
+	/dev/cm206cd      /cdrom     iso9660 	ro,noauto,user
+
+   This will allow users to give the commands
+
+	mount /cdrom
+	umount /cdrom
+
+If things don't work
+--------------------
+
+- Try to do a `tail /var/adm/messages' to find out if the driver
+  said anything about what is going wrong during the initialization.
+
+- Try to do a `dd if=/dev/cm206cd | od -tc | less' to read from the
+  CD.
+
+- Look in the /proc directory to see if `cm206' shows up under
+  one of `interrupts', `ioports', `devices' or `modules' (if
+  applicable). 
+
+
+DISCLAIMER 
+---------- 
+I cannot guarantee that this driver works, or that the hardware will
+not be harmed, although i consider it most unlikely. 
+
+I hope that you'll find this driver in some way useful. 
+
+					David van Leeuwen
+					david@tm.tno.nl
+
+Note for Linux CDROM vendors
+-----------------------------
+You are encouraged to include this driver on your Linux CDROM. If
+you do, you might consider sending me a free copy of that cd-rom.
+You can contact me through my e-mail address, david@tm.tno.nl. 
+If this driver is compiled into a kernel to boot off a cdrom, 
+you should actually send me a free copy of that cd-rom. 
+
+Copyright
+---------
+The copyright of the cm206 driver for Linux is 
+
+    (c) 1995 David A. van Leeuwen
+
+The driver is released, like most Linux software, under the conditions 
+of the GNU general public license.
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.gscd linux/drivers/block/README.gscd
--- v1.3.6/linux/drivers/block/README.gscd	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.gscd	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,60 @@
+	Goldstar R420 CD-Rom device driver README
+
+For all kind of other information about the GoldStar R420 CDROM
+and this Linux device driver is a WWW-URL Page installed:
+
+        http://linux.rz.fh-hannover.de/~raupach        
+
+
+      If you are the editor of a Linux CD, you should
+      enable gscd.c within your boot floppy kernel. Please,
+      send me one of your CDs for free.
+
+
+This current driver version 0.4a only supports reading data from the disk.
+Currently we have no audio and no multisession or XA support.
+The polling interface is used, no DMA.
+
+
+Sometimes the GoldStar R420 is sold in a 'Reveal Multimedia Kit'. This kit's
+drive interface is compatible, too.
+
+
+Installation
+------------
+
+Change to '/usr/src/linux/include/linux' and edit the file 'gscd.h'. Insert
+the i/o address of your interface card.
+
+The default base address is 0x340. This will work for most applications. 
+Address selection is accomplished by jumpers PN801-1 to PN801-4 on the 
+GoldStar Interface Card.
+Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360
+0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0             
+
+Then go back to '/usr/src/linux/' and 'make config' to build the new
+configuration for your kernel. If you want to use the GoldStar driver
+like a module, don't select 'GoldStar CDROM support'. By the way, you
+have to include the iso9660 filesystem.
+
+Now start compiling the kernel with 'make dep ; make zImage'.
+If you want to use the driver as a module, you have to do 'make modules' 
+and 'make modules_install', additionally.
+Install your new kernel as usual - maybe you do it with 'make zlilo'.
+
+Before you can use the driver, you have to
+   mknod /dev/gscd0 b 16 0
+to create the appropriate device file (once for all times).
+
+If you use modules, you can try to insert the driver.
+Say: 'insmod /usr/src/linux/modules/gscd.o'
+or:  'insmod /usr/src/linux/modules/gscd.o gscd=<address>'
+The driver should report his results now.
+
+That's it! Mount a disk, i.e. 'mount -rt iso9660 /dev/gscd0 /cdrom'
+
+Feel free to report errors and suggestions to the following address.
+Be sure, I'm very happy to receive your comments!
+ 
+        Oliver Raupach                                Hannover, Juni 1995
+(raupach@nwfs1.rz.fh-hannover.de)
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.mcd linux/drivers/block/README.mcd
--- v1.3.6/linux/drivers/block/README.mcd	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.mcd	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,6 @@
+This driver does not support XA or MultiSession CDs (PhotoCDs). Use the
+experimental driver mcdx.c for that.
+
+You can use mcd for one interface, and mcdx for another; this way, you
+can use the audio functions with the "old" driver, and read XA CDs with
+the "new" driver (if you have at least two drives).
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.mcdx linux/drivers/block/README.mcdx
--- v1.3.6/linux/drivers/block/README.mcdx	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.mcdx	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,37 @@
+This is a first attempt to create an `improved' driver for the Mitsumi drives.
+It is able to "live together" with mcd.c, if you have at least two Mitsumi
+drives: each driver can use his own drive.
+
+To allow this "coexistence" as long as mcdx.c is not a superset of mcd.c,
+this driver has to use its own device files. We use MAJOR 20 for it. So,
+you have to do
+ # mknod /dev/mcdx0 b 20 0
+ # mknod /dev/mcdx1 b 20 1
+and so on, one entry for each drive to support, once.
+
+If you are using the driver as a module, you can specify your ports and IRQs
+like
+ # insmod mcdx.o mcdx=0x300,11,0x304,5
+and so on ("address,IRQ" pairs).
+This will override the configuration in mcdx.h.
+
+This driver:
+
+	o	handles XA (and hopefully) multi session CDs as well as
+		ordinary CDs;
+	o	supports up to 5 drives (of course, you'll need free 
+		IRQs, i/o ports and slots);
+	o	uses much less kernel memory than the standard mcd driver
+		(no extra driver internal buffers!).
+
+This version doesn't support yet:
+
+	o	audio functions;
+	o	shared IRQs (but it seems to be possible - I've successfully
+                connected two drives to the same irq.  So it's `only' a 
+                problem of the driver.)
+
+This driver never will:
+
+	o	Read digital audio (i.e. copy directly), due to missing
+		hardware features. 
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.optcd linux/drivers/block/README.optcd
--- v1.3.6/linux/drivers/block/README.optcd	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.optcd	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,38 @@
+This is the README file for the Optics Storage 8000 AT CDROM device driver.
+
+The driver contains code to enable an ISP16 interface if it finds one, so if
+you have that, you're lucky.
+
+My configuration code for the ISP-16 card can get found at
+      dutette.et.tudelft.nl:/pub/linux/
+and at Eberhard's mirror
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
+
+Much more elaborate information can be found at ftp:rbrf.msk.su,
+where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16
+device driver.
+Vadim's directory is
+      rbrf.msk.su:/linux/mediamagic/
+and Eberhard is holding a mirror at
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
+
+
+Before you can use the driver, you have to create the device file once:
+ # mknod /dev/optcd0 b 17 0
+
+To specify the base address if the driver is "compiled-in" to your kernel,
+you can use the kernel command line item (LILO option)
+             optcd=0x340
+with the right address.
+
+If you have compiled optcd as a module, you can load it with
+ # insmod /usr/src/linux/modules/optcd.o
+or
+ # insmod /usr/src/linux/modules/optcd.o optcd=0x340
+with the matching address value of your interface card.
+
+I have tried the module with several 1.2.x kernel versions, and it seems to
+work, as far as I tested. If you use it, I'd appreciate success/failure
+reports.
+
+Leo Spiekman (spiekman@dutette.et.tudelft.nl)
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.sjcd linux/drivers/block/README.sjcd
--- v1.3.6/linux/drivers/block/README.sjcd	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.sjcd	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,39 @@
+				80% of the work takes 20% of the time,
+				20% of the work takes 80% of the time...
+						(Murphy law)
+
+				Once started, training can not be stopped...
+						(StarWars)
+
+This file is meant as a tips & tricks edge for the usage of the SANYO CDR-H94A
+cdrom drive. It will grow as the questions arise. ;-)
+
+The driver should work with any SoundBlaster/Panasonic style CDROM interface,
+including the "soft configurable" MediaMagic sound card.
+To make this sound card (and others like "Mozart") working, it has to get 
+"configured" by software.
+So, you should boot DOS once (and by this, run the "configuration driver")
+and then switch to Linux by use of CTL-ALT-DEL. Each use of the RESET
+button or the power switch makes this procedure necessary again.
+
+Vadim has implemented a Linux "configuration driver" to set up the
+registers of the MediaMagic (ISP-16) sound card. These routines have to get
+integrated into the related CDROM and sound drivers directly at a later
+time. Currently, they reside at Vadim's server
+      rbrf.msk.su:/linux/mediamagic/
+and at Eberhard's mirror
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
+
+Leo Spiekman's configuration routines for the ISP-16 card can get found at
+      dutette.et.tudelft.nl:/pub/linux/
+and at Eberhard's mirror
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
+
+If something is wrong, e-mail to		vadim@rbrf.msk.su
+					or 	vadim@ipsun.ras.ru
+					or	model@cecmow.enet.dec.com
+
+It happens sometimes that Vadim is not reachable by mail. For these
+instances, Eric van der Maarel (maarel@marin.nl) will help, too.
+
+		Vadim V. Model, Eric van der Maarel, Eberhard Moenkeberg
diff -u --recursive --new-file v1.3.6/linux/drivers/block/README.sonycd535 linux/drivers/block/README.sonycd535
--- v1.3.6/linux/drivers/block/README.sonycd535	Mon Jun 12 12:06:02 1995
+++ linux/drivers/block/README.sonycd535	Sat Jul  1 19:05:58 1995
@@ -89,6 +89,8 @@
 	To install the module, simply type:
 
 insmod sony535.o
+	or
+insmod sony535.o sonycd535=<address>
 
 	And to remove it:
 
diff -u --recursive --new-file v1.3.6/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- v1.3.6/linux/drivers/block/blk.h	Tue Jun 27 14:11:33 1995
+++ linux/drivers/block/blk.h	Sat Jul  1 19:05:58 1995
@@ -37,23 +37,42 @@
 
 #define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
 
-extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
-extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
+#ifdef CONFIG_CDU31A
+extern unsigned long cdu31a_init(unsigned long, unsigned long);
+#endif CONFIG_CDU31A
+#ifdef CONFIG_MCD
+extern unsigned long mcd_init(unsigned long, unsigned long);
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+extern unsigned long mcdx_init(unsigned long, unsigned long);
+#endif CONFIG_MCDX
+#ifdef CONFIG_SBPCD
+extern unsigned long sbpcd_init(unsigned long, unsigned long);
+#endif CONFIG_SBPCD
 #ifdef CONFIG_AZTCD
-extern unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end);
-#endif
+extern unsigned long aztcd_init(unsigned long, unsigned long);
+#endif CONFIG_AZTCD
 #ifdef CONFIG_CDU535
-extern unsigned long sony535_init(unsigned long mem_start, unsigned long mem_end);
-#endif
+extern unsigned long sony535_init(unsigned long, unsigned long);
+#endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+extern unsigned long gscd_init(unsigned long, unsigned long);
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+extern unsigned long cm206_init(unsigned long, unsigned long);
+#endif CONFIG_CM206
+#ifdef CONFIG_OPTCD
+extern unsigned long optcd_init(unsigned long, unsigned long);
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+extern unsigned long sjcd_init(unsigned long, unsigned long);
+#endif CONFIG_SJCD
 #ifdef CONFIG_BLK_DEV_HD
 extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
 #endif
 #ifdef CONFIG_BLK_DEV_IDE
 extern unsigned long ide_init(unsigned long mem_start, unsigned long mem_end);
 #endif
-#ifdef CONFIG_SBPCD
-extern unsigned long sbpcd_init(unsigned long, unsigned long);
-#endif CONFIG_SBPCD
 extern void set_device_ro(int dev,int flag);
 
 extern int floppy_init(void);
@@ -165,19 +184,11 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
-#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
-
-#define DEVICE_NAME "Aztech CD-ROM"
-#define DEVICE_REQUEST do_aztcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
+#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
 
-#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
-
-#define DEVICE_NAME "SONY-CDU535"
-#define DEVICE_INTR do_cdu535
-#define DEVICE_REQUEST do_cdu535_request
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcdx */
+#define DEVICE_REQUEST do_mcdx_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
@@ -210,6 +221,54 @@
 
 #define DEVICE_NAME "Matsushita CD-ROM controller #4"
 #define DEVICE_REQUEST do_sbpcd4_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
+
+#define DEVICE_NAME "Aztech CD-ROM"
+#define DEVICE_REQUEST do_aztcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
+
+#define DEVICE_NAME "Goldstar R420"
+#define DEVICE_REQUEST do_gscd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CM206_CDROM_MAJOR)
+#define DEVICE_NAME "Philips/LMS cd-rom cm206"
+#define DEVICE_REQUEST do_cm206_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
+
+#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
+#define DEVICE_REQUEST do_optcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
+
+#define DEVICE_NAME "Sanyo H94A CD-ROM"
+#define DEVICE_REQUEST do_sjcd_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
diff -u --recursive --new-file v1.3.6/linux/drivers/block/cm206.c linux/drivers/block/cm206.c
--- v1.3.6/linux/drivers/block/cm206.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/cm206.c	Sat Jul  1 19:05:59 1995
@@ -0,0 +1,1198 @@
+/* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card.
+   Copyright (c) 1995 David van Leeuwen.
+   
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+     
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+     
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+History:
+ Started 25 jan 1994. Waiting for documentation...
+ 22 feb 1995: 0.1a first reasonably safe polling driver.
+	      Two major bugs, one in read_sector and one in 
+	      do_cm206_request, happened to cancel!
+ 25 feb 1995: 0.2a first reasonable interrupt driven version of above.
+              uart writes are still done in polling mode. 
+ 25 feb 1995: 0.21a writes also in interrupt mode, still some
+	      small bugs to be found... Larger buffer. 
+  2 mrt 1995: 0.22 Bug found (cd-> nowhere, interrupt was called in
+              initialization), read_ahead of 16. Timeouts implemented.
+	      unclear if they do something...
+  7 mrt 1995: 0.23 Start of background read-ahead.
+ 18 mrt 1995: 0.24 Working background read-ahead. (still problems)
+ 26 mrt 1995: 0.25 Multi-session ioctl added (kernel v1.2).
+              Statistics implemented, though separate stats206.h.
+	      Accessible trough ioctl 0x1000 (just a number).
+	      Hard to choose between v1.2 development and 1.1.75.
+	      Bottom-half doesn't work with 1.2...
+	      0.25a: fixed... typo. Still problems...
+  1 apr 1995: 0.26 Module support added. Most bugs found. Use kernel 1.2.n.
+  5 apr 1995: 0.27 Auto-probe for the adapter card base address.
+              Auto-probe for the adaptor card irq line.
+  7 apr 1995: 0.28 Added lilo setup support for base address and irq.
+              Use major number 32 (not in this source), officially
+	      assigned to this driver.
+  9 apr 1995: 0.29 Added very limited audio support. Toc_header, stop, pause,
+              resume, eject. Play_track ignores track info, because we can't 
+	      read a table-of-contents entry. Toc_entry is implemented
+	      as a `placebo' function: always returns start of disc. 
+  3 may 1995: 0.30 Audio support completed. The get_toc_entry function
+              is implemented as a binary search. 
+ 15 may 1995: 0.31 More work on audio stuff. Workman is not easy to 
+              satisfy; changed binary search into linear search.
+	      Auto-probe for base address somewhat relaxed.
+  1 jun 1995: 0.32 Removed probe_irq_on/off for module version.
+ 10 jun 1995: 0.33 Workman still behaves funny, but you should be
+              able to eject and substitute another disc.
+ * 
+ * Parts of the code are based upon lmscd.c written by Kai Petzke,
+ * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin
+ * Harriss, but any off-the-shelf dynamic programming algorithm won't
+ * be able to find them.
+ *
+ * The cm206 drive interface and the cm260 adapter card seem to be 
+ * sufficiently different from their cm205/cm250 counterparts
+ * in order to write a complete new driver.
+ * 
+ * I call all routines connected to the Linux kernel something
+ * with `cm206' in it, as this stuff is too series-dependent. 
+ * 
+ * Currently, my limited knowledge is based on:
+ * - The Linux Kernel Hacker's guide, v. 0.5 , by Michael J. Johnson
+ * - Linux Kernel Programmierung, by Michael Beck and others
+ * - Philips/LMS cm206 and cm226 product specification
+ * - Philips/LMS cm260 product specification
+ *
+ *                       David van Leeuwen, david@tm.tno.nl.  */
+#define VERSION "0.33"
+
+#ifdef MODULE			/* OK, so some of this is stolen */
+#include <linux/module.h>	
+#include <linux/version.h>
+#include <linux/malloc.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]=UTS_RELEASE;
+#endif
+#else 
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif MODULE
+
+#include <linux/errno.h>	/* These include what we really need */
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+
+#define MAJOR_NR CM206_CDROM_MAJOR
+#include "blk.h"
+#include <linux/cm206.h>
+
+/* This variable defines whether or not to probe for adapter base port 
+   address and interrupt request. It can be overridden by the boot 
+   parameter `auto'.
+*/
+static int auto_probe=1;	/* Yes, why not? */
+
+#define cm206_base cm206 /* for compatible "insmod" parameter passing */
+static int cm206_base = CM206_BASE;
+static int cm206_irq = CM206_IRQ; /* must directly follow cm206_base */
+
+#undef DEBUG
+#undef DEBUG_SECTORS
+#define STATISTICS
+#undef AUTO_PROBE_MODULE
+
+#define POLLOOP 10000
+#define READ_AHEAD 1		/* defines private buffer, waste! */
+#define BACK_AHEAD 1		/* defines adapter-read ahead */
+#define DATA_TIMEOUT 300	/* measured in jiffies (10 ms) */
+#define UART_TIMEOUT 5
+#define DSB_TIMEOUT 700		/* time for the slowest command to finish */
+
+#define RAW_SECTOR_SIZE 2352	/* ok, is also defined in cdrom.h */
+#define ISO_SECTOR_SIZE 2048
+
+#ifdef STATISTICS		/* keep track of errors in counters */
+#include <linux/stats206.h>
+#define stats(i) ++cd->stats[st_ ## i]; \
+                 cd->last_stat[st_ ## i] = cd->stat_counter++;
+#else
+#define stats(i) (void) 0
+#endif
+
+#ifdef DEBUG			/* from lmscd.c */
+#define debug(a) printk a
+#else
+#define debug(a) (void) 0
+#endif
+
+typedef unsigned char uch;	/* 8-bits */
+typedef unsigned short ush;	/* 16-bits */
+
+struct toc_struct{
+  uch track, fsm[3], q0;
+};
+
+struct cm206_struct {
+  ush intr_ds;	 /* data status read on last interrupt */
+  ush intr_ls;	 /* uart line status read on last interrupt*/
+  uch intr_ur;			/* uart receive buffer */
+  uch dsb, cc;	 /* drive status byte and condition (error) code */
+  uch fool;
+  int command;			/* command to be written to te uart */
+  int openfiles;
+  ush sector[READ_AHEAD*RAW_SECTOR_SIZE/2]; /* buffered cd-sector */
+  int sector_first, sector_last;	/* range of these sector */
+  struct wait_queue * uart;	/* wait for interrupt */
+  struct wait_queue * data;
+  struct timer_list timer;	/* time-out */
+  char timed_out;
+  signed char max_sectors;
+  char wait_back;		/* we're waiting for a background-read */
+  char background;		/* is a read going on in the background? */
+  int adapter_first;		/* if so, that's the starting sector */
+  int adapter_last;
+  char fifo_overflowed;
+  uch disc_status[7];		/* result of get_disc_status command */
+#ifdef STATISTICS
+  int stats[NR_STATS];
+  int last_stat[NR_STATS];	/* `time' at which stat was stat */
+  int stat_counter;
+#endif  
+  struct toc_struct toc[101];	/* The whole table of contents + lead-out */
+  uch q[10];			/* Last read q-channel info */
+  uch audio_status[5];		/* last read position on pause */
+};
+
+#define DISC_STATUS cd->disc_status[0]
+#define FIRST_TRACK cd->disc_status[1]
+#define LAST_TRACK cd->disc_status[2]
+#define PAUSED cd->audio_status[0] /* misuse this memory byte! */
+#define PLAY_TO cd->toc[0]	/* toc[0] records end-time in play */
+
+static struct cm206_struct * cd;
+
+/* First, we define some polling functions. These are actually
+   only being used in the initialization. */
+
+void send_command_polled(int command)
+{
+  int loop=POLLOOP;
+  while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) 
+    --loop;
+  outw(command, r_uart_transmit);
+}
+
+uch receive_echo_polled(void)
+{
+  int loop=POLLOOP;
+  while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) --loop;
+  return ((uch) inw(r_uart_receive));
+}
+
+uch send_receive_polled(int command)
+{
+  send_command_polled(command);
+  return receive_echo_polled();
+}
+
+/* The interrupt handler. When the cm260 generates an interrupt, very
+   much care has to be taken in reading out the registers in the right
+   order; in case of a receive_buffer_full interrupt, first the
+   uart_receive must be read, and then the line status again to
+   de-assert the interrupt line. It took me a couple of hours to find
+   this out:-( 
+
+   The function reset_cm206 appears to cause an interrupt, because
+   pulling up the INIT line clears both the uart-write-buffer /and/
+   the uart-write-buffer-empty mask. We call this a `lost interrupt,'
+   as there seems so reason for this to happen.
+*/
+
+static void cm206_interrupt(int sig, struct pt_regs * regs) /* you rang? */
+{
+  volatile ush fool;
+    cd->intr_ds = inw(r_data_status); /* resets data_ready, data_error,
+					 crc_error, sync_error, toc_ready 
+					 interrupts */
+    cd->intr_ls = inw(r_line_status); /* resets overrun bit */
+    /* receive buffer full? */
+    if (cd->intr_ls & ls_receive_buffer_full) {	
+      cd->intr_ur = inb(r_uart_receive); /* get order right! */
+      cd->intr_ls = inw(r_line_status); /* resets rbf interrupt */
+      if (!cd->background && cd->uart) wake_up_interruptible(&cd->uart);
+    }
+    /* data ready in fifo? */
+    else if (cd->intr_ds & ds_data_ready) { 
+      if (cd->background) ++cd->adapter_last;
+      if ((cd->wait_back || !cd->background) && cd->data) 
+	  wake_up_interruptible(&cd->data);
+      stats(data_ready);
+    }
+    /* ready to issue a write command? */
+    else if (cd->command && cd->intr_ls & ls_transmitter_buffer_empty) {
+      outw(dc_normal | (inw(r_data_status) & 0x7f), r_data_control);
+      outw(cd->command, r_uart_transmit);
+      cd->command=0;
+      if (!cd->background) wake_up_interruptible(&cd->uart);
+    }
+    /* now treat errors (at least, identify them for debugging) */
+    else if (cd->intr_ds & ds_fifo_overflow) {
+      debug(("Fifo overflow at sectors 0x%x\n", cd->sector_first));
+      fool = inw(r_fifo_output_buffer);	/* de-assert the interrupt */
+      cd->fifo_overflowed=1;	/* signal one word less should be read */
+      stats(fifo_overflow);
+    }
+    else if (cd->intr_ds & ds_data_error) {
+      debug(("Data error at sector 0x%x\n", cd->sector_first));
+      stats(data_error);
+    }
+    else if (cd->intr_ds & ds_crc_error) {
+      debug(("CRC error at sector 0x%x\n", cd->sector_first));
+      stats(crc_error);
+    }
+    else if (cd->intr_ds & ds_sync_error) {
+      debug(("Sync at sector 0x%x\n", cd->sector_first));
+      stats(sync_error);
+    }
+    else if (cd->intr_ds & ds_toc_ready) {
+				/* do something appropiate */
+    }
+    /* couldn't see why this interrupt, maybe due to init */
+    else {			
+      outw(dc_normal | READ_AHEAD, r_data_control);
+      stats(lost_intr);
+    }
+  if (cd->background && (cd->adapter_last-cd->adapter_first == cd->max_sectors
+      || cd->fifo_overflowed))
+    mark_bh(CM206_BH);	/* issue a stop read command */
+  stats(interrupt);
+}
+
+/* we have put the address of the wait queue in who */
+void cm206_timeout(unsigned long who)
+{
+  cd->timed_out = 1;
+  wake_up_interruptible((struct wait_queue **) who);
+}
+
+/* This function returns 1 if a timeout occurred, 0 if an interrupt
+   happened */
+int sleep_or_timeout(struct wait_queue ** wait, int timeout)
+{
+  cd->timer.data=(unsigned long) wait;
+  cd->timer.expires = timeout;
+  add_timer(&cd->timer);
+  interruptible_sleep_on(wait);
+  del_timer(&cd->timer);
+  if (cd->timed_out) {
+    cd->timed_out = 0;
+    return 1;
+  }
+  else return 0;
+}
+
+void cm206_delay(int jiffies) 
+{
+  struct wait_queue * wait = NULL;
+  sleep_or_timeout(&wait, jiffies);
+}
+
+void send_command(int command)
+{
+  if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) {
+    cd->command = command;
+    cli();			/* don't interrupt before sleep */
+    outw(dc_mask_sync_error | dc_no_stop_on_error | 
+	 (inw(r_data_status) & 0x7f), r_data_control);
+    /* interrupt routine sends command */
+    if (sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
+      debug(("Time out on write-buffer\n"));
+      stats(write_timeout);
+      outw(command, r_uart_transmit);
+    }
+  }
+  else outw(command, r_uart_transmit);
+}
+
+uch receive_echo(void)
+{
+  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
+      sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
+    debug(("Time out on receive-buffer\n"));
+    stats(receive_timeout);
+    return ((uch) inw(r_uart_receive));
+  }
+  return cd->intr_ur;
+}
+
+inline uch send_receive(int command)
+{
+  send_command(command);
+  return receive_echo();
+}
+
+uch wait_dsb(void)
+{
+  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
+      sleep_or_timeout(&cd->uart, DSB_TIMEOUT)) {
+    debug(("Time out on Drive Status Byte\n"));
+    stats(dsb_timeout);
+    return ((uch) inw(r_uart_receive));
+  }
+  return cd->intr_ur;
+}
+
+int type_0_command(int command, int expect_dsb)
+{
+  int e;
+  if (command != (e=send_receive(command))) {
+    debug(("command 0x%x echoed as 0x%x\n", command, e));
+    stats(echo);
+    return -1;
+  }
+  if (expect_dsb) {
+    cd->dsb = wait_dsb();	/* wait for command to finish */
+  }
+  return 0;
+}
+
+int type_1_command(int command, int bytes, uch * status) /* returns info */
+{
+  int i;
+  if (type_0_command(command,0)) return -1;
+  for(i=0; i<bytes; i++) 
+    status[i] = send_receive(c_gimme);
+  return 0;
+}  
+
+/* This function resets the adapter card. We'd better not do this too */
+/* often, because it tends to generate `lost interrupts.' */
+void reset_cm260(void)
+{
+  outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control);
+  udelay(10);			/* 3.3 mu sec minimum */
+  outw(dc_normal | READ_AHEAD, r_data_control);
+}
+
+/* fsm: frame-sec-min from linear address */
+void fsm(int lba, uch * fsm) 
+{
+  fsm[0] = lba % 75;
+  lba /= 75; lba += 2;
+  fsm[1] = lba % 60; fsm[2] = lba / 60;
+}
+
+inline int fsm2lba(uch * fsm) 
+{
+  return fsm[0] + 75*(fsm[1]-2 + 60*fsm[2]);
+}
+
+inline int f_s_m2lba(uch f, uch s, uch m)
+{
+  return f + 75*(s-2 + 60*m);
+}
+
+int start_read(int start) 
+{
+  uch read_sector[4] = {c_read_data, };
+  int i, e;
+
+  fsm(start, &read_sector[1]);
+  for (i=0; i<4; i++) 
+    if (read_sector[i] != (e=send_receive(read_sector[i]))) {
+      debug(("read_sector: %x echoes %x\n", read_sector[i], e));
+      stats(echo);
+      return -1;
+    }
+  return 0;
+}
+
+int stop_read(void)
+{
+  type_0_command(c_stop,0);
+  if(receive_echo() != 0xff) {
+    debug(("c_stop didn't send 0xff\n"));
+    stats(stop_0xff);
+    return -1;
+  }
+  return 0;
+}  
+
+/* This function starts to read sectors in adapter memory, the
+   interrupt routine should stop the read. In fact, the bottom_half
+   routine takes care of this. Set a flag `background' in the cd
+   struct to indicate the process. */
+
+int read_background(int start, int reading)
+{
+  if (cd->background) return -1; /* can't do twice */
+  outw(dc_normal | BACK_AHEAD, r_data_control);
+  if (!reading && start_read(start)) return -2;
+  cd->adapter_first = cd->adapter_last = start; 
+  cd->background = 1;		/* flag a read is going on */
+  return 0;
+}
+
+int read_sector(int start)
+{
+  if (cd->background) {
+    cd->background=0;
+    cd->adapter_last = -1;	/* invalidate adapter memory */
+    stop_read();
+  }
+  cd->fifo_overflowed=0;
+  reset_cm260();		/* empty fifo etc. */
+  if (start_read(start)) return -1;
+  if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
+    debug(("Read timed out sector 0x%x\n", start));
+    stats(read_timeout);
+    stop_read();
+    return -3;		
+  }
+  insw(r_fifo_output_buffer, cd->sector, READ_AHEAD*RAW_SECTOR_SIZE/2);
+  if (read_background(start+READ_AHEAD,1)) stats(read_background);
+  cd->sector_first = start; cd->sector_last = start+READ_AHEAD;
+  stats(read_restarted);
+  return 0;
+}
+
+/* The function of bottom-half is to send a stop command to the drive
+   This isn't easy because the routine is not `owned' by any process;
+   we can't go to sleep! The variable cd->background gives the status:
+   0 no read pending
+   1 a read is pending
+   2 c_stop waits for write_buffer_empty
+   3 c_stop waits for receive_buffer_full: echo
+   4 c_stop waits for receive_buffer_full: 0xff
+*/
+
+void cm206_bh(void * unused)
+{
+  debug(("bh: %d\n", cd->background));
+  switch (cd->background) {
+  case 1:
+    stats(bh);
+    if (!(cd->intr_ls & ls_transmitter_buffer_empty)) {
+      cd->command = c_stop;
+      outw(dc_mask_sync_error | dc_no_stop_on_error | 
+	   (inw(r_data_status) & 0x7f), r_data_control);
+      cd->background=2;
+      break;			/* we'd better not time-out here! */
+    }
+    else outw(c_stop, r_uart_transmit);
+    /* fall into case 2: */
+  case 2:			
+    /* the write has been satisfied by interrupt routine */
+    cd->background=3;
+    break;
+  case 3:
+    if (cd->intr_ur != c_stop) {
+      debug(("cm206_bh: c_stop echoed 0x%x\n", cd->intr_ur));
+      stats(echo);
+    }
+    cd->background++;
+    break;
+  case 4:
+    if (cd->intr_ur != 0xff) {
+      debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->intr_ur));
+      stats(stop_0xff);
+    }
+    cd->background=0;
+  }
+}
+
+void get_drive_status(void)
+{
+  uch status[2];
+  type_1_command(c_drive_status, 2, status); /* this might be done faster */
+  cd->dsb=status[0];
+  cd->cc=status[1];
+}
+
+void get_disc_status(void)
+{
+  if (type_1_command(c_disc_status, 7, cd->disc_status)) {
+    debug(("get_disc_status: error\n"));
+  }
+}
+
+static int cm206_open(struct inode *ip, struct file *fp)
+{
+  if (!cd->openfiles) {
+    cd->background=0;
+    reset_cm260();
+    cd->adapter_last = -1;	/* invalidate adapter memory */
+    cd->sector_last = -1;
+    get_drive_status();
+    if (cd->dsb & dsb_tray_not_closed) {
+      int i=0;
+      type_0_command(c_close_tray, 1);
+      while (i++<10 && cd->dsb & dsb_drive_not_ready) {
+	cm206_delay(100);
+	get_drive_status();
+      }
+    }
+    if (cd->dsb & (dsb_not_useful)) return -EIO;
+    if (!(cd->dsb & dsb_disc_present)) return -ENODATA;
+    if (cd->dsb & dsb_possible_media_change) {
+      memset(cd->toc, 0, sizeof(cd->toc));
+      memset(cd->audio_status, 0, sizeof(cd->audio_status));
+    }
+    get_disc_status();
+    type_0_command(c_lock_tray,1);
+    if (!(cd->dsb & dsb_tray_locked)) {
+      debug(("Couldn't lock tray\n"));
+    }
+#if 0
+    if (!(DISC_STATUS & cds_all_audio))
+      read_background(16,0);	/* do something useful */
+#endif
+  }
+  ++cd->openfiles; MOD_INC_USE_COUNT;
+  stats(open);
+  return 0;
+}
+
+static void cm206_release(struct inode *ip, struct file *fp)
+{
+  if (cd->openfiles==1) {
+    if (cd->background) {
+      cd->background=0;
+      stop_read();
+    }
+    type_0_command(c_unlock_tray,1);
+    cd->sector_last = -1;	/* Make our internal buffer invalid */
+    FIRST_TRACK = 0;	/* No valid disc status */
+    sync_dev(ip -> i_rdev);	/* These two lines are stolen */
+    invalidate_buffers(ip -> i_rdev);
+  }
+  --cd->openfiles; MOD_DEC_USE_COUNT;
+}
+
+/* Empty buffer empties $sectors$ sectors of the adapter card buffer,
+ * and then reads a sector in kernel memory.  */
+void empty_buffer(int sectors) 
+{
+  while (sectors>=0) {
+    insw(r_fifo_output_buffer, cd->sector + cd->fifo_overflowed, 
+	 RAW_SECTOR_SIZE/2 - cd->fifo_overflowed);
+    --sectors;
+    ++cd->adapter_first;	/* update the current adapter sector */
+    cd->fifo_overflowed=0;	/* reset overflow bit */
+    stats(sector_transferred);
+  } 
+  cd->sector_first=cd->adapter_first-1;
+  cd->sector_last=cd->adapter_first; /* update the buffer sector */
+}
+
+/* try_adapter. This function determines of the requested sector is is
+   in adapter memory, or will appear there soon. Returns 0 upon
+   success */
+int try_adapter(int sector)
+{
+  if (cd->adapter_first <= sector && sector < cd->adapter_last) { 
+    /* sector is in adapter memory */
+    empty_buffer(sector - cd->adapter_first);
+    return 0;
+  }
+  else if (cd->background==1 && cd->adapter_first <= sector
+	   && sector < cd->adapter_first+cd->max_sectors) {
+    /* a read is going on, we can wait for it */
+    cd->wait_back=1;
+    while (sector >= cd->adapter_last) {
+      if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
+	debug(("Timed out during background wait: %d %d %d %d\n", sector, 
+	       cd->adapter_last, cd->adapter_first, cd->background));
+	stats(back_read_timeout);
+	cd->wait_back=0;
+	return -1;
+      }
+    }
+    cd->wait_back=0;
+    empty_buffer(sector - cd->adapter_first);
+    return 0;
+  }
+  else return -2;
+}
+
+/* This is not a very smart implementation. We could optimize for 
+   consecutive block numbers. I'm not conviced this would really
+   bring down the processor load. */
+static void do_cm206_request(void)
+{
+  long int i, cd_sec_no;
+  int quarter, error; 
+  uch * source, * dest;
+  
+  while(1) {	 /* repeat until all requests have been satisfied */
+    INIT_REQUEST;
+    if (CURRENT == NULL || CURRENT->dev == -1) return;
+    if (CURRENT->cmd != READ) {
+      debug(("Non-read command %d on cdrom\n", CURRENT->cmd));
+      end_request(0);
+      continue;
+    }
+    error=0;
+    for (i=0; i<CURRENT->nr_sectors; i++) {
+      cd_sec_no = (CURRENT->sector+i)/4; /* 4 times 512 bytes */
+      quarter = (CURRENT->sector+i) % 4; 
+      dest = CURRENT->buffer + i*512;
+      /* is already in buffer memory? */
+      if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) {
+	source = ((uch *) cd->sector) + 16 + 
+	  quarter*512 + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE;
+ 	memcpy(dest, source, 512); 
+      }
+      else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) {
+	source =  ((uch *) cd->sector)+16+quarter*512;
+	memcpy(dest, source, 512); 
+      }
+      else {
+	error=1;
+      }
+    }
+    end_request(!error);
+  }
+}
+
+int get_multi_session_info(struct cdrom_multisession * mssp)
+{
+  if (!FIRST_TRACK) get_disc_status();
+  if (mssp && DISC_STATUS & cds_multi_session) { /* multi-session */
+    mssp->addr.lba = fsm2lba(&cd->disc_status[3]);
+    mssp->xa_flag = 1;		/* don't know */
+    mssp->addr_format = CDROM_LBA; /* too bad if fsm requested! */
+    return 1;
+  }
+  return 0;
+}
+
+/* Audio support. I've tried very hard, but the cm206 drive doesn't 
+   seem to have a get_toc (table-of-contents) function, while i'm
+   pretty sure it must read the toc upon disc insertion. Therefore
+   this function has been implemented through a binary search 
+   strategy. All track starts that happen to be found are stored in
+   cd->toc[], for future use. 
+
+   I've spent a whole day on a bug that only shows under Workman---
+   I don't get it. Tried everything, nothing works. If workman asks
+   for track# 0xaa, it'll get the wrong time back. Any other program
+   receives the correct value. I'm stymied.
+*/
+
+/* seek seeks to address lba. It does wait to arrive there. */
+void seek(int lba)
+{
+  int i;
+  uch seek_command[4]={c_seek, };
+  
+  fsm(lba, &seek_command[1]);
+  for (i=0; i<4; i++) type_0_command(seek_command[i], 0);
+  cd->dsb = wait_dsb();
+}
+
+uch bcdbin(unsigned char bcd)	/* stolen from mcd.c! */
+{
+  return (bcd >> 4)*10 + (bcd & 0xf);
+} 
+
+inline uch normalize_track(uch track) 
+{
+  if (track<1) return 1;
+  if (track>LAST_TRACK) return LAST_TRACK+1;
+  return track;
+}
+
+/* This function does a binary search for track start. It records all
+ * tracks seen in the process. Input $track$ must be between 1 and
+ * #-of-tracks+1 */
+int get_toc_lba(uch track)
+{
+  int max=74*60*75-150, min=0;
+  int i, lba, l, old_lba=0;
+  uch * q = cd->q;
+  uch ct;			/* current track */
+  int binary=0;
+  const skip = 3*60*75;
+
+  for (i=track; i>0; i--) if (cd->toc[i].track) {
+    min = fsm2lba(cd->toc[i].fsm);
+    break;
+  }
+  lba = min + skip;		/* 3 minutes */
+  do {
+    seek(lba); 
+    type_1_command(c_read_current_q, 10, q);
+    ct = normalize_track(q[1]);
+    if (!cd->toc[ct].track) {
+      l = q[9]-bcdbin(q[5]) + 75*(q[8]-bcdbin(q[4])-2 + 
+				  60*(q[7]-bcdbin(q[3])));
+      cd->toc[ct].track=q[1];	/* lead out still 0xaa */
+      fsm(l, cd->toc[ct].fsm);
+      cd->toc[ct].q0 = q[0];	/* contains adr and ctrl info */
+/*
+      if (ct==LAST_TRACK+1) 
+	printk("Leadout %x %x %x %x %d %d %d \n", q[1], q[3], q[4], q[5],
+	       q[7], q[8], q[9]);
+*/
+      if (ct==track) return l;
+    }
+    old_lba=lba;
+    if (binary) {
+      if (ct < track) min = lba; else max = lba;
+      lba = (min+max)/2; 
+    } else {
+      if(ct < track) lba += skip;
+      else {
+	binary=1;
+	max = lba; min = lba - skip;
+	lba = (min+max)/2;
+      }
+    }
+  } while (lba!=old_lba);
+  return lba;
+}
+
+void update_toc_entry(uch track) 
+{
+  track = normalize_track(track);
+  if (!cd->toc[track].track) get_toc_lba(track);
+}
+
+int read_toc_header(struct cdrom_tochdr * hp)
+{
+  if (!FIRST_TRACK) get_disc_status();
+  if (hp && DISC_STATUS & cds_all_audio) { /* all audio */
+    int i;
+    hp->cdth_trk0 = FIRST_TRACK;
+    hp->cdth_trk1 = LAST_TRACK;
+    cd->toc[1].track=1;		/* fill in first track position */
+    for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i];
+    update_toc_entry(LAST_TRACK+1);		/* find most entries */
+    return 1;
+  }
+  return 0;
+}  
+
+void play_from_to_msf(struct cdrom_msf* msfp)
+{
+  uch play_command[] = {c_play, 
+	   msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0,
+	   msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, 2};
+  int i;
+  for (i=0; i<9; i++) type_0_command(play_command[i], 0);
+  for (i=0; i<3; i++) 
+    PLAY_TO.fsm[i] = play_command[i+4];
+  PLAY_TO.track = 0;		/* say no track end */
+  cd->dsb = wait_dsb();
+}  
+
+void play_from_to_track(int from, int to)
+{
+  uch play_command[8] = {c_play, };
+  int i;
+
+  if (from==0) {		/* continue paused play */
+    for (i=0; i<3; i++) { 
+      play_command[i+1] = cd->audio_status[i+2];
+      play_command[i+4] = PLAY_TO.fsm[i];
+    }
+  } else {
+    update_toc_entry(from); update_toc_entry(to+1);
+    for (i=0; i<3; i++) {
+      play_command[i+1] = cd->toc[from].fsm[i];
+      PLAY_TO.fsm[i] = play_command[i+4] = cd->toc[to+1].fsm[i];
+    }
+    PLAY_TO.track = to; 
+  }
+  for (i=0; i<7; i++) type_0_command(play_command[i],0);
+  for (i=0; i<2; i++) type_0_command(0x2, 0); /* volume */
+  cd->dsb = wait_dsb();
+}
+
+int get_current_q(struct cdrom_subchnl * qp)
+{
+  int i;
+  uch * q = cd->q;
+  if (type_1_command(c_read_current_q, 10, q)) return 0;
+/*  q[0] = bcdbin(q[0]); Don't think so! */
+  for (i=2; i<6; i++) q[i]=bcdbin(q[i]); 
+  qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4;	/* from mcd.c */
+  qp->cdsc_trk = q[1];  qp->cdsc_ind = q[2];
+  if (qp->cdsc_format == CDROM_MSF) {
+    qp->cdsc_reladdr.msf.minute = q[3];
+    qp->cdsc_reladdr.msf.second = q[4];
+    qp->cdsc_reladdr.msf.frame = q[5];
+    qp->cdsc_absaddr.msf.minute = q[7];
+    qp->cdsc_absaddr.msf.second = q[8];
+    qp->cdsc_absaddr.msf.frame = q[9];
+  } else {
+    qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]);
+    qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]);
+  }
+  get_drive_status();
+  if (cd->dsb & dsb_play_in_progress) 
+    qp->cdsc_audiostatus = CDROM_AUDIO_PLAY ;
+  else if (PAUSED) 
+    qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED;
+  else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
+  return 1;
+}
+
+void get_toc_entry(struct cdrom_tocentry * ep)
+{
+  uch track = normalize_track(ep->cdte_track);
+  update_toc_entry(track);
+  if (ep->cdte_format == CDROM_MSF) {
+    ep->cdte_addr.msf.frame = cd->toc[track].fsm[0];
+    ep->cdte_addr.msf.second = cd->toc[track].fsm[1];
+    ep->cdte_addr.msf.minute = cd->toc[track].fsm[2];
+  } 
+  else ep->cdte_addr.lba = fsm2lba(cd->toc[track].fsm);
+  ep->cdte_adr = cd->toc[track].q0 & 0xf; 
+  ep->cdte_ctrl = cd->toc[track].q0 >> 4;
+  ep->cdte_datamode=0;
+}
+  
+/* Ioctl. I have made the statistics accessible through an ioctl
+   call. The constant is defined in cm206.h, it shouldn't clash with
+   the standard Linux ioctls. Multisession info is gathered at
+   run-time, this may turn out to be slow. */
+
+static int cm206_ioctl(struct inode * inode, struct file * file, 
+		       unsigned int cmd, unsigned long arg)
+{
+  switch (cmd) {
+#ifdef STATISTICS
+  case CM206CTL_GET_STAT:
+    if (arg >= NR_STATS) return -EINVAL;
+    else return cd->stats[arg];
+  case CM206CTL_GET_LAST_STAT:
+    if (arg >= NR_STATS) return -EINVAL;
+    else return cd->last_stat[arg];
+#endif    
+  case CDROMMULTISESSION: {
+    struct cdrom_multisession ms_info;
+    stats(ioctl_multisession);
+    if (get_multi_session_info(&ms_info)) {
+      memcpy_tofs((struct cdrom_multisession *) arg, &ms_info, 
+		  sizeof(struct cdrom_multisession));
+      return 0;
+    }
+    else return -cmd;
+  }
+  case CM206_RESET_DRIVE:
+    stop_read();
+    reset_cm260();
+    outw(dc_normal | dc_break | READ_AHEAD, r_data_control);
+    udelay(1000);		/* 750 musec minimum */
+    outw(dc_normal | READ_AHEAD, r_data_control);
+    cd->sector_last = -1;		/* flag no data buffered */
+    cd->adapter_last = -1;    
+    return 0;
+  }
+  get_drive_status();
+  if (cd->dsb & (dsb_drive_not_ready | dsb_tray_not_closed) )
+    return -EAGAIN; 
+  switch (cmd) {
+  case CDROMREADTOCHDR: {
+    struct cdrom_tochdr header;
+    get_drive_status();
+    if (cd->dsb & (dsb_drive_not_ready | dsb_tray_not_closed) )
+      return -EAGAIN; 
+    if (read_toc_header(&header)) {
+      memcpy_tofs((struct cdrom_tochdr *) arg, &header, sizeof(header));
+      return 0;
+    }
+    else return -ENODATA;
+  }
+  case CDROMREADTOCENTRY: {	
+    struct cdrom_tocentry entry;
+    memcpy_fromfs(&entry, (struct cdrom_tocentry *) arg, sizeof entry);
+    get_toc_entry(&entry);
+    memcpy_tofs((struct cdrom_tocentry *) arg, &entry, sizeof entry);
+    return 0;
+  }
+  case CDROMPLAYMSF: {
+    struct cdrom_msf msf;
+    memcpy_fromfs(&msf, (struct cdrom_mdf *) arg, sizeof msf);
+    play_from_to_msf(&msf);
+    return 0;
+  }
+  case CDROMPLAYTRKIND: {
+    struct cdrom_ti track_index;
+    memcpy_fromfs(&track_index, (struct cdrom_ti *) arg, sizeof(track_index));
+    play_from_to_track(track_index.cdti_trk0, track_index.cdti_trk1);
+    return 0;
+  }
+  case CDROMSTOP: 
+    PAUSED=0;
+    if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1);
+    return 0;
+  case CDROMPAUSE: 
+    if (cd->dsb & dsb_play_in_progress) {
+      type_0_command(c_stop, 1);
+      type_1_command(c_audio_status, 5, cd->audio_status);
+      PAUSED=1;	/* say we're paused */
+    }
+    return 0;
+  case CDROMRESUME:
+    if (PAUSED) play_from_to_track(0,0);
+    PAUSED=0;
+    return 0;
+  case CDROMEJECT:
+    PAUSED=0;
+    if (cd->openfiles == 1) {	/* Must do an open before an eject! */
+      type_0_command(c_open_tray,1);
+      memset(cd->toc, 0, sizeof(cd->toc));
+      memset(cd->disc_status, 0, sizeof(cd->disc_status));
+      return 0;
+    }
+    else return -EBUSY;
+  case CDROMSTART:
+  case CDROMVOLCTRL:
+    return 0;
+  case CDROMSUBCHNL: {
+    struct cdrom_subchnl q;
+    memcpy_fromfs(&q, (struct cdrom_subchnl *) arg, sizeof q);
+    if (get_current_q(&q)) {
+      memcpy_tofs((struct cdrom_subchnl *) arg, &q, sizeof q);
+      return 0;
+    }
+    else return -cmd;
+  }
+  case CDROM_GET_UPC: {
+    uch upc[10];
+    if (type_1_command(c_read_upc, 10, upc)) return -EIO;
+    memcpy_tofs((uch *) arg, &upc[1], 8);
+    return 0;
+  } 
+  default:
+    debug(("Unknown ioctl call 0x%x\n", cmd));
+    return -EINVAL;
+  }
+}     
+
+/* from lmscd.c */
+static struct file_operations cm206_fops = {
+	NULL,			/* lseek */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir */
+	NULL,			/* select */
+	cm206_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	cm206_open,		/* open */
+	cm206_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* media_change */
+	NULL			/* revalidate */
+};
+
+/* This routine gets called during init if thing go wrong, can be used
+ * in cleanup_module as well. */
+void cleanup(int level)
+{
+  switch (level) {
+  case 4: 
+    if (unregister_blkdev(MAJOR_NR, "cm206")) {
+      printk("Can't unregister cm206\n");
+      return;
+    }
+  case 3: 
+    free_irq(cm206_irq);
+  case 2: 
+  case 1: 
+#ifdef MODULE
+    kfree(cd);
+#endif
+    release_region(cm206_base, 16);
+  default:
+  }
+}
+
+/* This function probes for the adapter card. It returns the base
+   address if it has found the adapter card. One can specify a base 
+   port to probe specifically, or 0 which means span all possible
+   bases. 
+
+   Linus says it is too dangerous to use writes for probing, so we
+   stick with pure reads for a while. Hope that 8 possible ranges,
+   check_region, 15 bits of one port and 6 of another make things
+   likely enough to accept the region on the first hit...
+ */
+int probe_base_port(int base)
+{
+  int b=0x300, e=0x370;		/* this is the range of start addresses */
+  volatile int fool;
+#if 0
+  const pattern1=0x65, pattern2=0x1a;
+#endif
+
+  if (base) b=e=base;
+  for (base=b; base<=e; base += 0x10) {
+    if (check_region(base, 0x10)) continue;
+    fool = inw(base+2);		/* empty possibly uart_receive_buffer */
+    if((inw(base+6) & 0xffef) != 0x0001 || /* line_status */
+       (inw(base) & 0xad00) != 0) /* data status */
+      continue;
+#if 0				/* writes... dangerous... */
+    outw(dc_normal | pattern1, base+8); 
+    if ((inw(base) & 0x7f) != pattern1) continue;
+    outw(dc_normal | pattern2, base+8);
+    if ((inw(base) & 0x7f) != pattern2) continue;
+    outw(dc_normal | READ_AHEAD, base+8);
+#endif
+    return(base);
+  }
+  return 0;
+}
+
+#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)
+/* Probe for irq# nr. If nr==0, probe for all possible irq's. */
+int probe_irq(int nr) {
+  int irqs, irq;
+  outw(dc_normal | READ_AHEAD, r_data_control);	/* disable irq-generation */
+  sti(); 
+  irqs = probe_irq_on();
+  reset_cm260();		/* causes interrupt */
+  udelay(10);			/* wait for it */
+  irq = probe_irq_off(irqs);
+  outw(dc_normal | READ_AHEAD, r_data_control);	/* services interrupt */
+  if (nr && irq!=nr && irq>0) return 0;	/* wrong interrupt happened */
+  else return irq;
+}
+#endif
+
+/* Wow is this piece of #ifdeffing ugly! */
+#ifdef MODULE
+#define OK  0
+#define ERROR  -EIO
+int init_module(void)
+#else
+#define OK  mem_start+size
+#define ERROR  mem_start
+unsigned long cm206_init(unsigned long mem_start, unsigned long mem_end)
+#endif
+{
+  uch e=0;
+  long int size=sizeof(struct cm206_struct);
+
+  printk("cm206: v" VERSION);
+#if defined(MODULE) && !defined(AUTO_PROBE_MODULE)
+  auto_probe=0;
+#endif
+  cm206_base = probe_base_port(auto_probe ? 0 : cm206_base);
+  if (!cm206_base) {
+    printk(" can't find adapter!\n");
+    return ERROR;
+  }
+  printk(" adapter at 0x%x", cm206_base);
+  request_region(cm206_base, 16, "cm206");
+#ifdef MODULE
+  cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL);
+  if (!cd) return ERROR;
+#else 
+  cd = (struct cm206_struct *) mem_start;
+#endif
+  /* Now we have found the adaptor card, try to reset it. As we have
+   * found out earlier, this process generates an interrupt as well,
+   * so we might just exploit that fact for irq probing! */
+#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)
+  cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq);	
+  if (cm206_irq<=0) {
+    printk("can't find IRQ!\n");
+    cleanup(1);
+    return ERROR;
+  }
+  else printk(" IRQ %d found\n", cm206_irq);
+#else
+  reset_cm260();
+  printk(" using IRQ %d\n", cm206_irq);
+#endif
+  if (send_receive_polled(c_drive_configuration) != c_drive_configuration) 
+    {
+      printk(" drive not there\n");
+      cleanup(1);
+      return ERROR;
+    }
+  e = send_receive_polled(c_gimme);
+  printk("Firmware revision %d", e & dcf_revision_code);
+  if (e & dcf_transfer_rate) printk(" double");
+  else printk(" single");
+  printk(" speed drive");
+  if (e & dcf_motorized_tray) printk(", motorized tray");
+  if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206")) {
+    printk("\nUnable to reserve IRQ---aborted\n");
+    cleanup(2);
+    return ERROR;
+  }
+  printk(".\n");
+  if (register_blkdev(MAJOR_NR, "cm206", &cm206_fops) != 0) {
+    printk("Cannot register for major %d!\n", MAJOR_NR);
+    cleanup(3);
+    return ERROR;
+  }
+  blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+  read_ahead[MAJOR_NR] = 16;	/* reads ahead what? */
+  bh_base[CM206_BH].routine = cm206_bh;
+  enable_bh(CM206_BH);
+
+  memset(cd, 0, sizeof(*cd));	/* give'm some reasonable value */
+  cd->sector_last = -1;		/* flag no data buffered */
+  cd->adapter_last = -1;
+  cd->timer.function = cm206_timeout;
+  cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97;
+  printk("%d kB adapter memory available, "  
+	 " %ld bytes kernel memory used.\n", cd->max_sectors*2, size);
+  return OK;
+}
+#undef OK
+#undef ERROR
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+  cleanup(4);
+  printk("cm206 removed\n");
+}
+
+#else MODULE
+
+/* This setup function accepts either `auto' or numbers in the range
+ * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */
+void cm206_setup(char *s, int *p)
+{
+  int i;
+#ifdef AUTO_PROBE_MODULE
+  if (!strcmp(s, "auto")) auto_probe=1;
+#endif
+  for(i=1; i<=p[0]; i++) {
+    if (0x300 <= p[i] && i<= 0x370 && p[i] % 0x10 == 0) {
+      cm206_base = p[i];
+      auto_probe = 0;
+    }
+    else if (3 <= p[i] && p[i] <= 15) {
+      cm206_irq = p[i];
+      auto_probe = 0;
+    }
+  }
+}
+#endif MODULE
diff -u --recursive --new-file v1.3.6/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v1.3.6/linux/drivers/block/genhd.c	Fri May 26 08:23:33 1995
+++ linux/drivers/block/genhd.c	Wed Jul  5 12:53:22 1995
@@ -35,6 +35,8 @@
 	printk(" %s%c%d", hd->major_name, minor_name(hd, minor),
 		minor & ((1 << hd->minor_shift) - 1));
 }
+
+#ifdef CONFIG_MSDOS_PARTITION
 /*
  * Create devices for each logical partition in an extended partition.
  * The logical partitions form a linked list, with each entry being
@@ -101,15 +103,131 @@
 	brelse(bh);
 }
 
-static void check_partition(struct gendisk *hd, unsigned int dev)
+static int msdos_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
 {
-	static int first_time = 1;
 	int i, minor = current_minor;
 	struct buffer_head *bh;
 	struct partition *p;
-	unsigned long first_sector;
 	int mask = (1 << hd->minor_shift) - 1;
 
+	if (!(bh = bread(dev,0,1024))) {
+		printk("unable to read partition table\n");
+		return -1;
+	}
+	if (*(unsigned short *) (bh->b_data+510) != 0xAA55) {
+		brelse(bh);
+		return 0;
+	}
+	current_minor += 4;  /* first "extra" minor (for extended partitions) */
+	p = (struct partition *) (0x1BE + bh->b_data);
+	for (i=1 ; i<=4 ; minor++,i++,p++) {
+		if (!p->nr_sects)
+			continue;
+		add_partition(hd, minor, first_sector+p->start_sect, p->nr_sects);
+		if ((current_minor & 0x3f) >= 60)
+			continue;
+		if (p->sys_ind == EXTENDED_PARTITION) {
+			printk(" <");
+			extended_partition(hd, (hd->major << 8) | minor);
+			printk(" >");
+		}
+	}
+	/*
+	 * check for Disk Manager partition table
+	 */
+	if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
+		p = (struct partition *) (0x1BE + bh->b_data);
+		for (i = 4 ; i < 16 ; i++, current_minor++) {
+			p--;
+			if ((current_minor & mask) >= mask-2)
+				break;
+			if (!(p->start_sect && p->nr_sects))
+				continue;
+			add_partition(hd, current_minor, p->start_sect, p->nr_sects);
+		}
+	}
+	printk("\n");
+	brelse(bh);
+	return 1;
+}
+
+#endif /* CONFIG_MSDOS_PARTITION */
+
+#ifdef CONFIG_OSF_PARTITION
+
+static int osf_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+	int i;
+	struct buffer_head *bh;
+	struct disklabel {
+		u32 d_magic;
+		u16 d_type,d_subtype;
+		u8 d_typename[16];
+		u8 d_packname[16];
+		u32 d_secsize;
+		u32 d_nsectors;
+		u32 d_ntracks;
+		u32 d_ncylinders;
+		u32 d_secpercyl;
+		u32 d_secprtunit;
+		u16 d_sparespertrack;
+		u16 d_sparespercyl;
+		u32 d_acylinders;
+		u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
+		u32 d_headswitch, d_trkseek, d_flags;
+		u32 d_drivedata[5];
+		u32 d_spare[5];
+		u32 d_magic2;
+		u16 d_checksum;
+		u16 d_npartitions;
+		u32 d_bbsize, d_sbsize;
+		struct d_partition {
+			u32 p_size;
+			u32 p_offset;
+			u32 p_fsize;
+			u8  p_fstype;
+			u8  p_frag;
+			u16 p_cpg;
+		} d_partitions[8];
+	} * label;
+	struct d_partition * partition;
+#define DISKLABELMAGIC (0x82564557UL)
+
+	if (!(bh = bread(dev,0,1024))) {
+		printk("unable to read partition table\n");
+		return -1;
+	}
+	label = (struct disklabel *) (bh->b_data+64);
+	partition = label->d_partitions;
+	if (label->d_magic != DISKLABELMAGIC) {
+		printk("magic: %08x\n", label->d_magic);
+		brelse(bh);
+		return 0;
+	}
+	if (label->d_magic2 != DISKLABELMAGIC) {
+		printk("magic2: %08x\n", label->d_magic2);
+		brelse(bh);
+		return 0;
+	}
+	for (i = 0 ; i < label->d_npartitions; i++, partition++) {
+		if (partition->p_size)
+			add_partition(hd, current_minor,
+				first_sector+partition->p_offset,
+				partition->p_size);
+		current_minor++;
+	}
+	printk("\n");
+	brelse(bh);
+	return 1;
+}
+
+#endif /* CONFIG_OSF_PARTITION */
+
+static void check_partition(struct gendisk *hd, unsigned int dev)
+{
+	static int first_time = 1;
+	unsigned long first_sector;
+
 	if (first_time)
 		printk("Partition check:\n");
 	first_time = 0;
@@ -124,44 +242,16 @@
 		return;
 	}
 
-	if (!(bh = bread(dev,0,1024))) {
-		printk("  unable to read partition table of device %04x\n",dev);
+	printk("  %s%c:", hd->major_name, minor_name(hd, MINOR(dev)));
+#ifdef CONFIG_MSDOS_PARTITION
+	if (msdos_partition(hd, dev, first_sector))
 		return;
-	}
-	printk("  %s%c:", hd->major_name, minor_name(hd, minor));
-	current_minor += 4;  /* first "extra" minor (for extended partitions) */
-	if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
-		p = (struct partition *) (0x1BE + bh->b_data);
-		for (i=1 ; i<=4 ; minor++,i++,p++) {
-			if (!p->nr_sects)
-				continue;
-			add_partition(hd, minor, first_sector+p->start_sect, p->nr_sects);
-			if ((current_minor & 0x3f) >= 60)
-				continue;
-			if (p->sys_ind == EXTENDED_PARTITION) {
-				printk(" <");
-				extended_partition(hd, (hd->major << 8) | minor);
-				printk(" >");
-			}
-		}
-		/*
-		 * check for Disk Manager partition table
-		 */
-		if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
-			p = (struct partition *) (0x1BE + bh->b_data);
-			for (i = 4 ; i < 16 ; i++, current_minor++) {
-				p--;
-				if ((current_minor & mask) >= mask-2)
-					break;
-				if (!(p->start_sect && p->nr_sects))
-					continue;
-				add_partition(hd, current_minor, p->start_sect, p->nr_sects);
-			}
-		}
-	} else
-		printk(" bad partition table");
-	printk("\n");
-	brelse(bh);
+#endif
+#ifdef CONFIG_OSF_PARTITION
+	if (osf_partition(hd, dev, first_sector))
+		return;
+#endif
+	printk("unknown partition table\n");
 }
 
 /* This function is used to re-read partition tables for removable disks.
diff -u --recursive --new-file v1.3.6/linux/drivers/block/gscd.c linux/drivers/block/gscd.c
--- v1.3.6/linux/drivers/block/gscd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/gscd.c	Sat Jul  1 19:05:59 1995
@@ -0,0 +1,1129 @@
+#define GSCD_VERSION "0.4a Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>"
+
+/*
+	linux/drivers/block/gscd.c - GoldStar R420 CDROM driver
+
+        Copyright (C) 1995  Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
+        based upon pre-works by   Eberhard Moenkeberg <emoenke@gwdg.de>
+        
+
+        For all kind of other information about the GoldStar CDROM
+        and this Linux device driver I installed a WWW-URL:
+        http://linux.rz.fh-hannover.de/~raupach        
+
+
+             If you are the editor of a Linux CD, you should
+             enable gscd.c within your boot floppy kernel and
+             send me one of your CDs for free.
+
+
+        --------------------------------------------------------------------
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2, or (at your option)
+	any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* These settings are for various debug-level. Leave they untouched ... */ 
+#define  NO_GSCD_DEBUG 
+#define  NO_IOCTL_DEBUG
+#define  NO_MODULE_DEBUG
+#define  NO_FUTURE_WORK
+/*------------------------*/
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/malloc.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[] = UTS_RELEASE;
+#endif
+#endif MODULE
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR GOLDSTAR_CDROM_MAJOR
+#include "blk.h"
+#define gscd_port gscd /* for compatible parameter passing with "insmod" */
+#include <linux/gscd.h>
+
+
+static int gscdPresent            = 0;
+
+static unsigned char gscd_buf[2048];    /* buffer for block size conversion */
+static int   gscd_bn              = -1;
+static short gscd_port            = GSCD_BASE_ADDR;
+
+/* Kommt spaeter vielleicht noch mal dran ...
+ *    static struct wait_queue *gscd_waitq = NULL;
+ */ 
+
+static void gscd_transfer         (void);
+static void gscd_read_cmd         (void);
+static void gscd_hsg2msf          (long hsg, struct msf *msf);
+static void gscd_bin2bcd          (unsigned char *p);
+
+/* Schnittstellen zum Kern/FS */
+
+static void do_gscd_request       (void);
+static int  gscd_ioctl            (struct inode *, struct file *, unsigned int, unsigned long);
+static int  gscd_open             (struct inode *, struct file *);
+static void gscd_release          (struct inode *, struct file *);
+static int  check_gscd_med_chg    (dev_t);
+
+/*      GoldStar Funktionen    */
+
+static void cc_Reset             (void);
+static int  wait_drv_ready       (void);
+static int  find_drives          (void);
+static void cmd_out              (int, char *, char *, int);
+static void cmd_status           (void);
+static void cc_Ident             (char *);
+static void cc_SetSpeed          (void);
+static void init_cd_drive        (int);
+
+static int  get_status           (void);
+static void clear_Audio          (void);
+static void cc_invalidate        (void);
+
+/* some things for the next version */
+#ifdef FUTURE_WORK
+static void update_state          (void);
+static long gscd_msf2hsg          (struct msf *mp);
+static int  gscd_bcd2bin          (unsigned char bcd);
+#endif
+
+/*    common GoldStar Initialization    */
+
+static long my_gscd_init (unsigned long , unsigned long);
+
+
+/*      lo-level cmd-Funktionen    */
+
+static void cmd_info_in          ( char *, int );
+static void cmd_end              ( void );
+static void cmd_read_b           ( char *, int, int );
+static void cmd_read_w           ( char *, int, int );
+static int  cmd_unit_alive       ( void );
+static void cmd_write_cmd        ( char * );
+
+
+/*      GoldStar Variablen     */
+
+static int  curr_drv_state;
+static int  drv_states[]       = {0,0,0,0,0,0,0,0};
+static int  drv_mode;
+static int  disk_state;
+static int  speed;
+static int  ndrives;
+
+static unsigned char drv_num_read;
+static unsigned char f_dsk_valid;
+static unsigned char current_drive;
+static unsigned char f_drv_ok;
+
+
+static char f_AudioPlay;
+static char f_AudioPause;
+static int  AudioStart_m;
+static int  AudioStart_f;
+static int  AudioEnd_m;
+static int  AudioEnd_f;
+ 
+
+static struct file_operations gscd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	gscd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	gscd_open,		/* open */
+	gscd_release,		/* release */
+	NULL,                   /* fsync */
+	NULL,                   /* fasync*/
+	check_gscd_med_chg,     /* media change */
+	NULL                    /* revalidate */
+};
+
+/* 
+ * Checking if the media has been changed
+ * (not yet implemented)
+ */
+static int check_gscd_med_chg (dev_t full_dev)
+{
+   int target;
+
+
+   target = MINOR(full_dev);
+
+   if (target > 0) 
+   {
+      printk("GSCD: GoldStar CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   #ifdef GSCD_DEBUG
+   printk ("gscd: check_med_change\n");
+   #endif
+ 
+  return 0;
+}
+
+
+void gscd_setup (char *str, int *ints)
+{
+  if (ints[0] > 0) 
+  {
+     gscd_port = ints[1];
+  }
+}
+
+
+static int gscd_ioctl (struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+unsigned char to_do[10];
+unsigned char dummy;
+
+                             
+    switch (cmd)
+    {
+       case CDROMSTART:     /* Spin up the drive */
+		/* Don't think we can do this.  Even if we could,
+ 		 * I think the drive times out and stops after a while
+		 * anyway.  For now, ignore it.
+		 */
+            return 0;
+
+       case CDROMRESUME:   /* keine Ahnung was das ist */
+            return 0;
+
+
+       case CDROMEJECT:
+            cmd_status ();
+            to_do[0] = CMD_TRAY_CTL;
+            cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+
+            return 0;
+
+       default:
+            return -EINVAL;
+    }
+
+}
+
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+
+static void gscd_transfer (void)
+{
+long offs;
+
+	while (CURRENT -> nr_sectors > 0 && gscd_bn == CURRENT -> sector / 4)
+	{
+		offs = (CURRENT -> sector & 3) * 512;
+		memcpy(CURRENT -> buffer, gscd_buf + offs, 512);
+		CURRENT -> nr_sectors--;
+		CURRENT -> sector++;
+		CURRENT -> buffer += 512;
+	}
+}
+
+
+/*
+ * I/O request routine called from Linux kernel.
+ */
+
+static void do_gscd_request (void)
+{
+unsigned int block,dev;
+unsigned int nsect;
+
+repeat:
+	if (!(CURRENT) || CURRENT->dev < 0) return;
+	INIT_REQUEST;
+	dev = MINOR(CURRENT->dev);
+	block = CURRENT->sector;
+	nsect = CURRENT->nr_sectors;
+
+	if (CURRENT == NULL || CURRENT -> sector == -1)
+		return;
+
+	if (CURRENT -> cmd != READ)
+	{
+		printk("GSCD: bad cmd %d\n", CURRENT -> cmd);
+		end_request(0);
+		goto repeat;
+	}
+
+	if (MINOR(CURRENT -> dev) != 0)
+	{
+		printk("GSCD: this version supports only one device\n");
+		end_request(0);
+		goto repeat;
+	}
+
+	gscd_transfer();
+
+	/* if we satisfied the request from the buffer, we're done. */
+
+	if (CURRENT -> nr_sectors == 0)
+	{
+		end_request(1);
+		goto repeat;
+	}
+
+#ifdef GSCD_DEBUG
+        printk ("GSCD: dev %d, block %d, nsect %d\n", dev, block, nsect );
+#endif
+
+	gscd_read_cmd ();
+}
+
+
+
+/*
+ * Check the result of the set-mode command.  On success, send the
+ * read-data command.
+ */
+
+static void
+gscd_read_cmd (void)
+{
+long   block;
+struct gscd_Play_msf gscdcmd;
+char   cmd[] = { CMD_READ, 0x80, 0,0,0, 0,1 }; /* cmd mode M-S-F secth sectl */
+
+
+
+        cmd_status ();
+        if ( disk_state & (ST_NO_DISK | ST_DOOR_OPEN) )
+        {
+           printk ( "GSCD: no disk or door open\n" );
+           end_request (0);
+        }
+        else
+        {
+           if ( disk_state & ST_INVALID )
+           {
+              printk ( "GSCD: disk invalid\n" );
+              end_request (0);
+           }
+           else
+           {
+              gscd_bn = -1;		/* purge our buffer */
+              block = CURRENT -> sector / 4;
+              gscd_hsg2msf(block, &gscdcmd.start);	/* cvt to msf format */
+
+              cmd[2] = gscdcmd.start.min;
+              cmd[3] = gscdcmd.start.sec;
+              cmd[4] = gscdcmd.start.frame;
+
+#ifdef GSCD_DEBUG
+              printk ("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3], cmd[4] ); 
+#endif 
+              cmd_out ( TYPE_DATA, (char *)&cmd, (char *)&gscd_buf[0], 1 );
+        
+              gscd_bn = CURRENT -> sector / 4;
+              gscd_transfer();
+              end_request(1);
+	   }
+	}
+	SET_TIMER(do_gscd_request, 1);
+}
+
+
+/*
+ * Open the device special file.  Check that a disk is in.
+ */
+
+static int gscd_open (struct inode *ip, struct file *fp)
+{
+int   st;
+
+#ifdef GSCD_DEBUG
+printk ( "GSCD: open\n" );
+#endif
+
+	if (gscdPresent == 0)
+		return -ENXIO;			/* no hardware */
+
+        get_status ();
+        st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN);
+        if ( st )
+        {
+           printk ( "GSCD: no disk or door open\n" );
+           return -ENXIO;
+        }
+                   
+/*	if (updateToc() < 0)
+		return -EIO;
+*/
+
+        #ifdef MODULE
+           MOD_INC_USE_COUNT;
+        #endif
+ 
+	return 0;
+}
+
+
+/*
+ * On close, we flush all gscd blocks from the buffer cache.
+ */
+
+static void gscd_release (struct inode * inode, struct file * file)
+{
+
+#ifdef GSCD_DEBUG
+printk ( "GSCD: release\n" );
+#endif
+
+	gscd_bn = -1;
+	sync_dev(inode->i_rdev);
+	invalidate_buffers(inode -> i_rdev);
+
+        #ifdef MODULE
+           MOD_DEC_USE_COUNT;
+        #endif
+}
+
+
+int get_status (void)
+{
+int  status;
+
+    cmd_status ();
+    status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01);
+     
+    if ( status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01) )
+    {
+       cc_invalidate ();
+       return 1;
+    }
+    else
+    {
+       return 0;
+    }
+}
+
+
+void cc_invalidate (void)
+{
+   drv_num_read  = 0xFF;
+   f_dsk_valid   = 0xFF;
+   current_drive = 0xFF;
+   f_drv_ok      = 0xFF;
+
+   clear_Audio ();
+   
+}   
+
+void clear_Audio (void)
+{
+
+   f_AudioPlay = 0;
+   f_AudioPause = 0;
+   AudioStart_m = 0;
+   AudioStart_f = 0;
+   AudioEnd_m   = 0;
+   AudioEnd_f   = 0;
+   
+}
+
+/*
+ *   waiting ?  
+ */
+
+int wait_drv_ready (void)
+{
+int found, read;
+
+   do
+   {  
+       found = inb ( GSCDPORT(0) ); 
+       found &= 0x0f;
+       read  = inb ( GSCDPORT(0) );
+       read  &= 0x0f;
+   } while ( read != found );
+   
+#ifdef GSCD_DEBUG
+printk ( "Wait for: %d\n", read );
+#endif   
+   
+   return read;
+}
+
+void cc_Ident (char * respons)
+{
+char to_do [] = {CMD_IDENT, 0, 0};
+
+    cmd_out (TYPE_INFO, (char *)&to_do, (char *)respons, (int)0x1E );
+
+}
+
+void cc_SetSpeed (void)
+{
+char to_do [] = {CMD_SETSPEED, 0, 0};
+char dummy;
+
+    if ( speed > 0 )
+    {
+       to_do[1] = speed & 0x0F;
+       cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+    }
+}
+
+
+void cc_Reset (void)
+{
+char to_do [] = {CMD_RESET, 0};
+char dummy;
+
+   cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+}
+
+
+
+void cmd_status (void)
+{
+char to_do [] = {CMD_STATUS, 0};
+char dummy;
+
+   cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+
+#ifdef GSCD_DEBUG
+printk ("GSCD: Status: %d\n", disk_state );
+#endif
+
+}
+
+void cmd_out ( int cmd_type, char * cmd, char * respo_buf, int respo_count )
+{
+int        result;
+
+
+       result = wait_drv_ready ();
+       if ( result != drv_mode )
+       {
+       unsigned long test_loops = 0xFFFF;
+       int           i,dummy;
+
+          outb ( curr_drv_state, GSCDPORT(0));
+          
+          /* LOCLOOP_170 */
+          do
+          {
+             result = wait_drv_ready ();
+             test_loops--;
+          } while ( (result != drv_mode) && (test_loops > 0) );
+
+          if ( result != drv_mode )
+          {
+             disk_state = ST_x08 | ST_x04 | ST_INVALID;
+             return;
+          }          
+        
+          /* ...and waiting */
+          for ( i=1,dummy=1 ; i<0xFFFF ; i++ )
+          {
+             dummy *= i;
+          }              
+       }
+
+       /* LOC_172 */    
+       /* check the unit */
+       /* and wake it up */
+       if ( cmd_unit_alive () != 0x08 )
+       {
+          /* LOC_174 */
+          /* game over for this unit */
+          disk_state = ST_x08 | ST_x04 | ST_INVALID;
+          return;
+       }
+       
+       /* LOC_176 */
+       #ifdef GSCD_DEBUG
+        printk ("LOC_176 ");
+       #endif       
+       if ( drv_mode == 0x09 )
+       {
+          /* magic... */
+          printk ("GSCD: magic ...\n");       
+          outb ( result, GSCDPORT(2));
+       }
+
+       /* write the command to the drive */
+       cmd_write_cmd (cmd);
+
+       /* LOC_178 */
+       for (;;)
+       {
+          result = wait_drv_ready ();
+          if ( result != drv_mode )
+          {
+             /* LOC_179 */
+             if ( result == 0x04 )                  /* Mode 4 */
+             {
+                /* LOC_205 */
+                #ifdef GSCD_DEBUG
+                printk ("LOC_205 ");	              
+                #endif
+                disk_state = inb ( GSCDPORT (2));
+             
+                do
+                {
+                   result = wait_drv_ready ();
+                } while ( result != drv_mode );
+                return;
+
+             }
+             else
+             {
+                if ( result == 0x06 )               /* Mode 6 */
+                {
+                   /* LOC_181 */
+                   #ifdef GSCD_DEBUG
+                    printk ("LOC_181 ");	              
+                   #endif
+
+                   if (cmd_type == TYPE_DATA)
+                   {
+                      /* read data */
+                      /* LOC_184 */
+                      if ( drv_mode == 9 )
+                      {
+                         /* read the data to the buffer (word) */
+ 
+                         /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
+                         cmd_read_w ( respo_buf, respo_count, CD_FRAMESIZE/2 );
+                         return;
+                      } 
+                      else
+                      { 
+                         /* read the data to the buffer (byte) */
+                         
+                         /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW)    */
+                         cmd_read_b ( respo_buf, respo_count, CD_FRAMESIZE );
+                         return;
+                      }
+                   }
+                   else
+                   { 
+                      /* read the info to the buffer */
+                      cmd_info_in ( respo_buf, respo_count ); 
+                      return;
+                   }                  
+
+                   return;
+                }
+             }
+
+          }
+          else
+          {
+             disk_state = ST_x08 | ST_x04 | ST_INVALID;         
+             return;
+          }    
+       } /* for (;;) */
+
+
+#ifdef GSCD_DEBUG
+printk ("\n");       
+#endif    
+}
+
+
+static void cmd_write_cmd ( char *pstr )
+{
+int  i,j;
+
+    /* LOC_177 */
+    #ifdef GSCD_DEBUG
+     printk ("LOC_177 ");       
+    #endif
+       
+    /* calculate the number of parameter */
+    j = *pstr & 0x0F;
+
+    /* shift it out */
+    for ( i=0 ; i<j ; i++ )
+    {
+       outb ( *pstr, GSCDPORT(2) );
+       pstr++;
+    }
+}
+
+
+static int cmd_unit_alive ( void )
+{
+int            result;
+unsigned long  max_test_loops;
+
+
+    /* LOC_172 */
+    #ifdef GSCD_DEBUG
+     printk ("LOC_172 ");       
+    #endif
+
+    outb ( curr_drv_state, GSCDPORT(0));       
+    max_test_loops = 0xFFFF;
+     
+    do
+    {
+       result = wait_drv_ready ();
+       max_test_loops--;
+    } while ( (result != 0x08) && (max_test_loops > 0) );
+
+    return result;
+}       
+
+
+static void cmd_info_in ( char *pb, int count )
+{
+int   result;
+char  read;
+
+
+        /* read info */
+        /* LOC_182 */
+        #ifdef GSCD_DEBUG
+         printk ("LOC_182 ");				              
+        #endif
+       
+        do
+        {
+           read = inb (GSCDPORT(2)); 
+           if ( count > 0 )
+           {
+              *pb = read;
+              pb++;
+              count--;
+           }
+                         
+           /* LOC_183 */
+           do
+           {
+              result = wait_drv_ready ();
+           } while ( result == 0x0E );
+        } while ( result == 6 );                     
+
+        cmd_end ();
+        return;
+}
+
+
+static void cmd_read_b ( char *pb, int count, int size )
+{
+int  result;
+int  i;
+                     
+                     
+       /* LOC_188 */
+       /* LOC_189 */
+       #ifdef GSCD_DEBUG
+        printk ("LOC_189 ");			              
+       #endif
+
+       do 
+       {
+          do
+          {
+             result = wait_drv_ready ();
+          } while ( result != 6 || result == 0x0E );
+                         
+          if ( result != 6 )
+          {
+             cmd_end ();
+             return;
+          }                       
+          
+          #ifdef GSCD_DEBUG
+           printk ("LOC_191 ");       
+          #endif
+
+          for ( i=0 ; i< size ; i++ )
+          {
+             *pb = inb (GSCDPORT(2));
+             pb++;
+          }
+          count--;
+       } while ( count > 0 );
+       
+       cmd_end ();
+       return;
+}
+
+
+static void cmd_end (void)
+{
+int   result;
+
+
+      /* LOC_204 */
+      #ifdef GSCD_DEBUG
+       printk ("LOC_204 ");				              
+      #endif
+      
+      do
+      {
+         result = wait_drv_ready ();
+         if ( result == drv_mode )
+         {
+            return;
+         }
+      } while ( result != 4 );
+
+      /* LOC_205 */
+      #ifdef GSCD_DEBUG
+       printk ("LOC_205 ");			              
+      #endif
+       
+      disk_state = inb ( GSCDPORT (2));
+             
+      do
+      {
+         result = wait_drv_ready ();
+      } while ( result != drv_mode );
+      return;
+
+}
+
+
+static void cmd_read_w ( char *pb, int count, int size )
+{
+int  result;
+int  i;
+
+
+    #ifdef GSCD_DEBUG
+     printk ("LOC_185 ");			              
+    #endif
+ 
+    do 
+    {
+       /* LOC_185 */
+       do
+       {
+          result = wait_drv_ready ();
+       } while ( result != 6 || result == 0x0E );
+                         
+       if ( result != 6 )
+       {
+          cmd_end ();
+          return;
+       }                       
+                         
+       for ( i=0 ; i<size ; i++ )
+       {
+           /* na, hier muss ich noch mal drueber nachdenken */
+           *pb = inw(GSCDPORT(2));
+           pb++;
+        }
+        count--;
+     } while ( count > 0 );
+                    
+     cmd_end ();
+     return;
+}
+
+int find_drives (void)
+{
+int *pdrv;
+int drvnum;
+int subdrv;
+int i;
+
+    speed           = 0;
+    pdrv            = (int *)&drv_states;
+    curr_drv_state  = 0xFE;
+    subdrv          = 0;
+    drvnum          = 0;
+
+    for ( i=0 ; i<8 ; i++ )
+    { 
+       subdrv++;
+       cmd_status ();
+       disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01;
+       if ( disk_state != (ST_x08 | ST_x04 | ST_INVALID) )
+       {
+          /* LOC_240 */
+          *pdrv = curr_drv_state;
+          init_cd_drive (drvnum);
+          pdrv++;
+          drvnum++;
+       }
+       else
+       {
+          if ( subdrv < 2 )
+          {
+             continue;
+          }
+          else
+          {
+             subdrv = 0;
+          }
+       }   
+
+/*       curr_drv_state<<1;         <-- das geht irgendwie nicht */ 
+/* muss heissen:    curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
+         curr_drv_state *= 2;
+         curr_drv_state |= 1;
+#ifdef GSCD_DEBUG
+         printk ("DriveState: %d\n", curr_drv_state );
+#endif
+    } 
+
+    ndrives = drvnum;
+    return drvnum;
+}    
+
+void init_cd_drive ( int num )
+{
+char resp [50];
+int  i;
+
+   printk ("GSCD: init unit %d\n", num );
+   cc_Ident ((char *)&resp);
+
+   printk ("GSCD: identification: ");
+   for ( i=0 ; i<0x1E; i++ )
+   {
+      printk ( "%c", resp[i] );
+   }
+   printk ("\n");
+   
+   cc_SetSpeed ();  
+
+}    
+
+#ifdef FUTURE_WORK
+/* return_done */
+static void update_state ( void )
+{
+unsigned int AX;
+
+
+    if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 )
+    {
+       if ( disk_state == (ST_x08 | ST_x04 | ST_INVALID))
+       {
+          AX = ST_INVALID;
+       }
+  
+       if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 )
+       {
+          invalidate ();
+          f_drv_ok = 0;
+       }
+       
+       AX |= 0x8000;
+    }
+    
+    if ( disk_state & ST_PLAYING )
+    {
+       AX |= 0x200;
+    }
+    
+    AX |= 0x100;
+    /* pkt_esbx = AX; */
+
+    disk_state = 0;
+    
+}
+#endif
+
+/* Init for the Module-Version */
+int init_module (void)
+{
+long err;
+
+
+     /* call the GoldStar-init with dummys */
+     err = my_gscd_init ( 10, 20 );
+
+     if ( err < 0 )
+     {
+         return -EIO;
+     }
+     else
+     {
+        printk ( "Happy GoldStar !\n" );
+        return 0;
+     }    
+}
+
+#ifdef MODULE
+void cleanup_module (void)
+{
+
+   if (MOD_IN_USE)
+   {
+      printk("GoldStar-module in use - can't remove it.\n" );
+      return;
+   }
+ 
+   if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL))
+   {
+      printk("What's that: can't unregister GoldStar-module\n" );
+      return;
+   }
+
+   release_region (gscd_port,4);
+   printk( "GoldStar-module released.\n" );
+}
+#endif
+
+
+/* Test for presence of drive and initialize it.  Called only at boot time. */
+unsigned long gscd_init (unsigned long mem_start, unsigned long mem_end)
+{
+unsigned long err;
+
+   err = my_gscd_init ( mem_start, mem_end );
+   return ( labs(err));
+}
+
+
+/* This is the common initalisation for the GoldStar drive. */
+/* It is called at boot time AND for module init.           */
+long my_gscd_init (unsigned long mem_start, unsigned long mem_end)
+{
+int i;
+int result;
+
+        printk ("GSCD: version %s\n", GSCD_VERSION);
+        printk ("GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port);
+
+        if (check_region(gscd_port, 4)) 
+        {
+	  printk("GSCD: Init failed, I/O port (%X) already in use.\n", gscd_port);
+	  return -mem_start;
+	}
+	  
+
+	/* check for card */
+	result = wait_drv_ready ();
+        if ( result == 0x09 )
+        {
+           printk ("GSCD: DMA kann ich noch nicht!\n" );
+           return -mem_start;
+        }
+ 
+        if ( result == 0x0b )
+        {
+           drv_mode = result;
+           i = find_drives ();
+           if ( i == 0 )
+           {
+              printk ( "GSCD: GoldStar CD-ROM Drive is not found.\n" );
+              return -mem_start;
+           }
+        }
+
+        if ( (result != 0x0b) && (result != 0x09) )
+        {
+              printk ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n" );
+              return -mem_start;
+        }          		            
+
+        /* reset all drives */
+        i = 0;
+        while ( drv_states[i] != 0 )
+        {
+           curr_drv_state = drv_states[i];
+           printk ( "GSCD: Reset unit %d ... ",i );
+           cc_Reset ();
+           printk ( "done\n" );
+           i++;
+        }
+
+	if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0)
+	{
+		printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n",
+		       MAJOR_NR);
+		return -mem_start;
+	}
+
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+        
+        disk_state = 0;
+        gscdPresent = 1;
+
+	request_region(gscd_port, 4, "gscd");
+
+        printk ( "GSCD: GoldStar CD-ROM Drive found.\n" );
+	return mem_start;
+}
+
+static void gscd_hsg2msf (long hsg, struct msf *msf)
+{
+	hsg += CD_BLOCK_OFFSET;
+	msf -> min = hsg / (CD_FRAMES*CD_SECS);
+	hsg %= CD_FRAMES*CD_SECS;
+	msf -> sec = hsg / CD_FRAMES;
+	msf -> frame = hsg % CD_FRAMES;
+
+	gscd_bin2bcd(&msf -> min);		/* convert to BCD */
+	gscd_bin2bcd(&msf -> sec);
+	gscd_bin2bcd(&msf -> frame);
+}
+ 
+
+static void gscd_bin2bcd (unsigned char *p)
+{
+int u, t;
+
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
+}
+
+
+#ifdef FUTURE_WOTK
+static long gscd_msf2hsg (struct msf *mp)
+{
+	return gscd_bcd2bin(mp -> frame)
+		+ gscd_bcd2bin(mp -> sec) * CD_FRAMES
+		+ gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS
+		- CD_BLOCK_OFFSET;
+}
+
+static int gscd_bcd2bin (unsigned char bcd)
+{
+	return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+#endif
+
+
diff -u --recursive --new-file v1.3.6/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.6/linux/drivers/block/ll_rw_blk.c	Tue Jun 27 14:11:33 1995
+++ linux/drivers/block/ll_rw_blk.c	Sat Jul  1 19:05:59 1995
@@ -592,26 +592,41 @@
 #ifdef CONFIG_BLK_DEV_XD
 	mem_start = xd_init(mem_start,mem_end);
 #endif
-#ifdef CONFIG_CDU31A
-	mem_start = cdu31a_init(mem_start,mem_end);
-#endif
-#ifdef CONFIG_CDU535
-	mem_start = sony535_init(mem_start,mem_end);
-#endif
-#ifdef CONFIG_MCD
-	mem_start = mcd_init(mem_start,mem_end);
-#endif
-#ifdef CONFIG_AZTCD
-        mem_start = aztcd_init(mem_start,mem_end);
-#endif
 #ifdef CONFIG_BLK_DEV_FD
 	floppy_init();
 #else
 	outb_p(0xc, 0x3f2);
 #endif
+#ifdef CONFIG_CDU31A
+	mem_start = cdu31a_init(mem_start,mem_end);
+#endif CONFIG_CDU31A
+#ifdef CONFIG_MCD
+	mem_start = mcd_init(mem_start,mem_end);
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+	mem_start = mcdx_init(mem_start,mem_end);
+#endif CONFIG_MCDX
 #ifdef CONFIG_SBPCD
 	mem_start = sbpcd_init(mem_start, mem_end);
 #endif CONFIG_SBPCD
+#ifdef CONFIG_AZTCD
+        mem_start = aztcd_init(mem_start,mem_end);
+#endif CONFIG_AZTCD
+#ifdef CONFIG_CDU535
+	mem_start = sony535_init(mem_start,mem_end);
+#endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+	mem_start = gscd_init(mem_start, mem_end);
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+	mem_start = cm206_init(mem_start, mem_end);
+#endif
+#ifdef CONFIG_OPTCD
+	mem_start = optcd_init(mem_start,mem_end);
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+	mem_start = sjcd_init(mem_start,mem_end);
+#endif CONFIG_SJCD
 	if (ramdisk_size)
 		mem_start += rd_init(mem_start, ramdisk_size*1024);
 	return mem_start;
diff -u --recursive --new-file v1.3.6/linux/drivers/block/mcd.c linux/drivers/block/mcd.c
--- v1.3.6/linux/drivers/block/mcd.c	Fri Feb  3 15:29:33 1995
+++ linux/drivers/block/mcd.c	Sat Jul  1 19:05:59 1995
@@ -3,7 +3,7 @@
 
 	Copyright (C) 1992  Martin Harriss
 
-	martin@bdsi.com
+	martin@bdsi.com (no longer valid - where are you now, Martin?)
 
 	This program is free software; you can redistribute it and/or modify
 	it under the terms of the GNU General Public License as published by
@@ -32,13 +32,29 @@
    		   (Jon Tombs <jon@gtex02.us.es>)
 
 	October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
-	Braunschweig, Germany: Total rework to speed up data read operation.
+	Braunschweig, Germany: rework to speed up data read operation.
 	Also enabled definition of irq and address from bootstrap, using the
-	environment. linux/init/main.c must be patched to export the env.
+	environment.
 	November 93 added code for FX001 S,D (single & double speed).
 	February 94 added code for broken M 5/6 series of 16-bit single speed.
+
+        0.4   Added support for loadable MODULEs, so mcd can now also be
+              loaded by insmod and removed by rmmod during runtime.
+              Werner Zimmermann (zimmerma@rz.fht-esslingen.de), Mar. 26, 95
 */
 
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+    char kernel_version[]= UTS_RELEASE;
+# endif
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/errno.h>
 #include <linux/signal.h>
@@ -59,6 +75,7 @@
 
 #define MAJOR_NR MITSUMI_CDROM_MAJOR
 #include "blk.h"
+#define mcd_port mcd    /* for compatible parameter passing with "insmod" */
 #include <linux/mcd.h>
 
 #if 0
@@ -114,8 +131,8 @@
 int mitsumi_bug_93_wait = 0;
 #endif /* WORK_AROUND_MITSUMI_BUG_93 */
 
-static short mcd_port = MCD_BASE_ADDR;
-static int   mcd_irq  = MCD_INTR_NR;
+static short mcd_port = MCD_BASE_ADDR; /* used as "mcd" by "insmod" */
+static int   mcd_irq  = MCD_INTR_NR; /* must directly follow mcd_port */
 
 static int McdTimeout, McdTries;
 static struct wait_queue *mcd_waitq = NULL;
@@ -1039,7 +1056,7 @@
 
 	}
 	++mcd_open_count;
-
+        MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -1050,7 +1067,7 @@
 
 static void
 mcd_release(struct inode * inode, struct file * file)
-{
+{ MOD_DEC_USE_COUNT;
   if (!--mcd_open_count) {
 	mcd_invalidate_buffers();
 	sync_dev(inode->i_rdev);
@@ -1080,15 +1097,23 @@
  * Test for presence of drive and initialize it.  Called at boot time.
  */
 
+#ifndef MODULE
 unsigned long
 mcd_init(unsigned long mem_start, unsigned long mem_end)
+#else
+int init_module(void)
+#endif
 {
 	int count;
 	unsigned char result[3];
 
 	if (mcd_port <= 0 || mcd_irq <= 0) {
 	  printk("skip mcd_init\n");
+#ifndef MODULE
 	  return mem_start;
+#else
+          return -EIO;
+#endif
 	}
 
 	printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
@@ -1097,13 +1122,22 @@
 	{
 		printk("Unable to get major %d for Mitsumi CD-ROM\n",
 		       MAJOR_NR);
+#ifndef MODULE
 		return mem_start;
+#else
+                return -EIO;
+#endif
+              
 	}
 
         if (check_region(mcd_port, 4)) {
 	  printk("Init failed, I/O port (%X) already in use\n",
 		 mcd_port);
+#ifndef MODULE		 
 	  return mem_start;
+#else
+          return -EIO;
+#endif	  
 	}
 	  
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
@@ -1123,7 +1157,11 @@
 	if (count >= 2000000) {
 		printk("Init failed. No mcd device at 0x%x irq %d\n",
 		     mcd_port, mcd_irq);
+#ifndef MODULE
 		return mem_start;
+#else
+                return -EIO;
+#endif                
 	}
 	count = inb(MCDPORT(0));		/* pick up the status */
 	
@@ -1132,12 +1170,19 @@
 		if(getValue(result+count)) {
 			printk("mitsumi get version failed at 0x%d\n",
 			       mcd_port);
+#ifndef MODULE
 			return mem_start;
+#else
+                        return -EIO;
+#endif
 		}	
 
 	if (result[0] == result[1] && result[1] == result[2])
+#ifndef MODULE
 		return mem_start;
-
+#else
+                return -EIO;
+#endif
 	printk("Mitsumi status, type and version : %02X %c %x\n",
 	       result[0],result[1],result[2]);
 
@@ -1153,7 +1198,11 @@
 	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD"))
 	{
 		printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
+#ifndef MODULE
 		return mem_start;
+#else
+                return -EIO;
+#endif
 	}
 	request_region(mcd_port, 4,"mcd");
 
@@ -1169,7 +1218,11 @@
 
 	mcd_invalidate_buffers();
 	mcdPresent = 1;
+#ifndef MODULE	
 	return mem_start;
+#else
+        return 0;
+#endif
 }
 
 
@@ -1520,3 +1573,17 @@
 	return limit > 0 ? 0 : -1;
 }
 
+#ifdef MODULE
+void cleanup_module(void)
+{ if (MOD_IN_USE)
+     { printk("mcd module in use - can't remove it.\n");
+       return;    
+     }
+  if ((unregister_blkdev(MAJOR_NR, "mcd") == -EINVAL))
+     { printk("What's that: can't unregister mcd\n");
+       return;    
+     }
+  release_region(mcd_port,4);
+  printk("mcd module released.\n");
+}
+#endif MODULE
diff -u --recursive --new-file v1.3.6/linux/drivers/block/mcdx.c linux/drivers/block/mcdx.c
--- v1.3.6/linux/drivers/block/mcdx.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/mcdx.c	Sat Jul  1 19:05:59 1995
@@ -0,0 +1,1533 @@
+/*
+ * The Mitsumi CDROM interface
+ * Copyright (C) 1995 Heiko Schlittermann
+ * VERSION: @VERSION@
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to
+ *  The Linux Community at all and ...
+ *  Martin Harriss (he wrote the first Mitsumi Driver)
+ *  Eberhard Moenkeberg (he gave me much support and the initial kick)
+ *  Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they
+ *      improved the original driver)
+ *  John Tombs, Bjorn Ekwall (module support)
+ *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
+ *  Gerd Knorr (he lent me his PhotoCD)
+ *  Nils Faerber and Roger E. Wolff (extensivly tested the LU portion)
+ *  ... somebody forgotten?
+ *  
+ */
+
+
+#if RCS
+static const char *mcdx_c_version
+		= "$Id: mcdx.c,v 1.2 1995/06/18 18:00:53 heiko Exp $";
+#endif
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]=UTS_RELEASE;
+#endif
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#define MOD_IN_USE 1
+#endif MODULE
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR MITSUMI_X_CDROM_MAJOR
+#include "blk.h"
+#define	mcdx_drive_map mcdx /* for compatible parameter passing with "insmod" */
+#include <linux/mcdx.h>
+
+/* CONSTANTS *******************************************************/
+
+const int REQUEST_SIZE = 200;
+const int DIRECT_SIZE = 200;
+
+enum drivemodes { TOC, DATA, RAW, COOKED };
+enum datamodes { MODE0, MODE1, MODE2 };
+enum resetmodes { SOFT, HARD };
+
+const int SINGLE = 0x01;
+const int DOUBLE = 0x02;
+const int DOOR   = 0x04;
+const int MULTI  = 0x08;
+
+const unsigned char READSSPEED = 0xc0;
+const unsigned char READDSPEED = 0xc1;
+
+
+/* DECLARATIONS ****************************************************/ 
+struct s_msf {
+	unsigned char minute;
+	unsigned char second;
+	unsigned char frame;
+};
+
+struct s_subqcode {
+	unsigned char control;
+	unsigned char tno;
+	unsigned char index;
+	struct s_msf tt;
+	struct s_msf dt;
+};
+
+struct s_diskinfo {
+	unsigned int n_first;
+	unsigned int n_last;
+	struct s_msf msf_leadout;
+	struct s_msf msf_first;
+};
+	
+struct s_multi {
+	unsigned char multi;
+	struct s_msf msf_last;
+};
+
+struct s_version {
+	unsigned char code;
+	unsigned char ver;
+};
+
+/* Per drive/controller stuff **************************************/
+
+struct s_drive_stuff {
+	/* waitquenes */
+    struct wait_queue *busyq;
+    struct wait_queue *lockq;
+    struct wait_queue *sleepq;
+
+ 	/* flags */
+    volatile int introk;	/* status of last irq operation */
+    volatile int busy;		/* drive performs an operation */
+    volatile int lock;		/* exclusive usage */
+
+	/* cd infos */
+	struct s_diskinfo di;
+	struct s_multi multi;
+	struct s_subqcode* toc;	/* first enty of the toc array */
+	struct s_subqcode start;
+	struct s_subqcode stop;
+	int xa;					/* 1 if xa disk */
+	int audio;				/* 1 if audio disk */
+	int audiostatus;			
+
+	/* `buffer' control */
+    volatile int valid;
+    volatile int pending;
+    volatile int off_direct;
+    volatile int off_requested;
+
+	/* adds and odds */
+	void* wreg_data;	/* w data */
+	void* wreg_reset;	/* w hardware reset */
+	void* wreg_hcon;	/* w hardware conf */
+	void* wreg_chn;		/* w channel */
+	void* rreg_data;	/* r data */
+	void* rreg_status;	/* r status */
+
+    int irq;			/* irq used by this drive */
+    int minor;			/* minor number of this drive */
+    int present;	    /* drive present and its capabilities */
+    char readcmd;		/* read cmd depends on single/double speed */
+    unsigned long changed;	/* last jiff the media was changed */
+    int users;				/* keeps track of open/close */
+    int lastsector;			/* last block accessible */
+    int errno;				/* last operation's error */
+
+};
+
+/* Prototypes ******************************************************/ 
+
+/*	The following prototypes are already declared elsewhere.  They are
+ 	repeated here to show what's going on.  And to sense, if they're
+	changed elsewhere. */
+
+/* declared in blk.h */
+unsigned long mcdx_init(unsigned long mem_start, unsigned long mem_end);
+void do_mcdx_request(void);
+
+int check_mcdx_media_change(dev_t);
+
+/* already declared in init/main */
+void mcdx_setup(char *, int *);
+
+/*	Indirect exported functions. These functions are exported by their
+	addresses, such as mcdx_open and mcdx_close in the 
+	structure fops. */
+
+/* ???  exported by the mcdx_sigaction struct */
+static void mcdx_intr(int, struct pt_regs*);
+
+/* exported by file_ops */
+static int mcdx_open(struct inode*, struct file*);
+static void mcdx_close(struct inode*, struct file*);
+static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
+
+/* misc internal support functions */
+static void log2msf(unsigned int, struct s_msf*);
+static unsigned int msf2log(const struct s_msf*);
+static unsigned int uint2bcd(unsigned int);
+static unsigned int bcd2uint(unsigned char);
+#if MCDX_DEBUG
+static void TRACE((int level, const char* fmt, ...));
+#endif
+#ifndef NOWARN
+static void WARN((const char* fmt, ...));
+#endif
+static char *port(int*);
+static int irq(int*);
+static void mcdx_delay(struct s_drive_stuff*, long jifs);
+static int mcdx_transfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors);
+
+static int mcdx_config(struct s_drive_stuff*, int);
+static int mcdx_closedoor(struct s_drive_stuff*, int);
+static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int);
+static int mcdx_lockdoor(struct s_drive_stuff*, int, int);
+#if 0
+static int mcdx_getstatus(struct s_drive_stuff*, int);
+#endif
+static int mcdx_stop(struct s_drive_stuff*, int);
+static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int);
+static int mcdx_eject(struct s_drive_stuff*, int);
+static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int);
+static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int);
+static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int);
+static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int);
+static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int);
+
+static int mcdx_talk(struct s_drive_stuff*, 
+		const unsigned char* cmd, size_t, void *buffer,
+		size_t size, unsigned int timeout, int);
+static int mcdx_readtoc(struct s_drive_stuff*);
+
+/* static variables ************************************************/
+
+static int dummy0;
+static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
+static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES];
+static struct s_drive_stuff* mcdx_irq_map[16] =
+		{0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0};
+
+static struct file_operations mcdx_fops = {
+	NULL,			/* lseek - use kernel default */
+	block_read,		/* read - general block-dev read */
+	block_write,	/* write - general block-dev write */
+	NULL,			/* no readdir */
+	NULL,			/* no select */
+	mcdx_ioctl,		/* ioctl() */
+	NULL,			/* no mmap */
+	mcdx_open,		/* open() */
+	mcdx_close,		/* close() */
+	NULL,			/* fsync */
+	NULL,                   /* fasync */
+	check_mcdx_media_change, /* media_change */
+	NULL                    /* revalidate */
+};
+
+/* KERNEL INTERFACE FUNCTIONS **************************************/ 
+
+static int 
+mcdx_ioctl(
+	struct inode* ip, struct file* fp, 
+	unsigned int cmd, unsigned long arg)
+{ 
+	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+
+	if (!stuffp->present) return -ENXIO;
+	if (!ip) return -EINVAL;
+
+	switch (cmd) {
+		case CDROMSTART: {
+			TRACE((IOCTL, "ioctl() START\n"));
+			return 0;
+		}
+		case CDROMSTOP: {
+			TRACE((IOCTL, "ioctl() STOP\n"));
+			if (-1 == mcdx_stop(stuffp, 1))
+				return -EIO;
+			return 0;
+		}
+		case CDROMPLAYTRKIND: {
+			return -EINVAL;
+			/*
+			int ans;
+			struct cdrom_ti ti;
+			TRACE((IOCTL, "ioctl() PLAYTRKIND\n"));
+			if (ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti));
+				return ans;
+			memcpy_fromfs(&ti, (void*) arg, sizeof(ti));
+			if ((ti.cdti_trk0 < stuffp->di.n_first)
+					|| (ti.cdti_trk0 > stuffp->di.n_last)
+					|| (ti.cdti_trk1 < stuffp->di.n_first))
+				return -EINVAL;
+			if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last;
+			*/
+		}
+
+		case CDROMREADTOCENTRY: {
+			struct cdrom_tocentry entry;
+			struct s_subqcode *tp = NULL;
+			int ans;
+
+			TRACE((IOCTL, "ioctl() READTOCENTRY\n"));
+
+			if ((stuffp->toc == NULL) && (0 != (ans = mcdx_readtoc(stuffp)))) return ans;
+
+			if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry)))) return ans;
+			memcpy_fromfs(&entry, (void *) arg, sizeof(entry));
+
+			if (entry.cdte_track == CDROM_LEADOUT) 
+				tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1];
+			else if (entry.cdte_track > stuffp->di.n_last 
+					|| entry.cdte_track < stuffp->di.n_first) return -EINVAL;
+			else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first];
+
+			if (NULL == tp) WARN(("FATAL.\n"));
+
+			entry.cdte_adr = tp->control;
+			entry.cdte_ctrl = tp->control >> 4;
+
+			if (entry.cdte_format == CDROM_MSF) {
+				entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute);
+				entry.cdte_addr.msf.second = bcd2uint(tp->dt.second);
+				entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame);
+			} else if (entry.cdte_format == CDROM_LBA)
+				entry.cdte_addr.lba = msf2log(&tp->dt);
+			else return -EINVAL;
+
+			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(entry)))) return ans;
+			memcpy_tofs((void*) arg, &entry, sizeof(entry));
+
+			return 0;
+		}
+
+		case CDROMSUBCHNL: {
+			int ans;
+			struct cdrom_subchnl sub;
+			struct s_subqcode q;
+			TRACE((IOCTL, "ioctl() SUBCHNL\n"));
+
+			if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(sub)))) return ans;
+			memcpy_fromfs(&sub, (void*) arg, sizeof(sub));
+
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) return -EIO;
+
+            TRACE((IOCTL, "audiostatus: %x\n", stuffp->audiostatus));
+			sub.cdsc_audiostatus = stuffp->audiostatus;
+			sub.cdsc_adr = q.control;
+			sub.cdsc_ctrl = q.control >> 4;
+			sub.cdsc_trk = bcd2uint(q.tno);
+			sub.cdsc_ind = bcd2uint(q.index);
+
+			if (sub.cdsc_format == CDROM_LBA) {
+				sub.cdsc_absaddr.lba = msf2log(&q.dt);
+				sub.cdsc_reladdr.lba = msf2log(&q.tt);
+			} else if (sub.cdsc_format == CDROM_MSF) {
+				sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute);
+				sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second);
+				sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
+				sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute);
+				sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second);
+				sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
+			} else return -EINVAL;
+
+			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(sub))))
+				return ans;
+			memcpy_tofs((void*) arg, &sub, sizeof(sub));
+
+			return 0;
+		}
+
+		case CDROMREADTOCHDR: {
+			struct cdrom_tochdr toc;
+			int ans;
+			TRACE((IOCTL, "ioctl() READTOCHDR\n"));
+			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc)))
+				return ans;
+			toc.cdth_trk0 = stuffp->di.n_first;
+			toc.cdth_trk1 = stuffp->di.n_last;
+			memcpy_tofs((void*) arg, &toc, sizeof toc);
+			TRACE((IOCTL, "ioctl() track0 = %d, track1 = %d\n",
+					stuffp->di.n_first, stuffp->di.n_last));
+			return 0;
+		}
+
+		case CDROMPAUSE: {
+			TRACE((IOCTL, "ioctl() PAUSE\n"));
+			if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL;
+			if (-1 == mcdx_stop(stuffp, 1)) return -EIO;
+			if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1))
+				return -EIO;
+			return 0;
+		}
+		case CDROMMULTISESSION: {
+			int ans;
+			struct cdrom_multisession ms;
+			TRACE((IOCTL, "ioctl() MULTISESSION\n"));
+			if (0 != (ans = verify_area(VERIFY_READ, (void*) arg, 
+					sizeof(struct cdrom_multisession))))
+				return ans;
+				
+			memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
+			if (ms.addr_format == CDROM_MSF) {
+				ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute);
+				ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second);
+				ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame);
+			} else if (ms.addr_format == CDROM_LBA)
+				ms.addr.lba = msf2log(&stuffp->multi.msf_last);
+			else
+				return -EINVAL;
+			ms.xa_flag = stuffp->xa;
+
+			if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg,
+					sizeof(struct cdrom_multisession))))
+				return ans;
+
+			memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
+			if (ms.addr_format == CDROM_MSF) 
+				TRACE((IOCTL, 
+						"ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n",
+						ms.xa_flag, 
+						ms.addr.msf.minute,
+						ms.addr.msf.second,
+						ms.addr.msf.frame,
+						stuffp->multi.msf_last.minute,
+						stuffp->multi.msf_last.second,
+						stuffp->multi.msf_last.frame));
+			else
+			  {
+			    dummy0=0;
+			    TRACE((IOCTL, 
+					"ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n",
+					ms.xa_flag,
+					ms.addr.lba,
+					stuffp->multi.msf_last.minute,
+					stuffp->multi.msf_last.second,
+					stuffp->multi.msf_last.frame));
+			  }
+			return 0;
+		}
+
+		case CDROMEJECT: {
+			TRACE((IOCTL, "ioctl() EJECT\n"));
+			if (stuffp->users > 1) return -EBUSY;
+			if (-1 == mcdx_eject(stuffp, 1)) return -EIO;
+			return 0;
+		}
+
+		default:
+			WARN(("ioctl(): unknown request 0x%04x\n", cmd));
+	    	return -EINVAL;
+	}
+}
+
+void do_mcdx_request()
+{
+    int dev;
+    struct s_drive_stuff *stuffp;
+
+  again:
+
+	TRACE((REQUEST, "do_request()\n"));
+
+	if ((CURRENT == NULL) || (CURRENT->dev < 0))  {
+		TRACE((REQUEST, "do_request() done\n"));
+		return;
+	}
+
+    stuffp = mcdx_stuffp[MINOR(CURRENT->dev)];
+	TRACE((REQUEST, "do_request() stuffp = %p\n", stuffp));
+
+    INIT_REQUEST;
+    dev = MINOR(CURRENT->dev);
+
+	if ((dev < 0) || (dev >= MCDX_NDRIVES) || (!stuffp->present)) {
+		printk(MCD "do_request(): bad device: 0x%04x\n", CURRENT->dev);
+		end_request(0);
+		goto again;
+    }
+
+	if (stuffp->audio) {
+		WARN(("do_request() attempt to read from audio cd\n"));
+		end_request(0);
+		goto again;
+	}
+
+    switch (CURRENT->cmd) {
+      case WRITE:
+	  printk(MCD ": do_request(): attempt to write to cd!!\n");
+	  end_request(0);
+	  break;
+
+      case READ:
+	  stuffp->errno = 0;
+	  while (CURRENT->nr_sectors) {
+	      int i;
+
+	      if (-1 == (i = mcdx_transfer(
+				      stuffp,
+				      CURRENT->buffer,
+				      CURRENT->sector,
+				      CURRENT->nr_sectors))) {
+		  printk(MCD " read error\n");
+		  if (stuffp->errno == MCDX_EOM) {
+		      CURRENT->sector += CURRENT->nr_sectors;
+		      CURRENT->nr_sectors = 0;
+		  }
+		  end_request(0);
+		  goto again;
+	      }
+	      CURRENT->sector += i;
+	      CURRENT->nr_sectors -= i;
+	      CURRENT->buffer += (i * 512);
+
+	  }
+
+	  end_request(1);
+	  break;
+
+      default:
+	  panic(MCD "do_request: unknown command.\n");
+	  break;
+    }
+
+    goto again;
+}
+
+static int 
+mcdx_open(struct inode *ip, struct file *fp)
+{
+    struct s_drive_stuff *stuffp;
+	static unsigned long changed = 0;
+
+	TRACE((OPENCLOSE, "open()\n"));
+    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+    if (!stuffp->present) return -ENXIO;
+
+	/* close the door, if necessary (get the door information
+    from the hardware status register) */
+	if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR)  
+		mcdx_closedoor(stuffp, 1);
+
+	/* if the media changed we will have to little more */
+	if (changed < stuffp->changed) {
+
+		TRACE((OPENCLOSE, "open() media changed\n"));
+        /* but wait - the time of media change will be set at the 
+        very last of this block - it seems, some of the following
+        talk() will detect a media change ... (I think, config()
+        is the reason. */
+
+		/* get the multisession information */
+		{
+			TRACE((OPENCLOSE, "open() Request multisession info\n"));
+			if (-1 == mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6))
+				return -EIO;
+		
+			if (stuffp->multi.multi > 2)
+				WARN(("open() unknown multisession value (%d)\n", stuffp->multi.multi));
+
+			/* multisession ? */
+			if (!stuffp->multi.multi)
+				stuffp->multi.msf_last.second = 2;
+
+			TRACE((OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n",
+					stuffp->multi.multi,
+					stuffp->multi.msf_last.minute,
+					stuffp->multi.msf_last.second,
+					stuffp->multi.msf_last.frame));
+		} /* got multisession information */
+
+
+		/* request the disks table of contents (aka diskinfo) */
+		if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) return -EIO;
+
+		stuffp->lastsector = (CD_FRAMESIZE / 512) * msf2log(&stuffp->di.msf_leadout) - 1;
+
+		TRACE((OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n",
+				stuffp->di.n_first,
+				stuffp->di.msf_first.minute,
+				stuffp->di.msf_first.second,
+				stuffp->di.msf_first.frame,
+				msf2log(&stuffp->di.msf_first)));
+		TRACE((OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n",
+				stuffp->di.n_last,
+				stuffp->di.msf_leadout.minute,
+				stuffp->di.msf_leadout.second,
+				stuffp->di.msf_leadout.frame,
+				msf2log(&stuffp->di.msf_leadout)));
+
+		if (stuffp->toc) {
+			TRACE((MALLOC, "open() free toc @ %p\n", stuffp->toc));
+			kfree(stuffp->toc);
+		}
+		stuffp->toc = NULL;
+
+		TRACE((OPENCLOSE, "open() init irq generation\n"));
+		if (-1 == mcdx_config(stuffp, 1)) return -EIO;
+
+		/* try to get the first sector ... */
+		{
+			char buf[512];
+			int ans;
+			int tries;
+
+			stuffp->xa = 0;
+			stuffp->audio = 0;
+
+			for (tries = 6; tries; tries--) {
+				TRACE((OPENCLOSE, "open() try as %s\n",
+					stuffp->xa ? "XA" : "normal"));
+
+				/* set data mode */
+				if (-1 == (ans = mcdx_setdatamode(stuffp, 
+						stuffp->xa ? MODE2 : MODE1, 1)))
+					return -EIO;
+
+				if ((stuffp->audio = e_audio(ans))) break; 
+
+				while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) 
+					;
+
+				if (ans == 1) break;
+				stuffp->xa = !stuffp->xa; 
+			}
+			if (!tries) return -EIO;
+		}
+
+		/* xa disks will be read in raw mode, others not */
+		if (-1 == mcdx_setdrivemode(stuffp, 
+				stuffp->xa ? RAW : COOKED, 1))
+			return -EIO;
+
+		if (stuffp->audio) {
+			WARN(("open() audio disk found\n"));
+		} else {
+			WARN(("open() %s%s disk found\n",
+					stuffp->xa ? "XA / " : "",
+					stuffp->multi.multi ? "Multi Session" : "Single Session"));
+		}
+
+        changed = stuffp->changed;
+	}
+
+	if (-1 == mcdx_lockdoor(stuffp, 1, 1)) return -EIO;
+
+    stuffp->users++;
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static void 
+mcdx_close(struct inode *ip, struct file *fp)
+{
+    struct s_drive_stuff *stuffp;
+
+    TRACE((OPENCLOSE, "close()\n"));
+
+    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+
+    if (0 == --stuffp->users) {
+		sync_dev(ip->i_rdev);	/* needed for r/o device? */
+
+		/* invalidate_inodes(ip->i_rdev); */
+		invalidate_buffers(ip->i_rdev);
+
+		if (-1 == mcdx_lockdoor(stuffp, 0, 1))
+				printk(MCD ": Cannot unlock the door\n");
+    }
+    MOD_DEC_USE_COUNT;
+
+    return;
+}
+
+int check_mcdx_media_change(dev_t full_dev)
+/*	Return: 1 if media changed since last call to 
+			  this function
+			0 else
+	Setting flag to 0 resets the changed state. */
+
+{
+    printk(MCD ":: check_mcdx_media_change(0x%x) called\n", full_dev);
+    return 0;
+}
+
+void mcdx_setup(char *str, int *pi)
+{
+#if MCDX_DEBUG
+    printk(MCD ":: setup(%s, %d) called\n",
+	    str, pi[0]);
+#endif
+}
+
+/* DIRTY PART ******************************************************/ 
+
+static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
+/*	This routine is used for sleeping while initialisation - it seems that
+ 	there are no other means available. May be we could use a simple count
+ 	loop w/ jumps to itself, but I wanna make this independend of cpu
+ 	speed.  */
+{
+    unsigned long tout = jiffies + jifs;
+
+    if (jifs < 0) return;
+
+#ifdef MODULE
+	/*	timer are available */
+    current->timeout = tout;
+    while (current->timeout)
+		interruptible_sleep_on(&stuff->sleepq);
+#else
+	/* timer are _not_ available at initialisation time */
+    if (stuff->present) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = tout;
+		interruptible_sleep_on(&stuff->sleepq);
+    } else
+		while (jiffies < tout) {
+	    current->timeout = jiffies;
+	    schedule();
+	}
+#endif MODULE
+}
+
+static void 
+mcdx_intr(int irq, struct pt_regs* regs)
+{
+    struct s_drive_stuff *stuffp;
+	unsigned char x;
+
+    stuffp = mcdx_irq_map[irq];
+
+    if (!stuffp->busy) {
+		WARN(("intr() unexpected interrupt @ irq %d\n", irq));
+		return;
+    }
+
+	/* if not ok read the next byte as the drives status */
+	if (0 == (stuffp->introk = 
+			(~(x = inb((unsigned int) stuffp->rreg_status)) & MCDX_RBIT_DTEN))) 
+		TRACE((IRQ, "intr() irq %d failed, status %02x %02x\n",
+				irq, x, inb((unsigned int) stuffp->rreg_data)));
+	else
+	  {
+	    dummy0=0;
+	    TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, x));
+	  }
+    stuffp->busy = 0;
+    wake_up_interruptible(&stuffp->busyq);
+}
+
+
+static int 
+mcdx_talk (
+		struct s_drive_stuff *stuffp, 
+		const unsigned char *cmd, size_t cmdlen,
+		void *buffer, size_t size, 
+		unsigned int timeout, int tries)
+/* Send a command to the drive, wait for the result.
+ * returns -1 on timeout, drive status otherwise
+ */
+{
+    const DELAY = 1;				/* minimum delay */
+	char c;
+	int st;
+
+	if (!buffer || size == 0) buffer = &c, size = 1;
+
+    while (stuffp->lock)
+		interruptible_sleep_on(&stuffp->lockq);
+
+    stuffp->lock = 1;
+    stuffp->valid = 0;	
+
+#if MCDX_DEBUG & TALK
+	{ 
+		unsigned char i;
+		TRACE((TALK, "talk() res.size %d, command 0x%02x", 
+				size, (unsigned char) cmd[0]));
+		for (i = 1; i < cmdlen; i++) printk(" 0x%02x", cmd[i]);
+		printk("\n");
+	}
+#endif
+
+	for (st = -1; st == -1 && tries; tries--) {
+
+		int sz = size;
+		unsigned char* bp = buffer;
+
+		outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen);
+
+		while (sz--) {
+			int to = 0;
+
+			/* wait for the status bit */
+			{
+				unsigned long limit = jiffies + timeout;
+				while ((inb((unsigned int) stuffp->rreg_status)) & MCDX_RBIT_STEN) {
+					if ((to = (jiffies > limit))) break;
+					mcdx_delay(stuffp, DELAY);
+				}
+			}
+
+			if (!to) {
+
+				/* status read? */
+				if (st == -1) {
+
+					/* read the status byte */
+					*bp++ = st = (unsigned char) inb((unsigned int) stuffp->rreg_data);
+
+					TRACE((TALK, "talk() got status 0x%02x\n", st));
+
+					/* audio status is handled here */
+					stuffp->audiostatus = e_audiobusy(st) ? 
+							CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS;
+
+					/* media change is handled here ... */
+					if (e_changed(st)) {
+						WARN(("talk() media changed\n"));
+						stuffp->changed = jiffies;
+					}
+
+					/* command error is handled here ... */
+					if (e_cmderr(st)) {
+						WARN(("command error\n"));
+						st = -1;
+						break;
+					}
+				} else {
+					/* read the answer */
+					 *bp++ = (unsigned char) inb((unsigned int) stuffp->rreg_data); 
+				}
+			} else {
+				WARN(("talk() timed out, %1d %s left\n", 
+					tries - 1, (tries -1 ) > 1 ? "tries" : "try"));
+				st = -1;
+				break;
+			}
+		}
+	}
+
+	if (tries) WARN(("talk() giving up\n"));
+
+
+    stuffp->lock = 0;
+    wake_up_interruptible(&stuffp->lockq);
+
+	TRACE((TALK, "talk() done with 0x%02x\n", st));
+    return st;
+}
+
+/* MODULE STUFF ***********************************************************/
+#ifdef MODULE
+
+int init_module(void)
+{
+	int i;
+	int drives = 0;
+
+	mcdx_init(0, 0);
+	for (i = 0; i < MCDX_NDRIVES; i++)  {
+		if (mcdx_stuffp[i]) {
+		TRACE((INIT, "init_module() drive %d stuff @ %p\n",
+				i, mcdx_stuffp[i]));
+			drives++;
+		}
+	}
+
+    if (!drives) {
+		WARN(("init_module() cannot init any drive\n"));
+		return -EIO;
+    }
+
+    return 0;
+}
+
+void cleanup_module(void)
+{
+    int i;
+
+	WARN(("cleanup_module called\n"));
+	
+    for (i = 0; i < MCDX_NDRIVES; i++) {
+		struct s_drive_stuff *stuffp;
+		stuffp = mcdx_stuffp[i];
+		if (!stuffp) continue;
+		release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
+		free_irq(stuffp->irq);
+		if (stuffp->toc) {
+			TRACE((MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc));
+			kfree(stuffp->toc);
+		}
+		TRACE((MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp));
+		mcdx_stuffp[i] = NULL;
+		kfree(stuffp);
+    }
+
+    if (unregister_blkdev(MAJOR_NR, MCD) != 0) 
+		printk(MCD ": cleanup_module failed.\n");
+    else 
+		printk(MCD ": cleanup_module succeeded.\n");
+}
+
+#endif MODULE
+
+/* Support functions ************************************************/
+
+#if MCDX_DEBUG
+void trace(int level, const char* fmt, ...)
+{
+	char s[255];
+	va_list args;
+	if (level < 1) return;
+	va_start(args, fmt);
+	if (sizeof(s) < vsprintf(s, fmt, args))
+		printk(MCD ":: dprintf exeeds limit!!\n");
+	else printk(MCD ":: %s", s);
+	va_end(args);
+}
+#endif
+
+#ifndef NOWARN
+void warn(const char* fmt, ...)
+{
+	char s[255];
+	va_list args;
+	va_start(args, fmt);
+	if (sizeof(s) < vsprintf(s, fmt, args))
+		printk(MCD ":: dprintf exeeds limit!!\n");
+	else printk(MCD ": %s", s);
+	va_end(args);
+}
+#endif
+
+
+unsigned long mcdx_init(unsigned long mem_start, unsigned long mem_end)
+{
+	int drive;
+
+	printk(MCD 
+			": Version @VERSION@ "
+			"$Id: mcdx.c,v 1.2 1995/06/18 18:00:53 heiko Exp $ \n");
+
+	/* zero the pointer array */
+	for (drive = 0; drive < MCDX_NDRIVES; drive++)
+		mcdx_stuffp[drive] = NULL;
+
+	/* do the initialisation */
+	for (drive = 0; drive < MCDX_NDRIVES; drive++) { 
+		struct s_version version;
+		struct s_drive_stuff* stuffp;
+		
+		TRACE((INIT, "init() try drive %d\n", drive));
+		TRACE((MALLOC, "init() malloc %d bytes\n", sizeof(*stuffp)));
+
+		if (!(stuffp = kmalloc(sizeof(*stuffp), GFP_KERNEL))) {
+			WARN(("init() malloc failed\n"));
+			break; 
+		}
+
+		TRACE((INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp));
+
+		/* zero the stuff */
+		memset(stuffp, 0, sizeof(*stuffp));
+
+		stuffp->present = 0;		/* this should be 0 already */
+		stuffp->toc = NULL;			/* this should be NULL already */
+		stuffp->changed = jiffies;
+
+		/* setup our irq and i/o addresses */
+		stuffp->irq = irq(mcdx_drive_map[drive]);
+		stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]);
+		stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
+		stuffp->wreg_hcon = stuffp->wreg_reset + 1;
+		stuffp->wreg_chn = stuffp->wreg_hcon + 1;
+
+		/* check if i/o addresses are available */
+		if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
+			WARN(("init() i/o ports at 0x%3p .. 0x%3p not available\n",
+					stuffp->wreg_data, stuffp->wreg_data + MCDX_IO_SIZE - 1));
+					stuffp->wreg_data = 0;
+			TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
+			kfree(stuffp);
+			TRACE((INIT, "init() continue at next drive\n"));
+			continue; /* next drive */
+		}
+
+		TRACE((INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data));
+
+		TRACE((INIT, "init() hardware reset\n"));
+		mcdx_reset(stuffp, HARD, 1);
+
+		TRACE((INIT, "init() get version\n"));
+		if (-1 == mcdx_requestversion(stuffp, &version, 2)) {
+			/* failed, next drive */
+			TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
+			kfree(stuffp);
+			TRACE((INIT, "init() continue at next drive\n"));
+			continue;
+		}
+		switch (version.code) {
+		case 'D': 
+                stuffp->readcmd = READDSPEED; 
+                stuffp->present = DOUBLE | DOOR | MULTI; 
+                break;
+		case 'F': 
+                stuffp->readcmd = READSSPEED; 
+                stuffp->present = SINGLE | DOOR | MULTI;
+                break;
+		case 'M': 
+                stuffp->readcmd = READSSPEED;
+                stuffp->present = SINGLE;
+                break;
+		default: 
+                stuffp->present = 0; break;
+		}
+
+
+		if (!stuffp->present) {
+			printk(MCD ": no drive found. continue at next drive\n");
+			kfree(stuffp);
+			continue; /* next drive */
+		}
+
+		TRACE((INIT, "init() register blkdev\n"));
+		if (register_blkdev(MAJOR_NR, MCD, &mcdx_fops) != 0) {
+			printk(MCD ": unable to get major %d for " DEVICE_NAME "\n",
+					MAJOR_NR);
+			kfree(stuffp);
+			continue; /* next drive */
+		}
+
+		blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+		read_ahead[MAJOR_NR] = READ_AHEAD;
+
+#if WE_KNOW_WHY
+		 blksize_size[MAJOR_NR] = BLKSIZES;
+#endif
+
+		TRACE((INIT, "init() subscribe irq and i/o\n"));
+		mcdx_irq_map[stuffp->irq] = stuffp;
+		if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, MCD)) {
+			printk(MCD ": init() unable to get IRQ %d for "
+					DEVICE_NAME "\n", stuffp->irq);
+			stuffp->irq = 0;
+			kfree(stuffp);
+			continue;
+		}
+		request_region((unsigned long) (unsigned int) stuffp->wreg_data, MCDX_IO_SIZE, MCD); 
+
+		TRACE((INIT, "init() get garbage\n"));
+		{
+			int i;
+			mcdx_delay(stuffp, 50);
+			for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status);
+		}
+
+
+#if WE_KNOW_WHY
+			outb(0x50, (unsigned int) stuffp->wreg_chn);	/* irq 11 -> channel register */
+#endif
+
+		TRACE((INIT, "init() set non dma but irq mode\n"));
+		mcdx_config(stuffp, 1);
+
+		stuffp->minor = drive;
+
+		printk(MCD ": " DEVICE_NAME " installed at 0x%3p, irq %d.\n"
+			   MCD ": Firmware version %c %x\n",
+			   stuffp->wreg_data, stuffp->irq, version.code, version.ver);
+		mcdx_stuffp[drive] = stuffp;
+		TRACE((INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp));
+	}
+
+	return mem_start;
+}
+
+
+static int mcdx_transfer(struct s_drive_stuff *stuffp,
+		char *p, int sector, int nr_sectors)
+/*	This does actually the transfer from the drive.
+	Return:	-1 on timeout or other error
+			else status byte (as in stuff->st) */
+{
+    int off;
+    int done = 0;
+
+	TRACE((TRANSFER, "transfer() %d sectors at sector %d\n",
+			nr_sectors, sector));
+
+	if (stuffp->audio) {
+		printk(MCD ": attempt to read from audio disk\n");
+		return -1;
+	}
+
+    while (stuffp->lock)
+		interruptible_sleep_on(&stuffp->lockq);
+
+    if (stuffp->valid
+			&& (sector >= stuffp->pending)
+			&& (sector < stuffp->off_direct)) {
+
+
+	off = stuffp->off_requested < (off = sector + nr_sectors)
+			? stuffp->off_requested : off;
+
+	stuffp->lock = current->pid;
+
+	do {
+	    int sig = 0;
+	    int to = 0;
+
+		/* wait for the drive become idle */
+	    current->timeout = jiffies + 500;
+	    while (stuffp->busy) {
+			interruptible_sleep_on(&stuffp->busyq);
+			if ((sig = (current->signal && ~current->blocked))
+					|| (to = (current->timeout == 0))) {
+				break;
+			} 	
+		}
+
+	    current->timeout = 0;
+
+		/* test for possible errors */
+	    if (((stuffp->busy == 0) && !stuffp->introk)
+				|| sig
+				|| to) {
+			if ((stuffp->busy == 0) && !stuffp->introk)
+				printk(MCD ": failure in data request\n");
+			else if (to)
+				printk(MCD ": timeout\n");
+			else if (sig)
+				printk(MCD ": got signal 0x%lx\n", current->signal);
+
+			stuffp->lock = 0;
+			stuffp->busy = 0;
+			wake_up_interruptible(&stuffp->lockq);
+			wake_up_interruptible(&stuffp->busyq);
+			stuffp->errno = MCDX_E;
+			TRACE((TRANSFER, "transfer() done (-1)\n"));
+			return -1;
+	    }
+
+		/* test if it's the first sector of a block,
+		 * there we have to skip some bytes as we read raw data */
+		if (stuffp->xa && (0 == (stuffp->pending & 3))) {
+			const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE;
+			TRACE((TRANSFER, "transfer() sector %d, skip %d header bytes\n",
+				stuffp->pending, HEAD));
+			insb((unsigned int) stuffp->rreg_data, p, HEAD);
+		}
+
+		/* now actually read the data */
+
+		TRACE((TRANSFER, "transfer() read sector %d\n", stuffp->pending));
+	    insb((unsigned int) stuffp->rreg_data, p, 512); 
+
+		/* test if it's the last sector of a block,
+		 * if so, we have to expect an interrupt and to skip some
+		 * data too */
+		if ((stuffp->busy = (3 == (stuffp->pending & 3))) && stuffp->xa) {
+			char dummy[CD_XA_TAIL];
+			TRACE((TRANSFER, "transfer() sector %d, skip %d trailer bytes\n",
+					stuffp->pending, CD_XA_TAIL));
+			insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL);
+		}
+
+	    if (stuffp->pending == sector) {
+			p += 512;
+			done++;
+			sector++;
+	    }
+	}
+	while (++(stuffp->pending) < off);
+
+	stuffp->lock = 0;
+	wake_up_interruptible(&stuffp->lockq);
+
+    } else {
+
+		static unsigned char cmd[] = {
+			0,
+			0, 0, 0,
+			0, 0, 0
+		};
+
+		cmd[0] = stuffp->readcmd;
+
+		stuffp->valid = 1;
+		stuffp->pending = sector & ~3;
+
+		/* do some sanity checks */
+		TRACE((TRANSFER, "transfer() request sector %d\n", stuffp->pending));
+		if (stuffp->pending > stuffp->lastsector) {
+			printk(MCD ": transfer() sector %d from nirvana requested.\n",
+				stuffp->pending);
+			stuffp->errno = MCDX_EOM;
+			TRACE((TRANSFER, "transfer() done (-1)\n"));
+			return -1;
+		}
+
+		if ((stuffp->off_direct = stuffp->pending + DIRECT_SIZE)
+			> stuffp->lastsector + 1)
+			stuffp->off_direct = stuffp->lastsector + 1;
+		if ((stuffp->off_requested = stuffp->pending + REQUEST_SIZE)
+			> stuffp->lastsector + 1)
+			stuffp->off_requested = stuffp->lastsector + 1;
+
+		TRACE((TRANSFER, "transfer() pending %d\n", stuffp->pending));
+		TRACE((TRANSFER, "transfer() off_dir %d\n", stuffp->off_direct));
+		TRACE((TRANSFER, "transfer() off_req %d\n", stuffp->off_requested));
+
+		{
+			struct s_msf pending;
+			log2msf(stuffp->pending / 4, &pending);
+			cmd[1] = pending.minute;
+			cmd[2] = pending.second;
+			cmd[3] = pending.frame;
+		}
+
+		stuffp->busy = 1;
+		cmd[6] = (unsigned char) (stuffp->off_requested - stuffp->pending) / 4;
+
+		outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
+
+    }
+
+    stuffp->off_direct = (stuffp->off_direct += done) < stuffp->off_requested
+	    ? stuffp->off_direct : stuffp->off_requested;
+
+	TRACE((TRANSFER, "transfer() done (%d)\n", done));
+    return done;
+}
+
+
+/*	Access to elements of the mcdx_drive_map members */
+
+static char* port(int *ip) { return (char*) ip[0]; }
+static int irq(int *ip) { return ip[1]; }
+
+/*	Misc number converters */
+
+static unsigned int bcd2uint(unsigned char c)
+{ return (c >> 4) * 10 + (c & 0x0f); }
+
+static unsigned int uint2bcd(unsigned int ival)
+{ return ((ival / 10) << 4) | (ival % 10); }
+
+static void log2msf(unsigned int l, struct s_msf* pmsf)
+{
+    l += CD_BLOCK_OFFSET;
+    pmsf->minute = uint2bcd(l / 4500), l %= 4500;
+    pmsf->second = uint2bcd(l / 75);
+    pmsf->frame = uint2bcd(l % 75);
+}
+
+static unsigned int msf2log(const struct s_msf* pmsf)
+{
+    return bcd2uint(pmsf->frame)
+    + bcd2uint(pmsf->second) * 75
+    + bcd2uint(pmsf->minute) * 4500
+    - CD_BLOCK_OFFSET;
+}
+	
+int mcdx_readtoc(struct s_drive_stuff* stuffp)
+{
+
+	TRACE((IOCTL, "ioctl() readtoc for %d tracks\n",
+			stuffp->di.n_last - stuffp->di.n_first + 1));
+
+#if MCDX_DEBUG && IOCTL
+	if (stuffp->toc) {
+		TRACE((IOCTL, "ioctl() toc already read\n"));
+		return 0;
+	}
+#endif
+
+	TRACE((IOCTL, "ioctl() tocmode\n"));
+	if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO;
+
+	/* all seems to be ok so far ... malloc */
+	{
+		int size;
+		size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2);
+
+		TRACE((MALLOC, "ioctl() malloc %d bytes\n", size));
+		stuffp->toc = kmalloc(size, GFP_KERNEL);
+		if (!stuffp->toc) {
+			WARN(("Cannot malloc %s bytes for toc\n", size));
+			mcdx_setdrivemode(stuffp, DATA, 1);
+			return -EIO;
+		}
+	}
+
+	/* now read actually the index */
+	{
+		int trk;
+		int retries;
+
+		for (trk = 0; 
+				trk < (stuffp->di.n_last - stuffp->di.n_first + 1); 
+				trk++)
+			stuffp->toc[trk].index = 0;
+
+		for (retries = 300; retries; retries--) { /* why 300? */
+			struct s_subqcode q;
+			unsigned int idx;
+		
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) {
+				mcdx_setdrivemode(stuffp, DATA, 1);
+				return -EIO;
+			}
+
+			idx = bcd2uint(q.index);
+
+			if ((idx > 0) 
+					&& (idx <= stuffp->di.n_last) 
+					&& (q.tno == 0)
+					&& (stuffp->toc[idx - stuffp->di.n_first].index == 0)) {
+				stuffp->toc[idx - stuffp->di.n_first] = q;
+				trk--;
+			}
+			if (trk == 0) break;
+		}
+		memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], 
+				0, sizeof(stuffp->toc[0]));
+		stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt
+				= stuffp->di.msf_leadout;
+	}
+
+	/* unset toc mode */
+	TRACE((IOCTL, "ioctl() undo toc mode\n"));
+	if (-1 == mcdx_setdrivemode(stuffp, DATA, 2))
+		return -EIO;
+
+#if MCDX_DEBUG && IOCTL
+	{ int trk;
+	for (trk = 0; 
+			trk < (stuffp->di.n_last - stuffp->di.n_first + 2); 
+			trk++)
+		TRACE((IOCTL, "ioctl() %d readtoc %02x %02x %02x"
+				"  %02x:%02x.%02x  %02x:%02x.%02x\n",
+				trk + stuffp->di.n_first,
+				stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index,
+				stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame,
+				stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame));
+	}
+#endif
+
+
+	return 0;
+}
+
+/* Drive functions ************************************************/
+
+static int 
+mcdx_closedoor(struct s_drive_stuff *stuffp, int tries)
+{
+	if (stuffp->present & DOOR)
+		return mcdx_talk(stuffp, "\xf8", 1, NULL, 0, 500, tries);
+	else
+		return 0;
+}
+
+static int 
+mcdx_stop(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\xf0", 1, NULL, 0, 200, tries); }
+
+static int
+mcdx_eject(struct s_drive_stuff *stuffp, int tries)
+{
+	if (stuffp->present & DOOR)
+		return mcdx_talk(stuffp, "\xf6", 1, NULL, 0, 500, tries);
+	else
+		return 0;
+}
+
+static int
+mcdx_requestsubqcode(struct s_drive_stuff *stuffp, struct s_subqcode *sub, int tries)
+{
+	char buf[11];
+	int ans;
+	ans = mcdx_talk(stuffp, "\x20", 1, buf, sizeof(buf), 200, tries);
+	sub->control = buf[1];
+	sub->tno = buf[2];
+	sub->index = buf[3];
+	sub->tt.minute = buf[4];
+	sub->tt.second = buf[5];
+	sub->tt.frame = buf[6];
+	sub->dt.minute = buf[8];
+	sub->dt.second = buf[9];
+	sub->dt.frame = buf[10];
+	return ans;
+}
+
+static int
+mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries)
+{
+	char buf[5];
+	int ans;
+
+    if (stuffp->present & MULTI) {
+        ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 200, tries);
+        multi->multi = buf[1];
+        multi->msf_last.minute = buf[2];
+        multi->msf_last.second = buf[3];
+        multi->msf_last.frame = buf[4];
+        return ans;
+    } else {
+        multi->multi = 0;
+        return 0;
+    }
+}
+
+static int 
+mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries)
+{
+	char buf[9];
+	int ans;
+	ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 200, tries);
+	info->n_first = bcd2uint(buf[1]);
+	info->n_last = bcd2uint(buf[2]);
+	info->msf_leadout.minute = buf[3];
+	info->msf_leadout.second = buf[4];
+	info->msf_leadout.frame = buf[5];
+	info->msf_first.minute = buf[6];
+	info->msf_first.second = buf[7];
+	info->msf_first.frame = buf[8];
+	return ans;
+}
+
+static int
+mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries)
+{
+	char cmd[2];
+	int ans;
+
+	TRACE((HW, "setdrivemode() %d\n", mode));
+
+	if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 500, tries)))
+		return ans;
+
+	switch (mode) {
+	  case TOC: cmd[1] |= 0x04; break;
+	  case DATA: cmd[1] &= ~0x04; break;
+	  case RAW: cmd[1] |= 0x40; break;
+	  case COOKED: cmd[1] &= ~0x40; break;
+	  default: break;
+	}
+	cmd[0] = 0x50;
+	return mcdx_talk(stuffp, cmd, 2, NULL, 0, 500, tries);
+}
+
+
+static int
+mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries)
+{
+	unsigned char cmd[2] = { 0xa0 };
+	TRACE((HW, "setdatamode() %d\n", mode));
+	switch (mode) {
+	  case MODE0: cmd[1] = 0x00; break;
+	  case MODE1: cmd[1] = 0x01; break;
+	  case MODE2: cmd[1] = 0x02; break;
+	  default: return -EINVAL;
+	}
+	return mcdx_talk(stuffp, cmd, 2, NULL, 0, 500, tries);
+}
+
+static int
+mcdx_config(struct s_drive_stuff *stuffp, int tries)
+{
+	char cmd[4];
+
+	TRACE((HW, "config()\n"));
+
+	cmd[0] = 0x90;
+
+	cmd[1] = 0x10;		/* irq enable */
+	cmd[2] = 0x05;		/* pre, err irq enable */
+
+	if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 0, 100, tries))
+		return -1;
+
+	cmd[1] = 0x02;		/* dma select */
+	cmd[2] = 0x00;		/* no dma */
+
+	return mcdx_talk(stuffp, cmd, 3, NULL, 0, 100, tries);
+}
+
+static int
+mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries)
+{
+	char buf[3];
+	int ans;
+
+	if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 1, buf, sizeof(buf), 200, tries)))
+		return ans;
+
+	ver->code = buf[1];
+	ver->ver = buf[2];
+
+	return ans;
+}
+
+static int
+mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries)
+{ 
+	if (mode == HARD) {
+		outb(0, (unsigned int) stuffp->wreg_chn);		/* no dma, no irq -> hardware */
+		outb(0, (unsigned int) stuffp->wreg_reset);		/* hw reset */
+		return 0;
+	} else return mcdx_talk(stuffp, "\x60", 1, NULL, 0, 500, tries);
+}
+
+static int
+mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries)
+{
+	char cmd[2] = { 0xfe };
+    if (stuffp->present & DOOR) {
+        cmd[1] = lock ? 0x01 : 0x00;
+        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 0, 500, tries);
+    } else 
+        return 0;
+}
+
+#if 0
+static int
+mcdx_getstatus(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\x40", 1, NULL, 0, 500, tries); }
+#endif
diff -u --recursive --new-file v1.3.6/linux/drivers/block/optcd.c linux/drivers/block/optcd.c
--- v1.3.6/linux/drivers/block/optcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/optcd.c	Sat Jul  1 19:05:59 1995
@@ -0,0 +1,1408 @@
+/*	$Id: optcd.c,v 1.7 1995/06/28 20:20:13 root Exp $
+	linux/drivers/block/optcd.c - Optics Storage 8000 AT CDROM driver
+
+	Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
+
+	Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks
+	by Eberhard Moenkeberg.
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2, or (at your option)
+	any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	History
+	14-5-95		v0.0	Plays sound tracks. No reading of data CDs yet.
+				Detection of disk change doesn't work.
+	21-5-95		v0.1	First ALPHA version. CD can be mounted. The
+				device major nr is borrowed from the Aztech
+				driver. Speed is around 240 kb/s, as measured
+				with "time dd if=/dev/cdrom of=/dev/null \
+				bs=2048 count=4096".
+	24-6-95		v0.2	Reworked the #defines for the command codes
+				and the like, as well as the structure of
+				the hardware communication protocol, to
+				reflect the "official" documentation, kindly
+				supplied by C.K. Tan, Optics Storage Pte. Ltd.
+				Also tidied up the state machine somewhat.
+	28-6-95		v0.3	Removed the ISP-16 interface code, as this
+				should go into its own driver. The driver now
+				has its own major nr.
+				Disk change detection now seems to work, too.
+*/
+
+#include <linux/major.h>
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+	char kernel_version[]= UTS_RELEASE;
+# endif
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#define MAJOR_NR OPTICS_CDROM_MAJOR
+
+# include "blk.h"
+#define optcd_port optcd	/* Needed for the modutils. */
+# include <linux/optcd.h>
+
+static short optcd_port = OPTCD_PORTBASE;
+
+/* Read current status/data availability flags */
+inline static int optFlags(void) {
+	return inb(STATUS_PORT) & FL_STDT;
+}
+
+/* Wait for status available; return TRUE on timeout */
+static int sten_low(void) {
+	int no_status;
+	unsigned long count = 0;
+	while ((no_status = (optFlags() & FL_STEN)))
+		if (++count >= BUSY_TIMEOUT)
+			break;
+#ifdef DEBUG_DRIVE_IF
+	if (no_status)
+		printk("optcd: timeout waiting for STEN low\n");
+	else
+		printk("optcd: STEN low after %ld\n", count);
+#endif
+	return no_status;
+}
+
+/* Wait for data available; return TRUE on timeout */
+static int dten_low(void) {
+	int no_data;
+	unsigned long count = 0;
+	while ((no_data = (optFlags() & FL_DTEN)))
+		if (++count >= BUSY_TIMEOUT)
+			break;
+#ifdef DEBUG_DRIVE_IF
+	if (no_data)
+		printk("optcd: timeout waiting for DTEN low\n");
+	else
+		printk("optcd: DTEN low after %ld\n", count);
+#endif
+	return no_data;
+}
+
+/* Facilities for polled waiting for status or data */
+static int sleep_timeout;		/* Max amount of time still to sleep */
+static unsigned char sleep_flags;	/* Flags read last time around */
+static struct wait_queue *waitq = NULL;
+static struct timer_list delay_timer = {NULL, NULL, 0, 0, NULL};
+
+/* Timer routine: wake up when either of FL_STEN or FL_DTEN goes down,
+ * or when timeout expires. Otherwise wait some more.
+ */
+static void sleep_timer(void) {
+	if ((sleep_flags = optFlags()) != FL_STDT) {
+		wake_up(&waitq);
+		return;
+	}
+	if (--sleep_timeout <= 0) {
+		wake_up(&waitq);
+		return;
+	}
+	SET_TIMER(sleep_timer, 1);
+}
+
+/* Sleep until any of FL_STEN or FL_DTEN go down, or until timeout.
+ * sleep_timeout must be set first.
+ */
+static int sleep_status(void) {
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: sleeping %d on status\n", sleep_timeout);
+#endif
+	if (sleep_timeout <= 0)		/* timeout immediately */
+		return FL_STDT;
+	if ((sleep_flags = optFlags()) == FL_STDT) {
+		SET_TIMER(sleep_timer, 1);
+		sleep_on(&waitq);
+	}
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: woken up with %d to go, flags %d\n",
+		sleep_timeout, sleep_flags);
+#endif
+	return sleep_flags;
+}
+
+/* Sleep until status available; return TRUE on timeout */
+inline static int sleep_sten_low(void) {
+	int flags;
+	sleep_timeout = SLEEP_TIMEOUT;
+	flags = sleep_status();
+#ifdef DEBUG_DRIVE_IF
+	if (!(flags & FL_DTEN))
+		printk("optcd: DTEN while waiting for STEN\n");
+#endif
+	return flags & FL_STEN;
+}
+
+/* Sleep until data available; return TRUE on timeout */
+inline static int sleep_dten_low(void) {
+	int flags;
+	sleep_timeout = SLEEP_TIMEOUT;
+	flags = sleep_status();
+#ifdef DEBUG_DRIVE_IF
+	if (!(flags & FL_STEN))
+		printk("optcd: STEN while waiting for DTEN\n");
+#endif
+	return flags & FL_DTEN;
+}
+
+/* Send command code. Return <0 indicates error */
+static int optSendCmd(int cmd) {
+	unsigned char ack;
+#if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
+	printk("optcd: executing command 0x%02x\n", cmd);
+#endif
+	outb(HCON_DTS, HCON_PORT);	/* Enable Suspend Data Transfer */
+	outb(cmd, COMIN_PORT);		/* Send command code */
+	if (sten_low())			/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	ack = inb(DATA_PORT);		/* read command acknowledge */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: acknowledge code 0x%02x\n", ack);
+#endif
+	outb(HCON_SDRQB, HCON_PORT);	/* Disable Suspend Data Transfer */
+	return ack==ST_OP_OK ? 0 : -ack;
+}
+
+/* Send command parameters. Return <0 indicates error */
+static int optSendParams(struct opt_Play_msf *params) {
+	unsigned char ack;
+#if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
+	printk("optcd: params %02x:%02x:%02x %02x:%02x:%02x\n",
+		params->start.min, params->start.sec, params->start.frame,
+		params->end.min, params->end.sec, params->end.frame);
+#endif
+	outb(params -> start.min, COMIN_PORT);
+	outb(params -> start.sec, COMIN_PORT);
+	outb(params -> start.frame, COMIN_PORT);
+	outb(params -> end.min, COMIN_PORT);
+	outb(params -> end.sec, COMIN_PORT);
+	outb(params -> end.frame, COMIN_PORT);
+	if (sten_low())			/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	ack = inb(DATA_PORT);		/* read command acknowledge */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: acknowledge code 0x%02x\n", ack);
+#endif
+	return ack==ST_PA_OK ? 0 : -ack;
+}
+
+/* Return execution status for quick response commands, i.e. busy wait.
+ * Return value <0 indicates timeout.
+ */
+static int optGetExecStatus(void) {
+	unsigned char exec_status;
+	if (sten_low())			/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	exec_status = inb(DATA_PORT);	/* read command execution status */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: returned execution status: 0x%02x\n", exec_status);
+#endif
+	return exec_status;
+}
+
+/* Return execution status for slow commands. Only use when no data is
+ * expected. Return value <0 indicates timeout.
+ */
+static int optSleepTillExecStatus(void) {
+	unsigned char exec_status;
+	if (sleep_sten_low())		/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	exec_status = inb(DATA_PORT);	/* read command execution status */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: returned execution status: 0x%02x\n", exec_status);
+#endif
+	return exec_status;
+}
+
+/* Fetch status that has previously been waited for. <0 means not available */
+inline static int optStatus(void) {
+	unsigned char status;
+	if (optFlags() & FL_STEN)
+		return -ERR_IF_NOSTAT;
+	status = inb(DATA_PORT);
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: read status: 0x%02x\n", status);
+#endif
+	return status;
+}
+
+/* Wait for extra byte of data that a command returns */
+static int optGetData(void) {
+	unsigned char data;
+	if (sten_low())
+		return -ERR_IF_DATA_TIMEOUT;
+	data = inb(DATA_PORT);
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: read data: 0x%02x\n", data);
+#endif
+	return data;
+}
+
+/* Read data that has previously been waited for. */
+inline static void optReadData(char *buf, int n) {
+	insb(DATA_PORT, buf, n);
+}
+
+/* Flush status and data fifos */
+inline static void optFlushData(void) {
+	while (optFlags() != FL_STDT)
+		inb(DATA_PORT);
+}
+
+/* Write something to RESET_PORT and wait. Return TRUE upon success. */
+static int optResetDrive(void) {
+	unsigned long count = 0;
+	int flags;
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: reset drive\n");
+#endif
+	outb(0, RESET_PORT);
+	while (++count < RESET_WAIT)
+		inb(DATA_PORT);
+	count = 0;
+	while ((flags = (inb(STATUS_PORT) & FL_RESET)) != FL_RESET)
+		if (++count >= BUSY_TIMEOUT)
+			break;
+#ifdef DEBUG_DRIVE_IF
+	if (flags == FL_RESET)
+		printk("optcd: drive reset\n");
+	else
+		printk("optcd: reset failed\n");
+#endif
+	if (flags != FL_RESET)
+		return 0;		/* Reset failed */
+	outb(HCON_SDRQB, HCON_PORT);	/* Disable Suspend Data Transfer */
+	return 1;			/* Reset succeeded */
+}
+
+
+/* Command protocol */
+
+/* Send a simple command and wait for response */
+inline static int optCmd(int cmd) {
+	int ack = optSendCmd(cmd);
+	if (ack < 0)
+		return ack;
+	if (cmd < COMFETCH)		/* Quick response command */
+		return optGetExecStatus();
+	else				/* Slow command */
+		return optSleepTillExecStatus();
+}
+
+/* Send a command with parameters and wait for response */
+inline static int optPlayCmd(int cmd, struct opt_Play_msf *params) {
+	int ack = optSendCmd(cmd);
+	if (ack < 0)
+		return ack;
+	if ((ack = optSendParams(params)) < 0)
+		return ack;
+	return optSleepTillExecStatus();
+}
+
+/* Send a command with parameters. Don't wait for the response,
+ * which consists of the data blocks read. */
+inline static int optReadCmd(int cmd, struct opt_Play_msf *params) {
+	int ack = optSendCmd(cmd);
+	if (ack < 0)
+		return ack;
+	return optSendParams(params);
+}
+
+
+/* Address conversion routines */
+
+/* Binary to BCD (2 digits) */
+inline static unsigned char bin2bcd(unsigned char p) {
+#ifdef DEBUG_CONV
+	if (p > 99)
+		printk("optcd: error bin2bcd %d\n", p);
+#endif
+	return (p % 10) | ((p / 10) << 4);
+}
+
+/* Linear address to minute, second, frame form */
+static void hsg2msf(long hsg, struct msf *msf) {
+	hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+#ifdef DEBUG_CONV
+	if (msf -> min >= 70)
+		printk("optcd: Error hsg2msf address Minutes\n");
+	if (msf -> sec >= 60)
+		printk("optcd: Error hsg2msf address Seconds\n");
+	if (msf -> frame >= 75)
+		printk("optcd: Error hsg2msf address Frames\n");
+#endif
+	msf -> min = bin2bcd(msf -> min);	/* convert to BCD */
+	msf -> sec = bin2bcd(msf -> sec);
+	msf -> frame = bin2bcd(msf -> frame);
+}
+
+/* Two BCD digits to binary */
+inline static int bcd2bin(unsigned char bcd) {
+	return (bcd >> 4) * 10 + (bcd & 0x0f);
+}
+
+/* Minute, second, frame address to linear address */
+static long msf2hsg(struct msf *mp) {
+#ifdef DEBUG_CONV
+	if (mp -> min >= 70)
+		printk("optcd: Error msf2hsg address Minutes\n");
+	if (mp -> sec >= 60)
+		printk("optcd: Error msf2hsg address Seconds\n");
+	if (mp -> frame >= 75)
+		printk("optcd: Error msf2hsg address Frames\n");
+#endif
+	return bcd2bin(mp -> frame)
+		+ bcd2bin(mp -> sec) * 75
+		+ bcd2bin(mp -> min) * 4500
+		- 150;
+}
+
+
+/* Drive status and table of contents */
+
+static int optAudioStatus = CDROM_AUDIO_NO_STATUS;
+static char optDiskChanged = 1;
+static char optTocUpToDate = 0;
+static struct opt_DiskInfo DiskInfo;
+static struct opt_Toc Toc[MAX_TRACKS];
+
+/* Get CDROM status, flagging completion of audio play and disk changes. */
+static int optGetStatus(void) {
+	int st;
+	if ((st = optCmd(COMIOCTLISTAT)) < 0)
+		return st;
+	if (st == 0xff)
+		return -ERR_IF_NOSTAT;
+	if (((st & ST_MODE_BITS) != ST_M_AUDIO) &&
+		(optAudioStatus == CDROM_AUDIO_PLAY)) {
+		optAudioStatus = CDROM_AUDIO_COMPLETED;
+	}
+	if (st & ST_DSK_CHG) {
+		optDiskChanged = 1;
+		optTocUpToDate = 0;
+		optAudioStatus = CDROM_AUDIO_NO_STATUS;
+	}
+	return st;
+}
+
+/*
+ * Read the current Q-channel info. Also used for reading the
+ * table of contents.
+ */
+static int optGetQChannelInfo(struct opt_Toc *qp) {
+	int st;
+#ifdef DEBUG_TOC
+	printk("optcd: starting optGetQChannelInfo\n");
+#endif
+	if ((st = optGetStatus()) < 0)
+		return st;
+	if ((st = optCmd(COMSUBQ)) < 0)
+		return st;
+	if ((qp -> ctrl_addr = st = optGetData()), st < 0) return st;
+	if ((qp -> track = st = optGetData()), st < 0) return st;
+	if ((qp -> pointIndex = st = optGetData()), st < 0) return st;
+	if ((qp -> trackTime.min = st = optGetData()), st < 0) return st;
+	if ((qp -> trackTime.sec = st = optGetData()), st < 0) return st;
+	if ((qp -> trackTime.frame = st = optGetData()), st < 0) return st;
+	if ((st = optGetData()) < 0) return st;		/* byte not used */
+	if ((qp -> diskTime.min = st = optGetData()), st < 0) return st;
+	if ((qp -> diskTime.sec = st = optGetData()), st < 0) return st;
+	if ((qp -> diskTime.frame = st = optGetData()), st < 0) return st;
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optGetQChannelInfo\n");
+#endif
+	return 0;
+}
+
+#define QINFO_FIRSTTRACK	0xa0
+#define QINFO_LASTTRACK		0xa1
+#define QINFO_DISKLENGTH	0xa2
+
+static int optGetDiskInfo(void) {
+	int st, limit;
+	unsigned char test = 0;
+	struct opt_Toc qInfo;
+#ifdef DEBUG_TOC
+	printk("optcd: starting optGetDiskInfo\n");
+#endif
+	optDiskChanged = 0;
+	if ((st = optCmd(COMLEADIN)) < 0)
+		return st;
+	for (limit = 300; (limit > 0) && (test != 0x0f); limit--) {
+		if ((st = optGetQChannelInfo(&qInfo)) < 0)
+			return st;
+		switch (qInfo.pointIndex) {
+		case QINFO_FIRSTTRACK:
+			DiskInfo.first = bcd2bin(qInfo.diskTime.min);
+#ifdef DEBUG_TOC
+			printk("optcd: got first: %d\n", DiskInfo.first);
+#endif
+			test |= 0x01;
+			break;
+		case QINFO_LASTTRACK:
+			DiskInfo.last = bcd2bin(qInfo.diskTime.min);
+#ifdef DEBUG_TOC
+			printk("optcd: got last: %d\n", DiskInfo.last);
+#endif
+			test |= 0x02;
+			break;
+		case QINFO_DISKLENGTH:
+			DiskInfo.diskLength.min = qInfo.diskTime.min;
+			DiskInfo.diskLength.sec = qInfo.diskTime.sec-2;
+			DiskInfo.diskLength.frame = qInfo.diskTime.frame;
+#ifdef DEBUG_TOC
+			printk("optcd: got length: %x:%x.%x\n",
+				DiskInfo.diskLength.min,
+				DiskInfo.diskLength.sec,
+				DiskInfo.diskLength.frame);
+#endif
+			test |= 0x04;
+			break;
+		default:
+			if ((test & 0x01)	/* Got no of first track */
+			 && (qInfo.pointIndex == DiskInfo.first)) {
+				/* StartTime of First Track */
+				DiskInfo.firstTrack.min = qInfo.diskTime.min;
+				DiskInfo.firstTrack.sec = qInfo.diskTime.sec;
+				DiskInfo.firstTrack.frame = qInfo.diskTime.frame;
+#ifdef DEBUG_TOC
+			printk("optcd: got start: %x:%x.%x\n",
+				DiskInfo.firstTrack.min,
+				DiskInfo.firstTrack.sec,
+				DiskInfo.firstTrack.frame);
+#endif
+				test |= 0x08;
+			}
+		}
+	}
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optGetDiskInfo\n");
+#endif
+	if (test != 0x0f)
+		return -ERR_TOC_MISSINGINFO;
+	return 0;
+}
+
+static int optGetToc(void) {	/* Presumes we have got DiskInfo */
+	int st, count, px, limit;
+	struct opt_Toc qInfo;
+#ifdef DEBUG_TOC
+	int i;
+	printk("optcd: starting optGetToc\n");
+#endif
+	for (count = 0; count < MAX_TRACKS; count++)
+		Toc[count].pointIndex = 0;
+	if ((st = optCmd(COMLEADIN)) < 0)
+		return st;
+	st = 0;
+	count = DiskInfo.last + 3;
+	for (limit = 300; (limit > 0) && (count > 0); limit--) {
+		if ((st = optGetQChannelInfo(&qInfo)) < 0)
+			break;
+		px = bcd2bin(qInfo.pointIndex);
+		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+			if (Toc[px].pointIndex == 0) {
+				Toc[px] = qInfo;
+				count--;
+			}
+	}
+	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optGetToc\n");
+	for (i = 1; i <= DiskInfo.last + 1; i++)
+		printk("i = %3d ctl-adr = %02x track %2d px "
+			"%02x %02x:%02x.%02x %02x:%02x.%02x\n",
+			i, Toc[i].ctrl_addr,
+			Toc[i].track,
+			Toc[i].pointIndex,
+			Toc[i].trackTime.min,
+			Toc[i].trackTime.sec,
+			Toc[i].trackTime.frame,
+			Toc[i].diskTime.min,
+			Toc[i].diskTime.sec,
+			Toc[i].diskTime.frame);
+	for (i = 100; i < 103; i++)
+		printk("i = %3d ctl-adr = %02x track %2d px "
+			"%02x %02x:%02x.%02x %02x:%02x.%02x\n",
+			i, Toc[i].ctrl_addr,
+			Toc[i].track,
+			Toc[i].pointIndex,
+			Toc[i].trackTime.min,
+			Toc[i].trackTime.sec,
+			Toc[i].trackTime.frame,
+			Toc[i].diskTime.min,
+			Toc[i].diskTime.sec,
+			Toc[i].diskTime.frame);
+#endif
+	return count ? -ERR_TOC_MISSINGENTRY : 0;
+}
+
+static int optUpdateToc(void) {
+#ifdef DEBUG_TOC
+	printk("optcd: starting optUpdateToc\n");
+#endif
+	if (optTocUpToDate)
+		return 0;
+	if (optGetDiskInfo() < 0)
+		return -EIO;
+	if (optGetToc() < 0)
+		return -EIO;
+	optTocUpToDate = 1;
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optUpdateToc\n");
+#endif
+	return 0;
+}
+
+
+/* Buffers */
+
+#define OPT_BUF_SIZ		16
+#define OPT_BLOCKSIZE		2048
+#define OPT_BLOCKSIZE_RAW	2336
+#define OPT_BLOCKSIZE_ALL	2646
+#define OPT_NOBUF		-1
+
+/* Buffer for block size conversion. */
+static char opt_buf[OPT_BLOCKSIZE*OPT_BUF_SIZ];
+static volatile int opt_buf_bn[OPT_BUF_SIZ], opt_next_bn;
+static volatile int opt_buf_in = 0, opt_buf_out = OPT_NOBUF;
+
+inline static void opt_invalidate_buffers(void) {
+	int i;
+#ifdef DEBUG_BUFFERS
+	printk("optcd: executing opt_invalidate_buffers\n");
+#endif
+	for (i = 0; i < OPT_BUF_SIZ; i++)
+		opt_buf_bn[i] = OPT_NOBUF;
+	opt_buf_out = OPT_NOBUF;
+}
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+static void opt_transfer(void) {
+#if (defined DEBUG_BUFFERS) || (defined DEBUG_REQUEST)
+	printk("optcd: executing opt_transfer\n");
+#endif
+	if (!CURRENT_VALID)
+		return;
+	while (CURRENT -> nr_sectors) {
+		int bn = CURRENT -> sector / 4;
+		int i, offs, nr_sectors;
+		for (i = 0; i < OPT_BUF_SIZ && opt_buf_bn[i] != bn; ++i);
+#ifdef DEBUG_REQUEST
+		printk("optcd: found %d\n", i);
+#endif
+		if (i >= OPT_BUF_SIZ) {
+			opt_buf_out = OPT_NOBUF;
+			break;
+		}
+		offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+		nr_sectors = 4 - (CURRENT -> sector & 3);
+		if (opt_buf_out != i) {
+			opt_buf_out = i;
+			if (opt_buf_bn[i] != bn) {
+				opt_buf_out = OPT_NOBUF;
+				continue;
+			}
+		}
+		if (nr_sectors > CURRENT -> nr_sectors)
+			nr_sectors = CURRENT -> nr_sectors;
+		memcpy(CURRENT -> buffer, opt_buf + offs, nr_sectors * 512);
+		CURRENT -> nr_sectors -= nr_sectors;
+		CURRENT -> sector += nr_sectors;
+		CURRENT -> buffer += nr_sectors * 512;
+	}
+}
+
+
+/* State machine for reading disk blocks */
+
+enum opt_state_e {
+	OPT_S_IDLE,	/* 0 */
+	OPT_S_START,	/* 1 */
+	OPT_S_READ,	/* 2 */
+	OPT_S_DATA,	/* 3 */
+	OPT_S_STOP,	/* 4 */
+	OPT_S_STOPPING	/* 5 */
+};
+
+static volatile enum opt_state_e opt_state = OPT_S_IDLE;
+#ifdef DEBUG_STATE
+static volatile enum opt_state_e opt_state_old = OPT_S_STOP;
+static volatile int opt_st_old = 0;
+static volatile long opt_state_n = 0;
+#endif
+
+static volatile int opt_transfer_is_active = 0;
+static volatile int opt_error = 0;	/* do something with this?? */
+static int optTries;			/* ibid?? */
+
+static void opt_poll(void) {
+	static int optTimeout;
+	static volatile int opt_read_count = 1;
+	int st = 0;
+	int loop_ctl = 1;
+	int skip = 0;
+
+	if (opt_error) {
+		printk("optcd: I/O error 0x%02x\n", opt_error);
+		opt_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+		if (optTries == 5)
+			printk("optcd: read block %d failed; audio disk?\n",
+			        opt_next_bn);
+#endif
+		if (!optTries--) {
+			printk("optcd: read block %d failed; Giving up\n",
+			       opt_next_bn);
+			if (opt_transfer_is_active) {
+				optTries = 0;
+				loop_ctl = 0;
+			}
+			if (CURRENT_VALID)
+				end_request(0);
+			optTries = 5;
+		}
+		opt_error = 0;
+		opt_state = OPT_S_STOP;
+	}
+
+	while (loop_ctl)
+	{
+		loop_ctl = 0; /* each case must flip this back to 1 if we want
+		                 to come back up here */
+#ifdef DEBUG_STATE
+		if (opt_state == opt_state_old)
+			opt_state_n++;
+		else {
+			opt_state_old = opt_state;
+			if (++opt_state_n > 1)
+				printk("optcd: %ld times in previous state\n",
+					opt_state_n);
+			printk("optcd: state %d\n", opt_state);
+			opt_state_n = 0;
+		}
+#endif
+		switch (opt_state) {
+		case OPT_S_IDLE:
+			return;
+		case OPT_S_START:
+			if (optSendCmd(COMDRVST))
+				return;
+			opt_state = OPT_S_READ;
+			optTimeout = 3000;
+			break;
+		case OPT_S_READ: {
+			struct opt_Play_msf msf;
+			if (!skip) {
+				if ((st = optStatus()) < 0)
+					break;
+				if (st & ST_DSK_CHG) {
+					optDiskChanged = 1;
+					optTocUpToDate = 0;
+					opt_invalidate_buffers();
+				}
+			}
+			skip = 0;
+			if ((st & ST_DOOR_OPEN) || (st & ST_DRVERR)) {
+				optDiskChanged = 1;
+				optTocUpToDate = 0;
+				printk((st & ST_DOOR_OPEN)
+				       ? "optcd: door open\n"
+				       : "optcd: disk removed\n");
+				if (opt_transfer_is_active) {
+					opt_state = OPT_S_START;
+					loop_ctl = 1;
+					break;
+				}
+				opt_state = OPT_S_IDLE;
+				while (CURRENT_VALID)
+					end_request(0);
+				return;
+			}
+			if (!CURRENT_VALID) {
+				opt_state = OPT_S_STOP;
+				loop_ctl = 1;
+				break;
+			}
+			opt_next_bn = CURRENT -> sector / 4;
+			hsg2msf(opt_next_bn, &msf.start);
+			opt_read_count = OPT_BUF_SIZ;
+			msf.end.min = 0;
+			msf.end.sec = 0;
+			msf.end.frame = opt_read_count;
+#ifdef DEBUG_REQUEST
+			printk("optcd: reading %x:%x.%x %x:%x.%x\n",
+				msf.start.min,
+				msf.start.sec,
+				msf.start.frame,
+				msf.end.min,
+				msf.end.sec,
+				msf.end.frame);
+			printk("optcd: opt_next_bn:%d opt_buf_in:%d opt_buf_out:%d opt_buf_bn:%d\n",
+				opt_next_bn,
+				opt_buf_in,
+				opt_buf_out,
+				opt_buf_bn[opt_buf_in]);
+#endif
+			optReadCmd(COMREAD, &msf);
+			opt_state = OPT_S_DATA;
+			optTimeout = READ_TIMEOUT;
+			break;
+		}
+		case OPT_S_DATA:
+			st = optFlags() & (FL_STEN|FL_DTEN);
+#ifdef DEBUG_STATE
+			if (st != opt_st_old) {
+				opt_st_old = st;
+				printk("optcd: st:%x\n", st);
+			}
+			if (st == FL_STEN)
+				printk("timeout cnt: %d\n", optTimeout);
+#endif
+			switch (st) {
+			case FL_DTEN:
+#ifdef WARN_IF_READ_FAILURE
+				if (optTries == 5)
+					printk("optcd: read block %d failed; audio disk?\n",
+					       opt_next_bn);
+#endif
+				if (!optTries--) {
+					printk("optcd: read block %d failed; Giving up\n",
+					       opt_next_bn);
+					if (opt_transfer_is_active) {
+						optTries = 0;
+						break;
+					}
+					if (CURRENT_VALID)
+						end_request(0);
+					optTries = 5;
+				}
+				opt_state = OPT_S_START;
+				optTimeout = READ_TIMEOUT;
+				loop_ctl = 1;
+			case (FL_STEN|FL_DTEN):
+				break;
+			default:
+				optTries = 5;
+				if (!CURRENT_VALID && opt_buf_in == opt_buf_out) {
+					opt_state = OPT_S_STOP;
+					loop_ctl = 1;
+					break;
+				}
+				if (opt_read_count<=0)
+					printk("optcd: warning - try to read 0 frames\n");
+				while (opt_read_count) {
+					opt_buf_bn[opt_buf_in] = OPT_NOBUF;
+					if (dten_low()) { /* should be no waiting here!?? */
+						printk("read_count:%d CURRENT->nr_sectors:%ld opt_buf_in:%d\n",
+							opt_read_count,
+							CURRENT->nr_sectors,
+							opt_buf_in);
+						printk("opt_transfer_is_active:%x\n",
+							opt_transfer_is_active);
+						opt_read_count = 0;
+						opt_state = OPT_S_STOP;
+						loop_ctl = 1;
+						end_request(0);
+						break;
+					}
+					optReadData(opt_buf+OPT_BLOCKSIZE*opt_buf_in, OPT_BLOCKSIZE);
+					opt_read_count--;
+#ifdef DEBUG_REQUEST
+					printk("OPT_S_DATA; ---I've read data- read_count: %d\n",
+					       opt_read_count);
+					printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
+					       opt_next_bn,
+					       opt_buf_in,
+					       opt_buf_out,
+					       opt_buf_bn[opt_buf_in]);
+#endif
+					opt_buf_bn[opt_buf_in] = opt_next_bn++;
+					if (opt_buf_out == OPT_NOBUF)
+						opt_buf_out = opt_buf_in;
+					opt_buf_in = opt_buf_in + 1 ==
+						OPT_BUF_SIZ ? 0 : opt_buf_in + 1;
+				}
+				if (!opt_transfer_is_active) {
+					while (CURRENT_VALID) {
+						opt_transfer();
+						if (CURRENT -> nr_sectors == 0)
+							end_request(1);
+						else
+							break;
+					}
+				}
+
+				if (CURRENT_VALID
+				    && (CURRENT -> sector / 4 < opt_next_bn ||
+				    CURRENT -> sector / 4 >
+				     opt_next_bn + OPT_BUF_SIZ)) {
+					opt_state = OPT_S_STOP;
+					loop_ctl = 1;
+					break;
+				}
+				optTimeout = READ_TIMEOUT;
+				if (opt_read_count == 0) {
+					opt_state = OPT_S_STOP;
+					loop_ctl = 1;
+					break;
+				}
+			}
+			break;
+		case OPT_S_STOP:
+			if (opt_read_count != 0)
+				printk("optcd: discard data=%x frames\n",
+					opt_read_count);
+			while (opt_read_count != 0) {
+				optFlushData();
+				opt_read_count--;
+			}
+			if (optSendCmd(COMDRVST))
+				return;
+			opt_state = OPT_S_STOPPING;
+			optTimeout = 1000;
+			break;
+		case OPT_S_STOPPING:
+			if ((st = optStatus()) < 0 && optTimeout)
+					break;
+			if ((st != -1) && (st & ST_DSK_CHG)) {
+				optDiskChanged = 1;
+				optTocUpToDate = 0;
+				opt_invalidate_buffers();
+			}
+			if (CURRENT_VALID) {
+				if (st != -1) {
+					opt_state = OPT_S_READ;
+					loop_ctl = 1;
+					skip = 1;
+					break;
+				} else {
+					opt_state = OPT_S_START;
+					optTimeout = 1;
+				}
+			} else {
+				opt_state = OPT_S_IDLE;
+				return;
+			}
+			break;
+		default:
+			printk("optcd: invalid state %d\n", opt_state);
+			return;
+		} /* case */
+	} /* while */
+
+	if (!optTimeout--) {
+		printk("optcd: timeout in state %d\n", opt_state);
+		opt_state = OPT_S_STOP;
+		if (optCmd(COMSTOP) < 0)
+			return;
+	}
+
+	SET_TIMER(opt_poll, 1);
+}
+
+
+static void do_optcd_request(void) {
+#ifdef DEBUG_REQUEST
+	printk("optcd: do_optcd_request(%ld+%ld)\n",
+	       CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+	opt_transfer_is_active = 1;
+	while (CURRENT_VALID) {
+		if (CURRENT->bh) {
+			if (!CURRENT->bh->b_lock)
+				panic(DEVICE_NAME ": block not locked");
+		}
+		opt_transfer();	/* First try to transfer block from buffers */
+		if (CURRENT -> nr_sectors == 0) {
+			end_request(1);
+		} else {	/* Want to read a block not in buffer */
+			opt_buf_out = OPT_NOBUF;
+			if (opt_state == OPT_S_IDLE) {
+				/* Should this block the request queue?? */
+				if (optUpdateToc() < 0) {
+					while (CURRENT_VALID)
+						end_request(0);
+					break;
+				}
+				/* Start state machine */
+				opt_state = OPT_S_START;
+				optTries = 5;
+				SET_TIMER(opt_poll, 1);	/* why not start right away?? */
+			}
+			break;
+		}
+	}
+	opt_transfer_is_active = 0;
+#ifdef DEBUG_REQUEST
+	printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
+	       opt_next_bn, opt_buf_in, opt_buf_out, opt_buf_bn[opt_buf_in]);
+	printk("optcd: do_optcd_request ends\n");
+#endif
+}
+
+
+/* VFS calls */
+
+static int opt_ioctl(struct inode *ip, struct file *fp,
+			unsigned int cmd, unsigned long arg) {
+	static struct opt_Play_msf opt_Play;	/* pause position */
+	int err;
+#ifdef DEBUG_VFS
+	printk("optcd: starting opt_ioctl, command 0x%x\n", cmd);
+#endif
+	if (!ip)
+		return -EINVAL;
+	if (optGetStatus() < 0)
+		return -EIO;
+	if ((err = optUpdateToc()) < 0)
+		return err;
+
+	switch (cmd) {
+	case CDROMPAUSE: {
+		struct opt_Toc qInfo;
+
+		if (optAudioStatus != CDROM_AUDIO_PLAY)
+			return -EINVAL;
+		if (optGetQChannelInfo(&qInfo) < 0) {
+			/* didn't get q channel info */
+			optAudioStatus = CDROM_AUDIO_NO_STATUS;
+			return 0;
+		}
+		opt_Play.start = qInfo.diskTime;	/* restart point */
+		if (optCmd(COMPAUSEON) < 0)
+			return -EIO;
+		optAudioStatus = CDROM_AUDIO_PAUSED;
+		break;
+	}
+	case CDROMRESUME:
+		if (optAudioStatus != CDROM_AUDIO_PAUSED)
+			return -EINVAL;
+		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
+			optAudioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+		optAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMPLAYMSF: {
+		int st;
+		struct cdrom_msf msf;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
+			return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
+		opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
+		opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
+		opt_Play.end.min = bin2bcd(msf.cdmsf_min1);
+		opt_Play.end.sec = bin2bcd(msf.cdmsf_sec1);
+		opt_Play.end.frame = bin2bcd(msf.cdmsf_frame1);
+		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
+			optAudioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+		optAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	}
+	case CDROMPLAYTRKIND: {
+		int st;
+		struct cdrom_ti ti;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof ti)))
+			return st;
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+		if (ti.cdti_trk0 < DiskInfo.first
+			|| ti.cdti_trk0 > DiskInfo.last
+			|| ti.cdti_trk1 < ti.cdti_trk0)
+			return -EINVAL;
+		if (ti.cdti_trk1 > DiskInfo.last)
+			ti.cdti_trk1 = DiskInfo.last;
+		opt_Play.start = Toc[ti.cdti_trk0].diskTime;
+		opt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+#ifdef DEBUG_VFS
+		printk("optcd: play %02x:%02x.%02x to %02x:%02x.%02x\n",
+			opt_Play.start.min,
+			opt_Play.start.sec,
+			opt_Play.start.frame,
+			opt_Play.end.min,
+			opt_Play.end.sec,
+			opt_Play.end.frame);
+#endif
+		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
+			optAudioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+		optAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	}
+	case CDROMREADTOCHDR: {		/* Read the table of contents header. */
+		int st;
+		struct cdrom_tochdr tocHdr;
+
+		if ((st = verify_area(VERIFY_WRITE,(void *)arg,sizeof tocHdr)))
+			return st;
+		if (!optTocUpToDate)
+			optGetDiskInfo();
+		tocHdr.cdth_trk0 = DiskInfo.first;
+		tocHdr.cdth_trk1 = DiskInfo.last;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		break;
+	}
+	case CDROMREADTOCENTRY: {	/* Read a table of contents entry. */
+		int st;
+		struct cdrom_tocentry entry;
+		struct opt_Toc *tocPtr;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof entry)))
+			return st;
+		if ((st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry)))
+			return st;
+		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+		if (!optTocUpToDate)
+			optGetDiskInfo();
+		if (entry.cdte_track == CDROM_LEADOUT)
+			tocPtr = &Toc[DiskInfo.last + 1];
+		else if (entry.cdte_track > DiskInfo.last
+			|| entry.cdte_track < DiskInfo.first)
+			return -EINVAL;
+		else
+			tocPtr = &Toc[entry.cdte_track];
+		entry.cdte_adr = tocPtr -> ctrl_addr;
+		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+		switch (entry.cdte_format) {
+		case CDROM_LBA:
+			entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
+			break;
+		case CDROM_MSF:
+			entry.cdte_addr.msf.minute =
+				bcd2bin(tocPtr -> diskTime.min);
+			entry.cdte_addr.msf.second =
+				bcd2bin(tocPtr -> diskTime.sec);
+			entry.cdte_addr.msf.frame =
+				bcd2bin(tocPtr -> diskTime.frame);
+			break;
+		default:
+			return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &entry, sizeof entry);
+		break;
+	}
+	case CDROMSTOP:
+		optCmd(COMSTOP);
+		optAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMSTART:
+		optCmd(COMCLOSE);	/* What else can we do? */
+		break;
+	case CDROMEJECT:
+		optCmd(COMUNLOCK);
+		optCmd(COMOPEN);
+		break;
+	case CDROMVOLCTRL: {
+		int st;
+		struct cdrom_volctrl volctrl;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg,
+				sizeof(volctrl))))
+			return st;
+		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+		opt_Play.start.min = 0x10;
+		opt_Play.start.sec = 0x32;
+		opt_Play.start.frame = volctrl.channel0;
+		opt_Play.end.min = volctrl.channel1;
+		opt_Play.end.sec = volctrl.channel2;
+		opt_Play.end.frame = volctrl.channel3;
+		if (optPlayCmd(COMCHCTRL, &opt_Play) < 0)
+			return -EIO;
+		break;
+	}
+	case CDROMSUBCHNL: {	/* Get subchannel info */
+		int st;
+		struct cdrom_subchnl subchnl;
+		struct opt_Toc qInfo;
+
+		if ((st = verify_area(VERIFY_READ,
+				(void *) arg, sizeof subchnl)))
+			return st;
+		if ((st = verify_area(VERIFY_WRITE,
+				(void *) arg, sizeof subchnl)))
+			return st;
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+		if (optGetQChannelInfo(&qInfo) < 0)
+			return -EIO;
+		subchnl.cdsc_audiostatus = optAudioStatus;
+		subchnl.cdsc_adr = qInfo.ctrl_addr;
+		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+		subchnl.cdsc_trk = bcd2bin(qInfo.track);
+		subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);
+		switch (subchnl.cdsc_format) {
+		case CDROM_LBA:
+			subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
+			subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
+			break;
+		case CDROM_MSF:
+			subchnl.cdsc_absaddr.msf.minute =
+				bcd2bin(qInfo.diskTime.min);
+			subchnl.cdsc_absaddr.msf.second =
+				bcd2bin(qInfo.diskTime.sec);
+			subchnl.cdsc_absaddr.msf.frame =
+				bcd2bin(qInfo.diskTime.frame);
+			subchnl.cdsc_reladdr.msf.minute =
+				bcd2bin(qInfo.trackTime.min);
+			subchnl.cdsc_reladdr.msf.second =
+				bcd2bin(qInfo.trackTime.sec);
+			subchnl.cdsc_reladdr.msf.frame =
+				bcd2bin(qInfo.trackTime.frame);
+			break;
+		default:
+			return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+		break;
+	}
+	case CDROMREADMODE1: {
+		int st;
+		struct cdrom_msf msf;
+		char buf[OPT_BLOCKSIZE];
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
+			return st;
+		if ((st = verify_area(VERIFY_WRITE,(void *)arg,OPT_BLOCKSIZE)))
+			return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
+		opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
+		opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
+		opt_Play.end.min = 0;
+		opt_Play.end.sec = 0;
+		opt_Play.end.frame = 1;	/* read only one frame */
+		st = optReadCmd(COMREAD, &opt_Play);
+#ifdef DEBUG_VFS
+		printk("optcd: COMREAD status 0x%x\n", st);
+#endif
+		sleep_dten_low();	/* error checking here?? */
+		optReadData(buf, OPT_BLOCKSIZE);
+		memcpy_tofs((void *) arg, &buf, OPT_BLOCKSIZE);
+		break;
+	}
+	case CDROMMULTISESSION:
+		return -EINVAL; /* unluckily, not implemented yet */
+
+	default:
+		return -EINVAL;
+	}
+#ifdef DEBUG_VFS
+	printk("optcd: exiting opt_ioctl\n");
+#endif
+	return 0;
+}
+
+static int optPresent = 0;
+static int opt_open_count = 0;
+
+/* Open device special file; check that a disk is in. */
+static int opt_open(struct inode *ip, struct file *fp) {
+#ifdef DEBUG_VFS
+	printk("optcd: starting opt_open\n");
+#endif
+	if (!optPresent)
+		return -ENXIO;		/* no hardware */
+	if (!opt_open_count && opt_state == OPT_S_IDLE) {
+		int st;
+		opt_invalidate_buffers();
+		if ((st = optGetStatus()) < 0)
+			return -EIO;
+		if (st & ST_DOOR_OPEN) {
+			optCmd(COMCLOSE);			/* close door */
+			if ((st = optGetStatus()) < 0)		/* try again */
+				return -EIO;
+		}
+		if (st & (ST_DOOR_OPEN|ST_DRVERR)) {
+			printk("optcd: no disk or door open\n");
+			return -EIO;
+		}
+		if (optUpdateToc() < 0)
+			return -EIO;
+	}
+	opt_open_count++;
+	MOD_INC_USE_COUNT;
+	optCmd(COMLOCK);		/* Lock door */
+#ifdef DEBUG_VFS
+	printk("optcd: exiting opt_open\n");
+#endif
+	return 0;
+}
+
+/* Release device special file; flush all blocks from the buffer cache */
+static void opt_release(struct inode *ip, struct file *fp) {
+#ifdef DEBUG_VFS
+	printk("optcd: executing opt_release\n");
+	printk("inode: %p, inode -> i_rdev: 0x%x, file: %p\n",
+		ip, ip -> i_rdev, fp);
+#endif
+	if (!--opt_open_count) {
+		opt_invalidate_buffers();
+		sync_dev(ip -> i_rdev);
+		invalidate_buffers(ip -> i_rdev);
+		CLEAR_TIMER;
+		optCmd(COMUNLOCK);	/* Unlock door */
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+
+/* Initialisation */
+
+static int version_ok(void) {
+	char devname[100];
+	int count, i, ch;
+
+	if (optCmd(COMVERSION) < 0)
+		return 0;
+	if ((count = optGetData()) < 0)
+		return 0;
+	for (i = 0, ch = -1; count > 0; count--) {
+		if ((ch = optGetData()) < 0)
+			break;
+		if (i < 99)
+			devname[i++] = ch;
+	}
+	devname[i] = '\0';
+	if (ch < 0)
+		return 0;
+	printk("optcd: Device %s detected\n", devname);
+	return ((devname[0] == 'D')
+	     && (devname[1] == 'O')
+	     && (devname[2] == 'L')
+	     && (devname[3] == 'P')
+	     && (devname[4] == 'H')
+	     && (devname[5] == 'I')
+	     && (devname[6] == 'N'));
+}
+
+
+static struct file_operations opt_fops = {
+	NULL,		/* lseek - default */
+	block_read,	/* read - general block-dev read */
+	block_write,	/* write - general block-dev write */
+	NULL,		/* readdir - bad */
+	NULL,		/* select */
+	opt_ioctl,	/* ioctl */
+	NULL,		/* mmap */
+	opt_open,	/* open */
+	opt_release,	/* release */
+	NULL,		/* fsync */
+	NULL,		/* fasync */
+	NULL,		/* media change */
+	NULL		/* revalidate */
+};
+
+
+/* Get kernel parameter when used as a kernel driver */
+void optcd_setup(char *str, int *ints) {
+	if (ints[0] > 0)
+		optcd_port = ints[1];
+}
+
+#ifndef MODULE
+#define RETURN_EIO return mem_start
+#else
+#define RETURN_EIO return -EIO
+#endif
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ */
+#ifndef MODULE
+unsigned long optcd_init(unsigned long mem_start, unsigned long mem_end) {
+#else
+int init_module(void) {
+#endif
+	if (optcd_port <= 0) {
+		printk("optcd: no Optics Storage CDROM Initialization\n");
+		RETURN_EIO;
+	}
+	if (check_region(optcd_port, 4)) {
+		printk("optcd: conflict, I/O port 0x%x already used\n",
+			optcd_port);
+		RETURN_EIO;
+	}
+	if (!optResetDrive()) {
+		printk("optcd: drive at 0x%x not ready\n", optcd_port);
+		RETURN_EIO;
+	}
+	if (!version_ok()) {
+		printk("optcd: unknown drive detected; aborting\n");
+		RETURN_EIO;
+	}
+	if (optCmd(COMINITDOUBLE) < 0) {
+		printk("optcd: cannot init double speed mode\n");
+		RETURN_EIO;
+	}
+	if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
+	{
+		printk("optcd: unable to get major %d\n", MAJOR_NR);
+		RETURN_EIO;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+	request_region(optcd_port, 4, "optcd");
+	optPresent = 1;
+	printk("optcd: 8000 AT CDROM at 0x%x\n", optcd_port);
+#ifndef MODULE
+	return mem_start;
+#else
+	return 0;
+#endif
+}
+
+#ifdef MODULE
+void cleanup_module(void) {
+	if (MOD_IN_USE) {
+		printk("optcd: module in use - can't remove it.\n");
+	return;
+	}
+	if ((unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL)) {
+		printk("optcd: what's that: can't unregister\n");
+		return;
+	}
+	release_region(optcd_port, 4);
+	printk("optcd: module released.\n");
+}
+#endif MODULE
diff -u --recursive --new-file v1.3.6/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.3.6/linux/drivers/block/sbpcd.c	Mon Jun 12 09:51:18 1995
+++ linux/drivers/block/sbpcd.c	Sat Jul  1 19:05:59 1995
@@ -9,9 +9,10 @@
  *            Also for the CreativeLabs CD200 drive (but I still need some
  *            detailed bug reports).
  *            Also for the TEAC CD-55A drive.
- *            Not for Funai or Sanyo drives.
+ *            Not for Sanyo drives (but sjcd is there...).
+ *            Not for Funai drives.
  *
- *  NOTE:     This is release 3.7.
+ *  NOTE:     This is release 3.8.
  *
  *  VERSION HISTORY
  *
@@ -214,6 +215,8 @@
  *       a lot of sessions.
  *       Bettered the sbpcd_open() behavior with TEAC drives.
  *
+ *  3.8  Elongated max_latency for CR-56x drives.
+ *
  *  TODO
  *
  *     disk change detection
@@ -295,7 +298,7 @@
 
 #include "blk.h"
 
-#define VERSION "v3.7 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v3.8 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*==========================================================================*/
 /*
@@ -3979,6 +3982,7 @@
 	case CDROMEJECT:
 		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
 		if (fam0_drive) return (0);
+		if (D_S[d].open_count>1) return (-EBUSY);
 		i=UnLockDoor();
 		D_S[d].open_count=-9; /* to get it locked next time again */
 		i=cc_SpinDown();
@@ -4028,6 +4032,8 @@
 					    }
 		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
 		if (st)	return (st);
+		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st) return (st);
 		memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
 		switch (D_S[d].audio_state)
 		{
@@ -4564,9 +4570,7 @@
 	max_latency=900;
 #else
 	if (D_S[d].f_multisession) max_latency=900;
-	else if (fam0L_drive) max_latency=300;
-	else if (famT_drive) max_latency=300;
-	else max_latency=100;
+	else max_latency=300;
 #endif
 	msg(DBG_TE2,"beginning to READ\n");
 	duration=jiffies;
diff -u --recursive --new-file v1.3.6/linux/drivers/block/sjcd.c linux/drivers/block/sjcd.c
--- v1.3.6/linux/drivers/block/sjcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/sjcd.c	Sat Jul  1 19:05:59 1995
@@ -0,0 +1,1358 @@
+/*
+ *   Sanyo CD-ROM device driver implementation.
+ *
+ *   Copyright (C) 1995  Vadim V. Model
+ *
+ *   model@cecmow.enet.dec.com
+ *   vadim@rbrf.msk.su
+ *   vadim@ipsun.ras.ru
+ *
+ *  This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de);
+ *  it was developed under use of mcd.c from Martin Harriss, with help of
+ *  E. van der Maarel (maarel@marin.nl).
+ *
+ *  It is planned to include these routines into sbpcd.c later - to make
+ *  a "mixed use" on one cable possible for all kinds of drives which use
+ *  the SoundBlaster/Panasonic style CDROM interface. But today, the
+ *  ability to install directly from CDROM is more important than flexibility.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR SANYO_CDROM_MAJOR
+#include "blk.h"
+#ifdef CONFIG_ISP_16
+#include <linux/isp16.h>
+#endif
+#include <linux/sjcd.h>
+
+static int sjcd_present = 0;
+
+#define SJCD_BUF_SIZ 32 /* cdr-h94a has internal 64K buffer */
+
+/*
+ * buffer for block size conversion
+ */
+static char sjcd_buf[ 2048 * SJCD_BUF_SIZ ];
+static volatile int sjcd_buf_bn[ SJCD_BUF_SIZ ], sjcd_next_bn;
+static volatile int sjcd_buf_in, sjcd_buf_out = -1;
+
+/*
+ * Status.
+ */
+static unsigned short sjcd_status_valid = 0;
+static unsigned short sjcd_door_closed;
+static unsigned short sjcd_media_is_available;
+static unsigned short sjcd_media_is_changed;
+static unsigned short sjcd_toc_uptodate = 0;
+static unsigned short sjcd_command_failed;
+static volatile unsigned char sjcd_completion_status = 0;
+static volatile unsigned char sjcd_completion_error = 0;
+static unsigned short sjcd_command_is_in_progress = 0;
+static unsigned short sjcd_error_reported = 0;
+
+static int sjcd_open_count;
+
+static int sjcd_audio_status;
+static struct sjcd_play_msf sjcd_playing;
+
+static short    sjcd_port = SJCD_BASE_ADDR;
+static int      sjcd_irq  = SJCD_INTR_NR;
+
+static struct wait_queue *sjcd_waitq = NULL;
+
+/*
+ * Data transfer.
+ */
+static volatile unsigned short sjcd_transfer_is_active = 0;
+
+enum sjcd_transfer_state {
+  SJCD_S_IDLE     = 0,
+  SJCD_S_START    = 1,
+  SJCD_S_MODE     = 2,
+  SJCD_S_READ     = 3,
+  SJCD_S_DATA     = 4,
+  SJCD_S_STOP     = 5,
+  SJCD_S_STOPPING = 6
+};
+static enum sjcd_transfer_state sjcd_transfer_state = SJCD_S_IDLE;
+static long sjcd_transfer_timeout = 0;
+static int sjcd_read_count = 0;
+#if 0
+static unsigned short sjcd_tries;
+#endif
+static unsigned char sjcd_mode = 0;
+
+#define SJCD_READ_TIMEOUT 5000
+
+/*
+ * Statistic.
+ */
+static struct sjcd_stat statistic;
+
+/*
+ * Timer.
+ */
+static struct timer_list sjcd_delay_timer = { NULL, NULL, 0, 0, NULL };
+
+#define SJCD_SET_TIMER( func, jiffies )           \
+    ( sjcd_delay_timer.expires = jiffies,         \
+      sjcd_delay_timer.function = ( void * )func, \
+      add_timer( &sjcd_delay_timer ) )
+
+#define CLEAR_TIMER del_timer( &sjcd_delay_timer )
+
+/*
+ * Set up device. Not yet implemented.
+ */
+void sjcd_setup( char *str, int *ints ){
+    printk( "Sanyo CDR-H94A device driver setup is called (%s).\n", str );
+}
+
+/*
+ * Special converters.
+ */
+static unsigned char bin2bcd( int bin ){
+  int u, v;
+
+  u = bin % 10; v = bin / 10;
+  return( u | ( v << 4 ) );
+}
+
+static int bcd2bin( unsigned char bcd ){
+    return( ( bcd >> 4 ) * 10 + ( bcd & 0x0F ) );
+}
+
+static long msf2hsg( struct msf *mp ){
+  return( bcd2bin( mp->frame ) + bcd2bin( mp->sec ) * 75
+	 + bcd2bin( mp->min ) * 4500 - 150 );
+}
+
+static void hsg2msf( long hsg, struct msf *msf ){
+  hsg += 150; msf->min = hsg / 4500;
+  hsg %= 4500; msf->sec = hsg / 75; msf->frame = hsg % 75;
+  msf->min = bin2bcd( msf->min );       /* convert to BCD */
+  msf->sec = bin2bcd( msf->sec );
+  msf->frame = bin2bcd( msf->frame );
+}
+
+/*
+ * Send a command to cdrom. Invalidate status.
+ */
+static void sjcd_send_cmd( unsigned char cmd ){
+#if 0
+  printk( "sjcd: send_cmd( 0x%x )\n", cmd );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Send a command with one arg to cdrom. Invalidate status.
+ */
+static void sjcd_send_1_cmd( unsigned char cmd, unsigned char a ){
+#if 0
+  printk( "sjcd: send_1_cmd( 0x%x, 0x%x )\n", cmd, a );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  outb( a, SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Send a command with four args to cdrom. Invalidate status.
+ */
+static void sjcd_send_4_cmd( unsigned char cmd, unsigned char a,
+	    unsigned char b, unsigned char c, unsigned char d ){
+#if 0
+  printk( "sjcd: send_4_cmd( 0x%x )\n", cmd );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  outb( a, SJCDPORT( 0 ) );
+  outb( b, SJCDPORT( 0 ) );
+  outb( c, SJCDPORT( 0 ) );
+  outb( d, SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Send a play or read command to cdrom. Invalidate Status.
+ */
+static void sjcd_send_6_cmd( unsigned char cmd, struct sjcd_play_msf *pms ){
+#if 0
+  printk( "sjcd: send_long_cmd( 0x%x )\n", cmd );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  outb( pms->start.min,   SJCDPORT( 0 ) );
+  outb( pms->start.sec,   SJCDPORT( 0 ) );
+  outb( pms->start.frame, SJCDPORT( 0 ) );
+  outb( pms->end.min,     SJCDPORT( 0 ) );
+  outb( pms->end.sec,     SJCDPORT( 0 ) );
+  outb( pms->end.frame,   SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Get a value from the data port. Should not block, so we use a little
+ * wait for a while. Returns 0 if OK.
+ */
+static int sjcd_load_response( void *buf, int len ){
+  unsigned char *resp = ( unsigned char * )buf;
+
+  for( ; len; --len ){ 
+    int i;
+    for( i = 200; i-- && inb( SJCDPORT( 1 ) ) !=  0x09; );
+    if( i > 0 ) *resp++ = ( unsigned char )inb( SJCDPORT( 0 ) );
+    else break;
+  }
+  return( len );
+}
+
+/*
+ * Load and parse status.
+ */
+static void sjcd_load_status( void ){
+  sjcd_media_is_changed = 0;
+  sjcd_completion_error = 0;
+  sjcd_completion_status = inb( SJCDPORT( 0 ) );
+  if( sjcd_completion_status & SST_DOOR_OPENED ){
+    sjcd_door_closed = sjcd_media_is_available = 0;
+  } else {
+    sjcd_door_closed = 1;
+    if( sjcd_completion_status & SST_MEDIA_CHANGED )
+      sjcd_media_is_available = sjcd_media_is_changed = 1;
+    else if( sjcd_completion_status & 0x0F ){
+      while( ( inb( SJCDPORT( 1 ) ) & 0x0B ) != 0x09 );
+      sjcd_completion_error = inb( SJCDPORT( 0 ) );
+      if( ( sjcd_completion_status & 0x08 ) &&
+	 ( sjcd_completion_error & 0x40 ) )
+	sjcd_media_is_available = 0;
+      else sjcd_command_failed = 1;
+    } else sjcd_media_is_available = 1;
+  }
+  /*
+   * Ok, status loaded successfully.
+   */
+  sjcd_status_valid = 1, sjcd_error_reported = 0;
+  sjcd_command_is_in_progress = 0;
+  if( sjcd_media_is_changed ) sjcd_toc_uptodate = 0;
+#if 0
+  printk( "sjcd: status %02x.%02x loaded.\n",
+	 ( int )sjcd_completion_status, ( int )sjcd_completion_error );
+#endif
+}
+
+/*
+ * Read status from cdrom. Check to see if the status is available.
+ */
+static int sjcd_check_status( void ){
+  /*
+   * Try to load the response from cdrom into buffer.
+   */
+  if( ( inb( SJCDPORT( 1 ) ) & 0x0B ) == 0x09 ){
+    sjcd_load_status();
+    return( 1 );
+  } else {
+    /*
+     * No status is available.
+     */
+    return( 0 );
+  }
+}
+
+static volatile long sjcd_status_timeout;
+#define SJCD_WAIT_FOR_STATUS_TIMEOUT 1000
+
+static void sjcd_status_timer( void ){
+  if( sjcd_check_status() ){
+    wake_up( &sjcd_waitq );
+  } else if( --sjcd_status_timeout <= 0 ){
+    wake_up( &sjcd_waitq );
+  } else {
+    SJCD_SET_TIMER( sjcd_status_timer, 1 );
+  }
+}
+
+static int sjcd_wait_for_status( void ){
+  sjcd_status_timeout = SJCD_WAIT_FOR_STATUS_TIMEOUT;
+  SJCD_SET_TIMER( sjcd_status_timer, 1 ); 
+  sleep_on( &sjcd_waitq );    
+  if( sjcd_status_timeout <= 0 )
+    printk( "sjcd: Error Wait For Status.\n" );
+  return( sjcd_status_timeout );
+}
+
+static int sjcd_receive_status( void ){
+  int i;
+#if 0
+  printk( "sjcd: receive_status\n" );
+#endif
+  /*
+   * Wait a bit for status available.
+   */
+  for( i = 200; i-- && ( sjcd_check_status() == 0 ); );
+  if( i < 0 ){
+#if 0
+    printk( "sjcd: long wait for status\n" );
+#endif
+    if( sjcd_wait_for_status() <= 0 )
+      printk( "sjcd: Timeout when read status.\n" );
+    else i = 0;
+  }
+  return( i );
+}
+
+/*
+ * Load the status.
+ */
+static void sjcd_get_status( void ){
+#if 0
+  printk( "sjcd: get_status\n" );
+#endif
+  sjcd_send_cmd( SCMD_GET_STATUS );
+  sjcd_receive_status();
+}
+
+/*
+ * Check the drive if the disk is changed.
+ */
+static int sjcd_disk_change( dev_t full_dev ){
+#if 0
+  printk( "sjcd_disk_change( 0x%x )\n", full_dev );
+#endif
+  if( MINOR( full_dev ) > 0 ){
+    printk( "sjcd: request error: invalid device minor.\n" );
+    return 0;
+  }
+  if( !sjcd_command_is_in_progress )
+    sjcd_get_status();
+  return( sjcd_status_valid ? sjcd_media_is_changed : 0 );
+}
+
+/*
+ * Read the table of contents (TOC) and TOC header if necessary.
+ * We assume that the drive contains no more than 99 toc entries.
+ */
+static struct sjcd_hw_disk_info sjcd_table_of_contents[ 100 ];
+static unsigned char sjcd_first_track_no, sjcd_last_track_no;
+#define sjcd_disk_length  sjcd_table_of_contents[0].un.track_msf
+
+static int sjcd_update_toc( void ){
+  struct sjcd_hw_disk_info info;
+  int i;
+#if 0
+  printk( "sjcd: update toc:\n" );
+#endif
+  /*
+   * check to see if we need to do anything
+   */
+  if( sjcd_toc_uptodate ) return( 0 );
+
+  /*
+   * Get the TOC start information.
+   */
+  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_1_TRACK );
+  sjcd_receive_status();
+
+  if( !sjcd_status_valid ){
+    printk( "cannot load status.\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_media_is_available ){
+    printk( "no disk in drive\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_command_failed ){
+    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
+      printk( "cannot load response about TOC start.\n" );
+      return( -1 );
+    }
+    sjcd_first_track_no = bcd2bin( info.un.track_no );
+  } else {
+    printk( "get first failed\n" );
+    return( -1 );
+  }
+#if 0
+  printk( "TOC start 0x%02x ", sjcd_first_track_no );
+#endif
+  /*
+   * Get the TOC finish information.
+   */
+  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_L_TRACK );
+  sjcd_receive_status();
+
+  if( !sjcd_status_valid ){
+    printk( "cannot load status.\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_media_is_available ){
+    printk( "no disk in drive\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_command_failed ){
+    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
+      printk( "cannot load response about TOC finish.\n" );
+      return( -1 );
+    }
+    sjcd_last_track_no = bcd2bin( info.un.track_no );
+  } else {
+    printk( "get last failed\n" );
+    return( -1 );
+  }
+#if 0
+  printk( "TOC finish 0x%02x ", sjcd_last_track_no );
+#endif
+  for( i = sjcd_first_track_no; i <= sjcd_last_track_no; i++ ){
+    /*
+     * Get the first track information.
+     */
+    sjcd_send_1_cmd( SCMD_GET_DISK_INFO, bin2bcd( i ) );
+    sjcd_receive_status();
+
+    if( !sjcd_status_valid ){
+      printk( "cannot load status.\n" );
+      return( -1 );
+    }
+
+    if( !sjcd_media_is_available ){
+      printk( "no disk in drive\n" );
+      return( -1 );
+    }
+
+    if( !sjcd_command_failed ){
+      if( sjcd_load_response( &sjcd_table_of_contents[ i ],
+			     sizeof( struct sjcd_hw_disk_info ) ) != 0 ){
+	printk( "cannot load info for %d track\n", i );
+	return( -1 );
+      }
+    } else {
+      printk( "get info %d failed\n", i );
+      return( -1 );
+    }
+  }
+
+  /*
+   * Get the disk lenght info.
+   */
+  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_D_SIZE );
+  sjcd_receive_status();
+
+  if( !sjcd_status_valid ){
+    printk( "cannot load status.\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_media_is_available ){
+    printk( "no disk in drive\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_command_failed ){
+    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
+      printk( "cannot load response about disk size.\n" );
+      return( -1 );
+    }
+    sjcd_disk_length.min = info.un.track_msf.min;
+    sjcd_disk_length.sec = info.un.track_msf.sec;
+    sjcd_disk_length.frame = info.un.track_msf.frame;
+  } else {
+    printk( "get size failed\n" );
+    return( 1 );
+  }
+#if 0
+  printk( "(%02x:%02x.%02x)\n", sjcd_disk_length.min,
+	 sjcd_disk_length.sec, sjcd_disk_length.frame );
+#endif
+  return( 0 );
+}
+
+/*
+ * Load subchannel information.
+ */
+static int sjcd_get_q_info( struct sjcd_hw_qinfo *qp ){
+  int s;
+#if 0
+  printk( "sjcd: load sub q\n" );
+#endif
+  sjcd_send_cmd( SCMD_GET_QINFO );
+  s = sjcd_receive_status();
+  if( s < 0 || sjcd_command_failed || !sjcd_status_valid ){
+    sjcd_send_cmd( 0xF2 );
+    s = sjcd_receive_status();
+    if( s < 0 || sjcd_command_failed || !sjcd_status_valid ) return( -1 );
+    sjcd_send_cmd( SCMD_GET_QINFO );
+    s = sjcd_receive_status();
+    if( s < 0 || sjcd_command_failed || !sjcd_status_valid ) return( -1 );
+  }
+  if( sjcd_media_is_available )
+    if( sjcd_load_response( qp, sizeof( *qp ) ) == 0 ) return( 0 );
+  return( -1 );
+}
+
+/*
+ * Start playing from the specified position.
+ */
+static int sjcd_play( struct sjcd_play_msf *mp ){
+  struct sjcd_play_msf msf;
+
+  /*
+   * Turn the device to play mode.
+   */
+  sjcd_send_1_cmd( SCMD_SET_MODE, SCMD_MODE_PLAY );
+  if( sjcd_receive_status() < 0 ) return( -1 );
+
+  /*
+   * Seek to the starting point.
+   */
+  msf.start = mp->start;
+  msf.end.min = msf.end.sec = msf.end.frame = 0x00;
+  sjcd_send_6_cmd( SCMD_SEEK, &msf );
+  if( sjcd_receive_status() < 0 ) return( -1 );
+
+  /*
+   * Start playing.
+   */
+  sjcd_send_6_cmd( SCMD_PLAY, mp );
+  return( sjcd_receive_status() );
+}
+
+/*
+ * Do some user commands.
+ */
+static int sjcd_ioctl( struct inode *ip, struct file *fp,
+		       unsigned int cmd, unsigned long arg ){
+#if 0
+  printk( "sjcd:ioctl\n" );
+#endif
+
+  if( ip == NULL ) return( -EINVAL );
+
+  sjcd_get_status();
+  if( !sjcd_status_valid ) return( -EIO );
+  if( sjcd_update_toc() < 0 ) return( -EIO );
+
+  switch( cmd ){
+  case CDROMSTART:{
+#if 0
+    printk( "sjcd: ioctl: start\n" );
+#endif
+    return( 0 );
+  }
+
+  case CDROMSTOP:{
+#if 0
+    printk( "sjcd: ioctl: stop\n" );
+#endif
+    sjcd_send_cmd( SCMD_PAUSE );
+    ( void )sjcd_receive_status();
+    sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+    return( 0 );
+  }
+
+  case CDROMPAUSE:{
+    struct sjcd_hw_qinfo q_info;
+#if 0
+    printk( "sjcd: ioctl: pause\n" );
+#endif
+    if( sjcd_audio_status == CDROM_AUDIO_PLAY ){
+      sjcd_send_cmd( SCMD_PAUSE );
+      ( void )sjcd_receive_status();
+      if( sjcd_get_q_info( &q_info ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+      } else {
+	sjcd_audio_status = CDROM_AUDIO_PAUSED;
+	sjcd_playing.start = q_info.abs;
+      }
+      return( 0 );
+    } else return( -EINVAL );
+  }
+
+  case CDROMRESUME:{
+#if 0
+    printk( "sjcd: ioctl: resume\n" );
+#endif
+    if( sjcd_audio_status == CDROM_AUDIO_PAUSED ){
+      /*
+       * continue play starting at saved location
+       */
+      if( sjcd_play( &sjcd_playing ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_ERROR;
+	return( -EIO );
+      } else {
+	sjcd_audio_status = CDROM_AUDIO_PLAY;
+	return( 0 );
+      }
+    } else return( -EINVAL );
+  }
+
+  case CDROMPLAYTRKIND:{
+    struct cdrom_ti ti; int s;
+#if 0
+    printk( "sjcd: ioctl: playtrkind\n" );
+#endif
+    if( ( s = verify_area( VERIFY_READ, (void *) arg, sizeof( ti ) ) ) == 0 ){
+      memcpy_fromfs( &ti, (void *) arg, sizeof( ti ) );
+
+      if( ti.cdti_trk0 < sjcd_first_track_no ) return( -EINVAL );
+      if( ti.cdti_trk1 > sjcd_last_track_no )
+	ti.cdti_trk1 = sjcd_last_track_no;
+      if( ti.cdti_trk0 > ti.cdti_trk1 ) return( -EINVAL );
+
+      sjcd_playing.start = sjcd_table_of_contents[ ti.cdti_trk0 ].un.track_msf;
+      sjcd_playing.end = ( ti.cdti_trk1 < sjcd_last_track_no ) ?
+	sjcd_table_of_contents[ ti.cdti_trk1 + 1 ].un.track_msf :
+	  sjcd_table_of_contents[ 0 ].un.track_msf;
+      
+      if( sjcd_play( &sjcd_playing ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_ERROR;
+	return( -EIO );
+      } else sjcd_audio_status = CDROM_AUDIO_PLAY;
+    }
+    return( s );
+  }
+
+  case CDROMPLAYMSF:{
+    struct cdrom_msf sjcd_msf; int s;
+#if 0
+    printk( "sjcd: ioctl: playmsf\n" );
+#endif
+    if( ( s = verify_area( VERIFY_READ, (void *) arg, sizeof( sjcd_msf ) ) ) == 0 ){
+      if( sjcd_audio_status == CDROM_AUDIO_PLAY ){
+	sjcd_send_cmd( SCMD_PAUSE );
+	( void )sjcd_receive_status();
+	sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+      }
+
+      memcpy_fromfs( &sjcd_msf, ( void * )arg, sizeof( sjcd_msf ) );
+
+      sjcd_playing.start.min = bin2bcd( sjcd_msf.cdmsf_min0 );
+      sjcd_playing.start.sec = bin2bcd( sjcd_msf.cdmsf_sec0 );
+      sjcd_playing.start.frame = bin2bcd( sjcd_msf.cdmsf_frame0 );
+      sjcd_playing.end.min = bin2bcd( sjcd_msf.cdmsf_min1 );
+      sjcd_playing.end.sec = bin2bcd( sjcd_msf.cdmsf_sec1 );
+      sjcd_playing.end.frame = bin2bcd( sjcd_msf.cdmsf_frame1 );
+
+      if( sjcd_play( &sjcd_playing ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_ERROR;
+	return( -EIO );
+      } else sjcd_audio_status = CDROM_AUDIO_PLAY;
+    }
+    return( s );
+  }
+
+  case CDROMREADTOCHDR:{
+    struct cdrom_tochdr toc_header; int s;
+#if 0
+    printk( "sjcd: ioctl: readtocheader\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *) arg, sizeof( toc_header ) ) ) == 0 ){
+      toc_header.cdth_trk0 = sjcd_first_track_no;
+      toc_header.cdth_trk1 = sjcd_last_track_no;
+      memcpy_tofs( ( void * )arg, &toc_header, sizeof( toc_header ) );
+    }
+    return( s );
+  }
+
+  case CDROMREADTOCENTRY:{
+    struct cdrom_tocentry toc_entry; int s;
+#if 0
+    printk( "sjcd: ioctl: readtocentry\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *) arg, sizeof( toc_entry ) ) ) == 0 ){
+      struct sjcd_hw_disk_info *tp;
+
+      memcpy_fromfs( &toc_entry, ( void * )arg, sizeof( toc_entry ) );
+
+      if( toc_entry.cdte_track == CDROM_LEADOUT )
+	tp = &sjcd_table_of_contents[ 0 ];
+      else if( toc_entry.cdte_track < sjcd_first_track_no ) return( -EINVAL );
+      else if( toc_entry.cdte_track > sjcd_last_track_no ) return( -EINVAL );
+      else tp = &sjcd_table_of_contents[ toc_entry.cdte_track ];
+
+      toc_entry.cdte_adr = tp->track_control & 0x0F;
+      toc_entry.cdte_ctrl = tp->track_control >> 4;
+
+      switch( toc_entry.cdte_format ){
+      case CDROM_LBA:
+	toc_entry.cdte_addr.lba = msf2hsg( &( tp->un.track_msf ) );
+	break;
+      case CDROM_MSF:
+	toc_entry.cdte_addr.msf.minute = bcd2bin( tp->un.track_msf.min );
+	toc_entry.cdte_addr.msf.second = bcd2bin( tp->un.track_msf.sec );
+	toc_entry.cdte_addr.msf.frame = bcd2bin( tp->un.track_msf.frame );
+	break;
+      default: return( -EINVAL );
+      }
+      memcpy_tofs( ( void * )arg, &toc_entry, sizeof( toc_entry ) );
+    }
+    return( s );
+  }
+
+  case CDROMSUBCHNL:{
+    struct cdrom_subchnl subchnl; int s;
+#if 0
+    printk( "sjcd: ioctl: subchnl\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *) arg, sizeof( subchnl ) ) ) == 0 ){
+      struct sjcd_hw_qinfo q_info;
+
+      memcpy_fromfs( &subchnl, ( void * )arg, sizeof( subchnl ) );
+      if( sjcd_get_q_info( &q_info ) < 0 ) return( -EIO );
+
+      subchnl.cdsc_audiostatus = sjcd_audio_status;
+      subchnl.cdsc_adr = q_info.track_control & 0x0F;
+      subchnl.cdsc_ctrl = q_info.track_control >> 4;
+      subchnl.cdsc_trk = bcd2bin( q_info.track_no );
+      subchnl.cdsc_ind = bcd2bin( q_info.x );
+
+      switch( subchnl.cdsc_format ){
+      case CDROM_LBA:
+	subchnl.cdsc_absaddr.lba = msf2hsg( &( q_info.abs ) );
+	subchnl.cdsc_reladdr.lba = msf2hsg( &( q_info.rel ) );
+	break;
+      case CDROM_MSF:
+	subchnl.cdsc_absaddr.msf.minute = bcd2bin( q_info.abs.min );
+	subchnl.cdsc_absaddr.msf.second = bcd2bin( q_info.abs.sec );
+	subchnl.cdsc_absaddr.msf.frame = bcd2bin( q_info.abs.frame );
+	subchnl.cdsc_reladdr.msf.minute = bcd2bin( q_info.rel.min );
+	subchnl.cdsc_reladdr.msf.second = bcd2bin( q_info.rel.sec );
+	subchnl.cdsc_reladdr.msf.frame = bcd2bin( q_info.rel.frame );
+	break;
+      default: return( -EINVAL );
+      }
+      memcpy_tofs( ( void * )arg, &subchnl, sizeof( subchnl ) );
+    }
+    return( s );
+  }
+
+  case CDROMVOLCTRL:{
+    struct cdrom_volctrl vol_ctrl; int s;
+#if 0
+    printk( "sjcd: ioctl: volctrl\n" );
+#endif
+    if( ( s = verify_area( VERIFY_READ, (void *) arg, sizeof( vol_ctrl ) ) ) == 0 ){
+      unsigned char dummy[ 4 ];
+
+      memcpy_fromfs( &vol_ctrl, ( void * )arg, sizeof( vol_ctrl ) );
+      sjcd_send_4_cmd( SCMD_SET_VOLUME, vol_ctrl.channel0, 0xFF,
+		      vol_ctrl.channel1, 0xFF );
+      if( sjcd_receive_status() < 0 ) return( -EIO );
+      ( void )sjcd_load_response( dummy, 4 );
+    }
+    return( s );
+  }
+
+  case CDROMEJECT:{
+#if 0
+    printk( "sjcd: ioctl: eject\n" );
+#endif
+    if( !sjcd_command_is_in_progress ){
+      sjcd_send_cmd( SCMD_EJECT_TRAY );
+      ( void )sjcd_receive_status();
+    }
+    return( 0 );
+  }
+
+  case 0xABCD:{
+    int s;
+#if 0
+    printk( "sjcd: ioctl: statistic\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *) arg, sizeof( statistic ) ) ) == 0 )
+      memcpy_tofs( ( void * )arg, &statistic, sizeof( statistic ) );
+    return( s );
+  }
+
+  default:
+    return( -EINVAL );
+  }
+}
+
+#if 0
+/*
+ * We only seem to get interrupts after an error.
+ * Just take the interrupt and clear out the status reg.
+ */
+static void sjcd_interrupt( int irq, struct pt_regs *regs ){
+  printk( "sjcd: interrupt is cought\n" );
+}
+#endif
+
+/*
+ * Invalidate internal buffers of the driver.
+ */
+static void sjcd_invalidate_buffers( void ){
+  int i;
+  for( i = 0; i < SJCD_BUF_SIZ; sjcd_buf_bn[ i++ ] = -1 );
+  sjcd_buf_out = -1;
+}
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+
+#define CURRENT_IS_VALID                                      \
+    ( CURRENT != NULL && MAJOR( CURRENT->dev ) == MAJOR_NR && \
+      CURRENT->cmd == READ && CURRENT->sector != -1 )
+
+static void sjcd_transfer( void ){
+#if 0
+  printk( "sjcd: transfer:\n" );
+#endif
+  if( CURRENT_IS_VALID ){
+    while( CURRENT->nr_sectors ){
+      int i, bn = CURRENT->sector / 4;
+      for( i = 0; i < SJCD_BUF_SIZ && sjcd_buf_bn[ i ] != bn; i++ );
+      if( i < SJCD_BUF_SIZ ){
+	int offs = ( i * 4 + ( CURRENT->sector & 3 ) ) * 512;
+	int nr_sectors = 4 - ( CURRENT->sector & 3 );
+	if( sjcd_buf_out != i ){
+	  sjcd_buf_out = i;
+	  if( sjcd_buf_bn[ i ] != bn ){
+	    sjcd_buf_out = -1;
+	    continue;
+	  }
+	}
+	if( nr_sectors > CURRENT->nr_sectors )
+	  nr_sectors = CURRENT->nr_sectors;
+#if 0
+	printk( "copy out\n" );
+#endif
+	memcpy( CURRENT->buffer, sjcd_buf + offs, nr_sectors * 512 );
+	CURRENT->nr_sectors -= nr_sectors;
+	CURRENT->sector += nr_sectors;
+	CURRENT->buffer += nr_sectors * 512;
+      } else {
+	sjcd_buf_out = -1;
+	break;
+      }
+    }
+  }
+#if 0
+  printk( "sjcd: transfer: done\n" );
+#endif
+}
+
+static void sjcd_poll( void ){
+  /*
+   * Update total number of ticks.
+   */
+  statistic.ticks++;
+  statistic.tticks[ sjcd_transfer_state ]++;
+
+ ReSwitch: switch( sjcd_transfer_state ){
+      
+  case SJCD_S_IDLE:{
+    statistic.idle_ticks++;
+#if 0
+    printk( "SJCD_S_IDLE\n" );
+#endif
+    return;
+  }
+
+  case SJCD_S_START:{
+    statistic.start_ticks++;
+    sjcd_send_cmd( SCMD_GET_STATUS );
+    sjcd_transfer_state =
+      sjcd_mode == SCMD_MODE_COOKED ? SJCD_S_READ : SJCD_S_MODE;
+    sjcd_transfer_timeout = 500;
+#if 0
+    printk( "SJCD_S_START: goto SJCD_S_%s mode\n",
+	   sjcd_transfer_state == SJCD_S_READ ? "READ" : "MODE" );
+#endif
+    break;
+  }
+    
+  case SJCD_S_MODE:{
+    if( sjcd_check_status() ){
+      /*
+       * Previos command is completed.
+       */
+      if( !sjcd_status_valid || sjcd_command_failed ){
+#if 0
+	printk( "SJCD_S_MODE: pre-cmd failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      sjcd_mode = 0; /* unknown mode; should not be valid when failed */
+      sjcd_send_1_cmd( SCMD_SET_MODE, SCMD_MODE_COOKED );
+      sjcd_transfer_state = SJCD_S_READ; sjcd_transfer_timeout = 1000;
+#if 0
+      printk( "SJCD_S_MODE: goto SJCD_S_READ mode\n" );
+#endif
+    } else statistic.mode_ticks++;
+    break;
+  }
+
+  case SJCD_S_READ:{
+    if( sjcd_status_valid ? 1 : sjcd_check_status() ){
+      /*
+       * Previos command is completed.
+       */
+      if( !sjcd_status_valid || sjcd_command_failed ){
+#if 0
+	printk( "SJCD_S_READ: pre-cmd failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+      if( !sjcd_media_is_available ){
+#if 0
+	printk( "SJCD_S_READ: no disk: goto to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+      if( sjcd_mode != SCMD_MODE_COOKED ){
+	/*
+	 * We seem to come from set mode. So discard one byte of result.
+	 */
+	if( sjcd_load_response( &sjcd_mode, 1 ) != 0 ){
+#if 0
+	  printk( "SJCD_S_READ: load failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	  sjcd_transfer_state = SJCD_S_STOP;
+	  goto ReSwitch;
+	}
+	if( sjcd_mode != SCMD_MODE_COOKED ){
+#if 0
+	  printk( "SJCD_S_READ: mode failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	  sjcd_transfer_state = SJCD_S_STOP;
+	  goto ReSwitch;
+	}
+      }
+
+      if( CURRENT_IS_VALID ){
+	struct sjcd_play_msf msf;
+
+	sjcd_next_bn = CURRENT->sector / 4;
+	hsg2msf( sjcd_next_bn, &msf.start );
+	msf.end.min = 0; msf.end.sec = 0;            
+	msf.end.frame = sjcd_read_count = SJCD_BUF_SIZ;
+#if 0
+	printk( "---reading msf-address %x:%x:%x  %x:%x:%x\n",
+	       msf.start.min, msf.start.sec, msf.start.frame,
+	       msf.end.min,   msf.end.sec,   msf.end.frame );
+	printk( "sjcd_next_bn:%x buf_in:%x buf_out:%x buf_bn:%x\n", \
+	     sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
+	     sjcd_buf_bn[ sjcd_buf_in ] );
+#endif	
+	sjcd_send_6_cmd( SCMD_DATA_READ, &msf );
+	sjcd_transfer_state = SJCD_S_DATA;
+	sjcd_transfer_timeout = 500;
+#if 0
+	printk( "SJCD_S_READ: go to SJCD_S_DATA mode\n" );
+#endif
+      } else {
+#if 0
+	printk( "SJCD_S_READ: nothing to read: go to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+    } else statistic.read_ticks++;
+    break;
+  }
+
+  case SJCD_S_DATA:{
+    unsigned char stat;
+
+  sjcd_s_data: stat = inb( SJCDPORT( 1 ) ) & 0x0B;
+#if 0
+    printk( "SJCD_S_DATA: status = 0x%02x\n", stat );
+#endif
+    if( stat == 0x09 ){
+      /*
+       * No data is waiting for us in the drive buffer. Status of operation
+       * completion is available. Read and parse it.
+       */
+      sjcd_load_status();
+
+      if( !sjcd_status_valid || sjcd_command_failed ){
+	printk( "sjcd: read block %d failed, maybe audio disk? Giving up\n",
+	       sjcd_next_bn );
+	if( CURRENT_IS_VALID ) end_request( 0 );
+	printk( "SJCD_S_DATA: pre-cmd failed: go to SJCD_S_STOP mode\n" );
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      if( !sjcd_media_is_available ){
+	printk( "SJCD_S_DATA: no disk: go to SJCD_S_STOP mode\n" );
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      sjcd_transfer_state = SJCD_S_START;
+      goto ReSwitch;
+    } else if( stat == 0x0A ){
+      /*
+       * One frame is read into device buffer. We must copy it to our memory.
+       * Otherwise cdrom hangs up. Check to see if we have something to read
+       * to.
+       */
+      if( !CURRENT_IS_VALID && sjcd_buf_in == sjcd_buf_out ){
+	printk( "SJCD_S_DATA: nothing to read: go to SJCD_S_STOP mode\n" );
+	printk( " ... all the date would be discarded\n" );
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      /*
+       * Everything seems to be OK. Just read the frame and recalculate
+       * indecis.
+       */
+      sjcd_buf_bn[ sjcd_buf_in ] = -1; /* ??? */
+      insb( SJCDPORT( 2 ), sjcd_buf + 2048 * sjcd_buf_in, 2048 );
+#if 0
+      printk( "SJCD_S_DATA: next_bn=%d, buf_in=%d, buf_out=%d, buf_bn=%d\n",
+	     sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
+	     sjcd_buf_bn[ sjcd_buf_in ] );
+#endif
+      sjcd_buf_bn[ sjcd_buf_in ] = sjcd_next_bn++;
+      if( sjcd_buf_out == -1 ) sjcd_buf_out = sjcd_buf_in;
+      if( ++sjcd_buf_in == SJCD_BUF_SIZ ) sjcd_buf_in = 0;
+
+      /*
+       * Only one frame is ready at time. So we should turn over to wait for
+       * another frame. If we need that, of course.
+       */
+      if( --sjcd_read_count == 0 ){
+	/*
+	 * OK, request seems to be precessed. Continue transferring...
+	 */
+	if( !sjcd_transfer_is_active ){
+	  while( CURRENT_IS_VALID ){
+	    /*
+	     * Continue transferring.
+	     */
+	    sjcd_transfer();
+	    if( CURRENT->nr_sectors == 0 ) end_request( 1 );
+	    else break;
+	  }
+	}
+	if( CURRENT_IS_VALID &&
+	   ( CURRENT->sector / 4 < sjcd_next_bn ||
+	    CURRENT->sector / 4 > sjcd_next_bn + SJCD_BUF_SIZ ) ){
+#if 0
+	  printk( "SJCD_S_DATA: can't read: go to SJCD_S_STOP mode\n" );
+#endif
+	  sjcd_transfer_state = SJCD_S_STOP;
+	  goto ReSwitch;
+	}
+      }
+      goto sjcd_s_data;
+      /* sjcd_transfer_timeout = 500; */
+    } else statistic.data_ticks++;
+    break;
+  }
+
+  case SJCD_S_STOP:{
+    sjcd_read_count = 0;
+    sjcd_send_cmd( SCMD_STOP );
+    sjcd_transfer_state = SJCD_S_STOPPING;
+    sjcd_transfer_timeout = 500;
+    statistic.stop_ticks++;
+    break;
+  }
+
+  case SJCD_S_STOPPING:{
+    unsigned char stat;
+    
+    stat = inb( SJCDPORT( 1 ) ) & 0x0B;
+#if 0
+    printk( "SJCD_S_STOP: status = 0x%02x\n", stat );
+#endif      
+    if( stat == 0x0A ){
+      int i;
+#if 0
+      printk( "SJCD_S_STOP: discard data\n" );
+#endif
+      /*
+       * Discard all the data from the pipe. Foolish method.
+       */
+      for( i = 2048; i--; ( void )inb( SJCDPORT( 2 ) ) );
+      sjcd_transfer_timeout = 500;
+    } else if( stat == 0x09 ){
+      sjcd_load_status();
+      if( sjcd_status_valid && sjcd_media_is_changed ) {
+	sjcd_toc_uptodate = 0;
+	sjcd_invalidate_buffers();
+      }
+      if( CURRENT_IS_VALID ){
+	if( sjcd_status_valid ) sjcd_transfer_state = SJCD_S_READ;
+	else sjcd_transfer_state = SJCD_S_START;
+      } else sjcd_transfer_state = SJCD_S_IDLE;
+      goto ReSwitch;
+    } else statistic.stopping_ticks++;
+    break;
+  }
+
+  default:
+    printk( "sjcd_poll: invalid state %d\n", sjcd_transfer_state );
+    return;
+  }
+  
+  if( --sjcd_transfer_timeout == 0 ){
+    printk( "sjcd: timeout in state %d\n", sjcd_transfer_state );
+    while( CURRENT_IS_VALID ) end_request( 0 );
+    sjcd_send_cmd( SCMD_STOP );
+    sjcd_transfer_state = SJCD_S_IDLE;
+    goto ReSwitch;
+  }
+
+  /*
+   * Get back in some time.
+   */
+  SJCD_SET_TIMER( sjcd_poll, 1 );
+}
+
+static void do_sjcd_request( void ){
+#if 0
+  printk( "sjcd: do_sjcd_request(%ld+%ld)\n",
+	 CURRENT->sector, CURRENT->nr_sectors );
+#endif
+  sjcd_transfer_is_active = 1;
+  while( CURRENT_IS_VALID ){
+    /*
+     * Who of us are paranoic?
+     */
+    if( CURRENT->bh && !( CURRENT->bh->b_lock ) )
+      panic( DEVICE_NAME ": block not locked" );
+
+    sjcd_transfer();
+    if( CURRENT->nr_sectors == 0 ) end_request( 1 );
+    else {
+      sjcd_buf_out = -1;         /* Want to read a block not in buffer */
+      if( sjcd_transfer_state == SJCD_S_IDLE ){
+	if( !sjcd_toc_uptodate ){
+	  if( sjcd_update_toc() < 0 ){
+	    printk( "sjcd: transfer: discard\n" );
+	    while( CURRENT_IS_VALID ) end_request( 0 );
+	    break;
+	  }
+	}
+	sjcd_transfer_state = SJCD_S_START;
+	SJCD_SET_TIMER( sjcd_poll, 1 );
+      }
+      break;
+    }
+  }
+  sjcd_transfer_is_active = 0;
+#if 0
+  printk( "sjcd_next_bn:%x sjcd_buf_in:%x sjcd_buf_out:%x sjcd_buf_bn:%x\n",
+	 sjcd_next_bn, sjcd_buf_in, sjcd_buf_out, sjcd_buf_bn[ sjcd_buf_in ] );
+  printk( "do_sjcd_request ends\n" );
+#endif
+}
+
+/*
+ * Open the device special file. Check that a disk is in.
+ */
+int sjcd_open( struct inode *ip, struct file *fp ){
+  /*
+   * Check the presence of device.
+   */
+  if( !sjcd_present ) return( -ENXIO );
+  
+  /*
+   * Only read operations are allowed. Really? (:-)
+   */
+  if( fp->f_mode & 2 ) return( -EROFS );
+  
+  if( sjcd_open_count == 0 ){
+    sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+    sjcd_mode = 0;
+    sjcd_transfer_state = SJCD_S_IDLE;
+    sjcd_invalidate_buffers();
+
+    /*
+     * Strict status checking.
+     */
+    sjcd_get_status();
+    if( !sjcd_status_valid ){
+#if 0
+      printk( "sjcd: open: timed out when check status.\n" );
+#endif
+      return( -EIO );
+    } else if( !sjcd_media_is_available ){
+#if 0
+      printk("sjcd: open: no disk in drive\n");
+#endif
+      return( -EIO );
+    }
+#if 0
+    printk( "sjcd: open: done\n" );
+#endif
+  }
+  return( ++sjcd_open_count, 0 );
+}
+
+/*
+ * On close, we flush all sjcd blocks from the buffer cache.
+ */
+static void sjcd_release( struct inode *inode, struct file *file ){
+#if 0
+  printk( "sjcd: release\n" );
+#endif
+  if( --sjcd_open_count == 0 ){
+    sjcd_invalidate_buffers();
+    sync_dev( inode->i_rdev );
+    invalidate_buffers( inode->i_rdev );
+  }
+}
+
+/*
+ * A list of file operations allowed for this cdrom.
+ */
+static struct file_operations sjcd_fops = {
+  NULL,               /* lseek - default */
+  block_read,         /* read - general block-dev read */
+  block_write,        /* write - general block-dev write */
+  NULL,               /* readdir - bad */
+  NULL,               /* select */
+  sjcd_ioctl,         /* ioctl */
+  NULL,               /* mmap */
+  sjcd_open,          /* open */
+  sjcd_release,       /* release */
+  NULL,               /* fsync */
+  NULL,               /* fasync */
+  sjcd_disk_change,   /* media change */
+  NULL                /* revalidate */
+};
+
+/*
+ * Following staff is intended for initialization of the cdrom. It
+ * first looks for presence of device. If the device is present, it
+ * will be reset. Then read the version of the drive and load status.
+ */
+static struct {
+  unsigned char major, minor;
+} sjcd_version;
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ * Probe cdrom, find out version and status.
+ */
+unsigned long sjcd_init( unsigned long mem_start, unsigned long mem_end ){
+  int i;
+
+  if( sjcd_port != 0x340 || sjcd_irq != 0x0A /* 10 */ ){
+    printk( "skip sjcd_init\n" );
+    return( mem_start );
+  }
+  
+#ifdef CONFIG_ISP_16
+  /*
+   * Initialize the CDROM interface of the card.
+   */
+  isp16_cdi_setup( ISP_CDROM_SANYO, ISP_CDROM_IRQ_10,
+		  ISP_CDROM_DMA_DISABLED, ISP_CDROM_PORT_340 );
+#endif
+
+#if 0
+  printk( "sjcd=0x%x,%d: ", sjcd_port, sjcd_irq );
+#endif  
+
+  if( register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){
+    printk( "Unable to get major %d for Sanyo CD-ROM\n", MAJOR_NR );
+    return( mem_start );
+  }
+  
+  blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST;
+  read_ahead[ MAJOR_NR ] = 4;
+  
+  if( check_region( sjcd_port, 4 ) ){
+    printk( "Init failed, I/O port (%X) is already in use\n",
+	   sjcd_port );
+    return( mem_start );
+  }
+  
+  printk( "Sanyo CDR-H94A:" );
+
+  /*
+   * Check for card.
+   */
+  for( i = 100; i > 0; --i )
+    if( !( inb( SJCDPORT( 1 ) ) & 0x04 ) ) break;
+  if( i == 0 ){
+    printk( " No device at 0x%x found.\n", sjcd_port );
+    return( mem_start );
+  }
+  
+  sjcd_send_cmd( SCMD_RESET );
+  while( !sjcd_status_valid ) ( void )sjcd_check_status();
+
+  /*
+   * Get and print out cdrom version.
+   */
+  sjcd_send_cmd( SCMD_GET_VERSION );
+  while( !sjcd_status_valid ) ( void )sjcd_check_status();
+
+  if( sjcd_load_response( &sjcd_version, sizeof( sjcd_version ) ) == 0 ){
+    printk( " Version %1x.%02x.", ( int )sjcd_version.major,
+	     ( int )sjcd_version.minor );
+  } else {
+    printk( " Read version failed.\n" );
+    return( mem_start );
+  }
+
+  /*
+   * Check and print out the tray state. (if it is needed?).
+   */
+  if( !sjcd_status_valid ){
+    sjcd_send_cmd( SCMD_GET_STATUS );
+    while( !sjcd_status_valid ) ( void )sjcd_check_status();
+  }
+
+  printk( " Status: port=0x%x, irq=%d\n",
+	 sjcd_port, sjcd_irq );
+
+  sjcd_present++;
+  return( mem_start );
+}
+
diff -u --recursive --new-file v1.3.6/linux/drivers/block/sonycd535.c linux/drivers/block/sonycd535.c
--- v1.3.6/linux/drivers/block/sonycd535.c	Mon Jun 12 12:06:03 1995
+++ linux/drivers/block/sonycd535.c	Sat Jul  1 19:05:59 1995
@@ -28,7 +28,7 @@
  *      (Heiko Eissfeldt <heiko@colossus.escape.de>)
  *
  * 1995-06-01
- *  More chages to support CDU-510/515 series
+ *  More changes to support CDU-510/515 series
  *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
  *
  * Things to do:
@@ -108,7 +108,6 @@
 
 
 #include <linux/config.h>
-#if defined(CONFIG_CDU535) || defined(MODULE)
 
 #ifdef MODULE
 # include <linux/module.h>
@@ -117,6 +116,9 @@
 # ifndef CONFIG_MODVERSIONS
 	char kernel_version[]= UTS_RELEASE;
 # endif
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
 #endif
 
 #include <linux/errno.h>
@@ -136,17 +138,11 @@
 #include <asm/segment.h>
 
 #include <linux/cdrom.h>
-#include <linux/sonycd535.h>
 
 #define MAJOR_NR CDU535_CDROM_MAJOR
-
-#ifdef MODULE
 # include "blk.h"
-#else
-# include "blk.h"
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
+#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */
+#include <linux/sonycd535.h>
 
 /*
  * this is the base address of the interface card for the Sony CDU-535
@@ -1541,8 +1537,8 @@
 	int  i;
 #endif
 
-	/* Setting the base I/O address to 0xffff will disable it. */
-	if (sony535_cd_base_io == 0xffff)
+	/* Setting the base I/O address to 0 will disable it. */
+	if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0))
 		goto bail;
 
 	/* Set up all the register locations */
@@ -1739,5 +1735,3 @@
 		printk(CDU535_HANDLE " module released\n");
 }
 #endif	/* MODULE */
-
-#endif	/* CONFIG_CDU535 */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.3.6/linux/drivers/net/3c501.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/3c501.c	Wed Jul  5 13:06:26 1995
@@ -572,7 +572,7 @@
      
     outb(AX_SYS, AX_CMD);
 
-    skb = dev_alloc_skb(pkt_len);
+    skb = dev_alloc_skb(pkt_len+2);
     /*
      *	Start of frame
      */
@@ -582,6 +582,7 @@
 	lp->stats.rx_dropped++;
 	return;
     } else {
+    	skb_reserve(skb,2);	/* Force 16 byte alignment */
 	skb->dev = dev;
 
 	/*
diff -u --recursive --new-file v1.3.6/linux/drivers/net/3c505.c linux/drivers/net/3c505.c
--- v1.3.6/linux/drivers/net/3c505.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/3c505.c	Wed Jul  5 13:06:26 1995
@@ -560,7 +560,7 @@
 
 	rlen = (len+1) & ~1;
 
-	skb = dev_alloc_skb(rlen);
+	skb = dev_alloc_skb(rlen+2);
 
 	/*
 	 * make sure the data register is going the right way
@@ -587,6 +587,7 @@
 		adapter->stats.rx_dropped++;
 
 	} else {
+		skb_reserve(skb,2);	/* 16 byte alignment */
 		skb->dev = dev;
 
 		/*
diff -u --recursive --new-file v1.3.6/linux/drivers/net/3c507.c linux/drivers/net/3c507.c
--- v1.3.6/linux/drivers/net/3c507.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/3c507.c	Wed Jul  5 13:06:26 1995
@@ -835,13 +835,14 @@
 			struct sk_buff *skb;
 
 			pkt_len &= 0x3fff;
-			skb = dev_alloc_skb(pkt_len);
+			skb = dev_alloc_skb(pkt_len+2);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->len = pkt_len;
+			
+			skb_reserve(skb,2);
 			skb->dev = dev;
 
 			/* 'skb->data' points to the start of sk_buff data area. */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.3.6/linux/drivers/net/3c509.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/3c509.c	Wed Jul  5 13:06:26 1995
@@ -591,12 +591,13 @@
 			short pkt_len = rx_status & 0x7ff;
 			struct sk_buff *skb;
 
-			skb = dev_alloc_skb(pkt_len+3);
+			skb = dev_alloc_skb(pkt_len+5);
 			if (el3_debug > 4)
 				printk("Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
 			if (skb != NULL) {
 				skb->dev = dev;
+				skb_reserve(skb,2);	/* Align IP on 16 byte boundaries */
 
 				/* 'skb->data' points to the start of sk_buff data area. */
 				insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
diff -u --recursive --new-file v1.3.6/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v1.3.6/linux/drivers/net/8390.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/8390.c	Wed Jul  5 13:06:26 1995
@@ -455,7 +455,7 @@
 		} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
 			struct sk_buff *skb;
 			
-			skb = dev_alloc_skb(pkt_len);
+			skb = dev_alloc_skb(pkt_len+2);
 			if (skb == NULL) {
 				if (ei_debug > 1)
 					printk("%s: Couldn't allocate a sk_buff of size %d.\n",
@@ -463,6 +463,7 @@
 				ei_local->stat.rx_dropped++;
 				break;
 			} else {
+				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */
 				skb->dev = dev;
 				
 				ei_block_input(dev, pkt_len, skb_put(skb,pkt_len),
diff -u --recursive --new-file v1.3.6/linux/drivers/net/README.wavelan linux/drivers/net/README.wavelan
--- v1.3.6/linux/drivers/net/README.wavelan	Wed Feb 22 16:25:04 1995
+++ linux/drivers/net/README.wavelan	Sun Jul  2 10:30:37 1995
@@ -1,6 +1,4 @@
-#if	defined(CONFIG_WAVELAN)
-
-Thu Feb 23 00:10:31 EST 1995
+Sun Jul  2 01:38:33 EST 1995
 
 1. At present the driver autoprobes for a WaveLAN card only at I/O address 0x390.
    The version of the card that I use (NCR) supports four I/O addresses (selectable
@@ -23,9 +21,13 @@
 	append	="ether=0,0x390,0x4321,eth0"
 	..
 
-3. If you encounter any problems send me some email.
+3. The driver uses the IRQ stored in the card's PSA.
+   To change this you will need to use the configuration/setup software that
+   accompanies each WaveLAN device.  Yes, the driver should use the value passed
+   in via LILO and it will, just as soon as I can work out why that part of the
+   code doesn't work :-(.
+
+4. If you encounter any problems send me some email.
 
 Good luck,
 Bruce Janson (bruce@cs.usyd.edu.au)
-
-#endif	/* defined(CONFIG_WAVELAN) */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v1.3.6/linux/drivers/net/arcnet.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/arcnet.c	Wed Jul  5 13:06:26 1995
@@ -2084,6 +2084,13 @@
 unsigned short arc_type_trans(struct sk_buff *skb,struct device *dev)
 {
 	struct ClientData *head = (struct ClientData *) skb->data;
+
+	/*
+	 *	Pull off the arcnet header.
+	 */
+	 
+	skb->mac.raw=skb->data;
+	skb_pull(skb,dev->hard_header_len);
 	
 	if (head->daddr==0)
 		skb->pkt_type=PACKET_BROADCAST;
diff -u --recursive --new-file v1.3.6/linux/drivers/net/at1700.c linux/drivers/net/at1700.c
--- v1.3.6/linux/drivers/net/at1700.c	Fri Jun 30 16:22:26 1995
+++ linux/drivers/net/at1700.c	Wed Jul  5 13:06:26 1995
@@ -527,7 +527,7 @@
 				lp->stats.rx_errors++;
 				break;
 			}
-			skb = dev_alloc_skb(pkt_len+1);
+			skb = dev_alloc_skb(pkt_len+3);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet (len %d).\n",
 					   dev->name, pkt_len);
@@ -537,8 +537,8 @@
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->len = pkt_len;
 			skb->dev = dev;
+			skb_reserve(skb,2);
 
 			insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
 			skb->protocol=eth_type_trans(skb, dev);
diff -u --recursive --new-file v1.3.6/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v1.3.6/linux/drivers/net/de4x5.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/de4x5.c	Thu Jul  6 13:22:04 1995
@@ -1153,9 +1153,10 @@
 	struct sk_buff *skb;
 	short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
 
-	if ((skb = dev_alloc_skb(pkt_len)) != NULL) {
+	if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
 	  skb->dev = dev;
 	
+	  skb_reserve(skb,2);		/* Align */
 	  if (entry < lp->rx_old) {         /* Wrapped buffer */
 	    short len = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
 	    memcpy(skb_put(skb,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf), len);
@@ -2663,21 +2664,21 @@
       tmp.addr[j++] = dev->dev_addr[i];
     }
     tmp.addr[j++] = lp->rxRingSize;
-    tmp.lval[j>>2] = (s32)lp->rx_ring; j+=4;
-    tmp.lval[j>>2] = (s32)lp->tx_ring; j+=4;
+    tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
+    tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
 
     for (i=0;i<lp->rxRingSize-1;i++){
       if (i < 3) {
-	tmp.lval[j>>2] = (s32)&lp->rx_ring[i].status; j+=4;
+	tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
       }
     }
-    tmp.lval[j>>2] = (s32)&lp->rx_ring[i].status; j+=4;
+    tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
     for (i=0;i<lp->txRingSize-1;i++){
       if (i < 3) {
-	tmp.lval[j>>2] = (s32)&lp->tx_ring[i].status; j+=4;
+	tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
       }
     }
-    tmp.lval[j>>2] = (s32)&lp->tx_ring[i].status; j+=4;
+    tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
       
     for (i=0;i<lp->rxRingSize-1;i++){
       if (i < 3) {
diff -u --recursive --new-file v1.3.6/linux/drivers/net/de600.c linux/drivers/net/de600.c
--- v1.3.6/linux/drivers/net/de600.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/de600.c	Wed Jul  5 13:06:26 1995
@@ -609,7 +609,7 @@
 		return;
 	}
 
-	skb = dev_alloc_skb(size);
+	skb = dev_alloc_skb(size+2);
 	sti();
 	if (skb == NULL) {
 		printk("%s: Couldn't allocate a sk_buff of size %d.\n",
@@ -619,6 +619,7 @@
 	/* else */
 
 	skb->dev = dev;
+	skb_reserve(skb,2);	/* Align */
 	
 	/* 'skb->data' points to the start of sk_buff data area. */
 	buffer = skb_put(skb,size);
diff -u --recursive --new-file v1.3.6/linux/drivers/net/de620.c linux/drivers/net/de620.c
--- v1.3.6/linux/drivers/net/de620.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/de620.c	Wed Jul  5 13:06:26 1995
@@ -678,13 +678,14 @@
 		printk("%s: Illegal packet size: %d!\n", dev->name, size);
 	}
 	else { /* Good packet? */
-		skb = dev_alloc_skb(size);
+		skb = dev_alloc_skb(size+2);
 		if (skb == NULL) { /* Yeah, but no place to put it... */
 			printk("%s: Couldn't allocate a sk_buff of size %d.\n",
 				dev->name, size);
 			((struct netstats *)(dev->priv))->rx_dropped++;
 		}
 		else { /* Yep! Go get it! */
+			skb_reserve(skb,2);	/* Align */
 			skb->dev = dev;
 			skb->free = 1;
 			/* skb->data points to the start of sk_buff data area */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/depca.c linux/drivers/net/depca.c
--- v1.3.6/linux/drivers/net/depca.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/depca.c	Thu Jul  6 13:22:04 1995
@@ -943,9 +943,11 @@
 	short len, pkt_len = readw(&lp->rx_ring[entry].msg_length);
 	struct sk_buff *skb;
 
-	skb = dev_alloc_skb(pkt_len);
+	skb = dev_alloc_skb(pkt_len+2);
 	if (skb != NULL) {
-	  unsigned char * buf = skb_put(skb,pkt_len);
+	  unsigned char * buf;
+	  skb_reserve(skb,2);	/* 16 byte align the IP header */
+	  buf = skb_put(skb,pkt_len);
 	  skb->dev = dev;
 	  if (entry < lp->rx_old) {         /* Wrapped buffer */
 	    len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
@@ -973,13 +975,13 @@
 	    }
 	  }
 	  if (buf[0] & 0x01) {              /* Multicast/Broadcast */
-	    if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
+	    if ((*(s16 *)&buf[0] == -1) && (*(s32 *)&buf[2] == -1)) {
 	      lp->pktStats.broadcast++;
 	    } else {
 	      lp->pktStats.multicast++;
 	    }
-	  } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
-		     (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+	  } else if ((*(s16 *)&buf[0] == *(s16 *)&dev->dev_addr[0]) &&
+		     (*(s32 *)&buf[2] == *(s32 *)&dev->dev_addr[2])) {
 	    lp->pktStats.unicast++;
 	  }
 	  
diff -u --recursive --new-file v1.3.6/linux/drivers/net/eepro.c linux/drivers/net/eepro.c
--- v1.3.6/linux/drivers/net/eepro.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/eepro.c	Wed Jul  5 13:06:26 1995
@@ -1046,13 +1046,14 @@
 			struct sk_buff *skb;
 
 			rcv_size &= 0x3fff;
-			skb = dev_alloc_skb(rcv_size);
+			skb = dev_alloc_skb(rcv_size+2);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
 			skb->dev = dev;
+			skb_reserve(skb,2);
 
 			insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 1) >> 1);
 	
diff -u --recursive --new-file v1.3.6/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.3.6/linux/drivers/net/eexpress.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/eexpress.c	Wed Jul  5 13:06:26 1995
@@ -941,13 +941,14 @@
 			struct sk_buff *skb;
 
 			pkt_len &= 0x3fff;
-			skb = dev_alloc_skb(pkt_len);
+			skb = dev_alloc_skb(pkt_len+2);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
 			skb->dev = dev;
+			skb_reserve(skb,2);
 
 			outw(data_buffer_addr + 10, ioaddr + READ_PTR);
 
diff -u --recursive --new-file v1.3.6/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- v1.3.6/linux/drivers/net/ewrk3.c	Fri Jun 30 16:22:27 1995
+++ linux/drivers/net/ewrk3.c	Thu Jul  6 13:29:59 1995
@@ -1035,18 +1035,19 @@
 	} else {
 	  struct sk_buff *skb;
 
-          if ((skb = dev_alloc_skb(pkt_len)) != NULL) {
+          if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
+            unsigned char *p;
 	    skb->dev = dev;
+	    skb_reserve(skb,2);		/* Align to 16 bytes */
+	    p = skb_put(skb,pkt_len);
 
 	    if (lp->shmem_length == IO_ONLY) {
-	      unsigned char *p = skb_put(skb,pkt_len);
-
 	      *p = inb(EWRK3_DATA);         /* dummy read */
-	      for (i=0; i<skb->len; i++) {
+	      for (i=0; i<pkt_len; i++) {
 		*p++ = inb(EWRK3_DATA);
 	      }
 	    } else {
-	      memcpy(skb->data, buf, pkt_len);
+	      memcpy(p, buf, pkt_len);
 	    }
 
 	    /* 
@@ -1731,7 +1732,7 @@
 
     break;
   case EWRK3_GET_STATS:              /* Get the driver statistics */
-    err = verify_area(VERIFY_WRITE, (void *)ioc->data, sizeof(lp->pktStats)));
+    err = verify_area(VERIFY_WRITE, (void *)ioc->data, sizeof(lp->pktStats));
     if (err) return err;
 
     cli();
@@ -1879,6 +1880,7 @@
   if (MOD_IN_USE) {
     printk("%s: device busy, remove delayed\n",thisEthwrk.name);
   } else {
+    release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE);
     unregister_netdev(&thisEthwrk);
   }
 }
diff -u --recursive --new-file v1.3.6/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v1.3.6/linux/drivers/net/hp100.c	Fri Jun 30 16:22:28 1995
+++ linux/drivers/net/hp100.c	Sat Jul  1 19:58:50 1995
@@ -601,7 +601,7 @@
        else
         {
           skb -> dev = dev;
-          insl( ioaddr + HP100_REG_DATA32, skb_put(pkt_len), ( pkt_len + 3 ) >> 2 );
+          insl( ioaddr + HP100_REG_DATA32, skb_put(skb, pkt_len), ( pkt_len + 3 ) >> 2 );
           skb->protocol=eth_type_trans(skb,dev);
           netif_rx( skb );
           lp -> stats.rx_packets++;
diff -u --recursive --new-file v1.3.6/linux/drivers/net/i82586.h linux/drivers/net/i82586.h
--- v1.3.6/linux/drivers/net/i82586.h	Tue Jan 31 14:36:24 1995
+++ linux/drivers/net/i82586.h	Sun Jul  2 10:30:37 1995
@@ -1,4 +1,3 @@
-#if	defined(CONFIG_WAVELAN)
 /*
  * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
  *
@@ -407,4 +406,3 @@
  *
  * For more details, see wavelan.c.
  */
-#endif	/* defined(CONFIG_WAVELAN) */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v1.3.6/linux/drivers/net/lance.c	Fri Jun 30 16:22:28 1995
+++ linux/drivers/net/lance.c	Wed Jul  5 13:06:26 1995
@@ -984,7 +984,7 @@
 			short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
 			struct sk_buff *skb;
 
-			skb = dev_alloc_skb(pkt_len);
+			skb = dev_alloc_skb(pkt_len+2);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
 				for (i=0; i < RX_RING_SIZE; i++)
@@ -999,9 +999,11 @@
 				break;
 			}
 			skb->dev = dev;
-			memcpy(skb_put(skb,pkt_len),
+			skb_reserve(skb,2);	/* 16 byte align */
+			skb_put(skb,pkt_len);	/* Make room */
+			eth_copy_and_sum(skb_put(skb,pkt_len),
 				   (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
-				   pkt_len);
+				   pkt_len,0);
 			skb->protocol=eth_type_trans(skb,dev);
 			netif_rx(skb);
 			lp->stats.rx_packets++;
diff -u --recursive --new-file v1.3.6/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v1.3.6/linux/drivers/net/ne.c	Tue Jun 27 14:11:36 1995
+++ linux/drivers/net/ne.c	Sun Jul  2 21:23:28 1995
@@ -20,6 +20,7 @@
 
     Paul Gortmaker	: use ENISR_RDC to monitor Tx PIO uploads, made
 			  sanity checks and bad clone support optional.
+    Paul Gortmaker	: new reset code, reset card after probe at boot.
 
 */
 
@@ -84,8 +85,6 @@
 #define NESM_START_PG	0x40	/* First page of TX buffer */
 #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
 
-#define NE_RDC_TIMEOUT	0x02	/* Max wait in jiffies for Tx RDC */
-
 int ne_probe(struct device *dev);
 static int ne_probe1(struct device *dev, int ioaddr);
 
@@ -173,6 +172,23 @@
 
     printk("NE*000 ethercard probe at %#3x:", ioaddr);
 
+    /* Reset card. Who knows what dain-bramaged state it was left in. */
+    {	unsigned long reset_start_time = jiffies;
+
+	/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+	outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+	/* wait 20 ms for the dust to settle. */
+	while (jiffies - reset_start_time < 2*HZ/100) 
+		barrier();
+
+	if ((inb_p(ioaddr+EN0_ISR) & ENISR_RESET) == 0) {
+		printk(" not found (no reset ack).\n");
+		return ENODEV;
+	}
+	outb_p(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
+    }
+
     /* Read the 16 bytes of station address PROM.
        We must first initialize registers, similar to NS8390_init(eifdev, 0).
        We can't reliably read the SAPROM address without this.
@@ -328,17 +344,19 @@
 static void
 ne_reset_8390(struct device *dev)
 {
-    int tmp = inb_p(NE_BASE + NE_RESET);
-    int reset_start_time = jiffies;
+    unsigned long reset_start_time = jiffies;
 
     if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
+
+    /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+    outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
     ei_status.txing = 0;
     ei_status.dmaing = 0;
 
-    outb_p(tmp, NE_BASE + NE_RESET);
     /* This check _should_not_ be necessary, omit eventually. */
     while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
-	if (jiffies - reset_start_time > 2) {
+	if (jiffies - reset_start_time > 2*HZ/100) {
 	    printk("%s: ne_reset_8390() did not complete.\n", dev->name);
 	    break;
 	}
@@ -502,7 +520,7 @@
 #endif
 
     while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
-	if (jiffies - dma_start > NE_RDC_TIMEOUT) {
+	if (jiffies - dma_start > 2*HZ/100) {		/* 20ms */
 		printk("%s: timeout waiting for Tx RDC.\n", dev->name);
 		ne_reset_8390(dev);
 		NS8390_init(dev,1);
diff -u --recursive --new-file v1.3.6/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v1.3.6/linux/drivers/net/ni52.c	Fri Jun 30 16:22:28 1995
+++ linux/drivers/net/ni52.c	Wed Jul  5 13:06:26 1995
@@ -803,10 +803,11 @@
         {
           totlen &= RBD_MASK; /* length of this frame */
           rbd->status = 0;
-          skb = (struct sk_buff *) dev_alloc_skb(totlen);
+          skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
           if(skb != NULL)
           {
             skb->dev = dev;
+            skb_reserve(skb,2);		/* 16 byte alignment */
             memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen);
             skb->protocol=eth_type_trans(skb,dev);
             netif_rx(skb);
diff -u --recursive --new-file v1.3.6/linux/drivers/net/pi2.c linux/drivers/net/pi2.c
--- v1.3.6/linux/drivers/net/pi2.c	Fri Jun 30 16:22:28 1995
+++ linux/drivers/net/pi2.c	Wed Jul  5 13:06:26 1995
@@ -567,6 +567,7 @@
 	    memcpy(cfix, (char *) cur_buf->data,
 		   pkt_len - 1);
 	    skb->protocol=htons(ETH_P_AX25);
+	    skb->mac.raw=skb->data;
 	    IS_SKB(skb);
 	    netif_rx(skb);
 	    lp->stats.rx_packets++;
diff -u --recursive --new-file v1.3.6/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.3.6/linux/drivers/net/ppp.c	Fri Jun 30 16:22:28 1995
+++ linux/drivers/net/ppp.c	Wed Jul  5 13:06:26 1995
@@ -1153,10 +1153,10 @@
   skb=dev_alloc_skb(count);
   if(skb)
   {
+  	skb->mac.raw=skb->data;
   	memcpy(skb_put(skb,count), c,count);
   	skb->protocol=htons(ETH_P_IP);
   	skb->dev=ppp->dev;
-  	skb->len=count;
   	netif_rx(skb);
   }
   return 1;
@@ -1717,7 +1717,7 @@
   /* Get pointers to the various components */
   ppp   = &ppp_ctrl[dev->base_addr];
   tty   = ppp->tty;
-  p     = (unsigned char *) (skb + 1);
+  p     = skb->data;
   len   = skb->len;
   proto = PROTO_IP;
 
@@ -1791,7 +1791,7 @@
     ++ppp->stats.suncomp;
       
   if (ppp_debug_netpackets) {
-    struct iphdr *iph = (struct iphdr *) (skb + 1);
+    struct iphdr *iph = (struct iphdr *)skb->data;
     PRINTK ((KERN_DEBUG "%s ==> proto %x len %d src %x dst %x proto %d\n",
 	    dev->name, (int) proto, (int) len, (int) iph->saddr,
 	    (int) iph->daddr, (int) iph->protocol))
diff -u --recursive --new-file v1.3.6/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c
--- v1.3.6/linux/drivers/net/sk_g16.c	Fri Jun 30 16:22:28 1995
+++ linux/drivers/net/sk_g16.c	Wed Jul  5 13:06:27 1995
@@ -1570,7 +1570,7 @@
 	    int len = (rmdp->mlen & 0x0fff);  /* extract message length from receive buffer */
 	    struct sk_buff *skb;
 
-	    skb = dev_alloc_skb(len); /* allocate socket buffer */ 
+	    skb = dev_alloc_skb(len+2); /* allocate socket buffer */ 
 
 	    if (skb == NULL)                /* Could not get mem ? */
 	    {
@@ -1591,6 +1591,7 @@
 	    /* Prepare sk_buff to queue for upper layers */
 
 	    skb->dev = dev;
+	    skb_reserve(skb,2);		/* Align IP header on 16 byte boundary */
 	    
 	    /* 
              * Copy data out of our receive descriptor into sk_buff.
diff -u --recursive --new-file v1.3.6/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.3.6/linux/drivers/net/slip.c	Fri Jun 30 16:22:29 1995
+++ linux/drivers/net/slip.c	Wed Jul  5 13:06:27 1995
@@ -1,7 +1,7 @@
 /*
  * slip.c	This module implements the SLIP protocol for kernel-based
  *		devices like TTY.  It interfaces between a raw TTY, and the
- *		kernel's INET protocol layers (via DDI).
+ *		kernel's INET protocol layers.
  *
  * Version:	@(#)slip.c	0.8.3	12/24/94
  *
@@ -321,6 +321,7 @@
 	}
 	skb->dev = sl->dev;
 	memcpy(skb_put(skb,count), sl->rbuff, count);
+	skb->mac.raw=skb->data;
 	if(sl->mode&(SL_MODE_AX25|SL_MODE_AX25VC))
 		skb->protocol=htons(ETH_P_AX25);
 	else
@@ -452,32 +453,6 @@
 #endif
 	}
 
-#ifdef CONFIG_AX25
-#ifdef CONFIG_INET
-	/*
-	 * This code is not very intelligent. Fix me.
-	 */
-	if (skb->data[15] == LAPB_UI && skb->data[16] == AX25_P_IP) {
-		struct sk_buff *skbn;
-		char mode;
-		
-		mode = ax25_ip_mode_get((ax25_address *)(skb->data + 1), dev);
-
-		if (mode == 'V' || mode == 'v' || (mode == ' ' && sl->mode & SL_MODE_AX25VC)) {
-			if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-				sl->tx_errors++;
-				return 1;
-			}
-
-			ax25_send_frame(skbn, (ax25_address *)dev->dev_addr, (ax25_address *)(skbn->data + 1), dev);
-			dev_kfree_skb(skb, FREE_WRITE);
-			mark_bh(NET_BH);
-			return 0;
-		}
-	}
-#endif
-#endif
-
 	/* We were not busy, so we are now... :-) */
 	if (skb != NULL) {
 		sl_lock(sl);
@@ -953,12 +928,12 @@
 {
 	int err;
 
-	err = verify_area(VERIFY_READ, addr, 7);
+	err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN);
 	if (err)  {
 		return err;
 	}
 
-	memcpy_fromfs(dev->dev_addr, addr, 7);	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy_fromfs(dev->dev_addr, addr, AX25_ADDR_LEN);	/* addr is an AX.25 shifted ASCII mac address */
 
 	return 0;
 }
@@ -966,9 +941,17 @@
 static int
 sl_set_dev_mac_address(struct device *dev, void *addr)
 {
-	memcpy(dev->dev_addr, addr, 7);
+	memcpy(dev->dev_addr, addr, AX25_ADDR_LEN);
 	return 0;
 }
+
+int sl_get_ax25_mode(struct device *dev)
+{
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+
+	return sl->mode & SL_MODE_AX25VC;
+}
+
 #endif /* CONFIG_AX25 */
 
 
@@ -1030,8 +1013,8 @@
 		}
 #else
 		if ((tmp & SL_MODE_AX25) || (tmp & SL_MODE_AX25VC)) {
-			sl->dev->addr_len=7;	/* sizeof an AX.25 addr */
-			sl->dev->hard_header_len=17;	/* We don't do digipeaters */
+			sl->dev->addr_len=AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
+			sl->dev->hard_header_len=AX25_HEADER_LEN; /* We don't do digipeaters */
 		} else	{
 			sl->dev->addr_len=0;	/* No mac addr in slip mode */
 			sl->dev->hard_header_len=0;
@@ -1078,9 +1061,9 @@
 	struct slip *sl = &sl_ctrl[dev->base_addr];
 	int i;
 #ifdef CONFIG_AX25
-	static char ax25_bcast[7] =
+	static char ax25_bcast[AX25_ADDR_LEN] =
 		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
-	static char ax25_test[7] =
+	static char ax25_test[AX25_ADDR_LEN] =
 		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
 #endif
 
@@ -1143,8 +1126,8 @@
 	if (sl->dev->type == 260 || sl->dev->type == 272)  {
 		sl->dev->type = ARPHRD_AX25;
 	}
-	memcpy(dev->broadcast, ax25_bcast, 7); /* Only activated in AX.25 mode */
-	memcpy(dev->dev_addr, ax25_test, 7);   /*    ""      ""       ""    "" */
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);	/* Only activated in AX.25 mode */
+	memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);	/*    ""      ""       ""    "" */
 #endif
 	dev->rebuild_header	= sl_rebuild_header;
 
diff -u --recursive --new-file v1.3.6/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v1.3.6/linux/drivers/net/tulip.c	Fri Jun 30 16:22:29 1995
+++ linux/drivers/net/tulip.c	Wed Jul  5 13:06:27 1995
@@ -600,7 +600,7 @@
 			short pkt_len = lp->rx_ring[entry].status >> 16;
 			struct sk_buff *skb;
 
-			skb = dev_alloc_skb(pkt_len);
+			skb = dev_alloc_skb(pkt_len+2);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
 				/* Check that at least two ring entries are free.
@@ -617,6 +617,7 @@
 				break;
 			}
 			skb->dev = dev;
+			skb_reserve(skb,2);	/* 16 byte align the data fields */
 			memcpy(skb_put(skb,pkt_len), lp->rx_ring[entry].buffer1, pkt_len);
 			skb->protocol=eth_type_trans(skb,dev);
 			netif_rx(skb);
diff -u --recursive --new-file v1.3.6/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v1.3.6/linux/drivers/net/wavelan.c	Fri Jun 30 16:22:29 1995
+++ linux/drivers/net/wavelan.c	Wed Jul  5 12:53:22 1995
@@ -6,7 +6,6 @@
 
 #include	<linux/config.h>
 
-#if	defined(CONFIG_WAVELAN)
 #if	defined(MODULE)
 #include	<linux/module.h>
 #include	<linux/version.h>
@@ -26,7 +25,7 @@
 #include	<asm/bitops.h>
 #include	<asm/io.h>
 #include	<asm/dma.h>
-#include	<errno.h>
+#include	<linux/errno.h>
 #include	<linux/netdevice.h>
 #include	<linux/etherdevice.h>
 #include	<linux/skbuff.h>
@@ -40,9 +39,10 @@
 #define WAVELAN_DEBUG			0
 #endif	/* WAVELAN_DEBUG */
 
-#define	nels(a)				(sizeof(a) / sizeof(a[0]))
-
 #define	WATCHDOG_JIFFIES		512	/* TODO: express in HZ. */
+#define	ENABLE_FULL_PROMISCUOUS		0x10000
+
+#define	nels(a)				(sizeof(a) / sizeof(a[0]))
 
 typedef struct device		device;
 typedef struct enet_statistics	en_stats;
@@ -63,6 +63,7 @@
 	unsigned int	correct_nwid;
 	unsigned int	wrong_nwid;
 	unsigned int	promiscuous;
+	unsigned int	full_promiscuous;
 	timer_list	watchdog;
 	device		*dev;
 	net_local	*prev;
@@ -242,6 +243,28 @@
 	wavelan_16_on(ioaddr, hacr);
 }
 
+#if	defined(IRQ_SET_WORKS)
+/*
+ * Write bytes to the PSA.
+ */
+static
+void
+psa_write(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+{
+	wavelan_16_off(ioaddr, hacr);
+
+	while (n-- > 0)
+	{
+		outw(o, PIOR2(ioaddr));
+		o++;
+		outb(*b, PIOP2(ioaddr));
+		b++;
+	}
+
+	wavelan_16_on(ioaddr, hacr);
+}
+#endif	/* defined(IRQ_SET_WORKS) */
+
 /*
  * Read bytes from the on-board RAM.
  */
@@ -310,21 +333,36 @@
 	}
 }
 
+static int	irqvals[]	=
+{
+	   0,    0,    0, 0x01,
+	0x02, 0x04,    0, 0x08,
+	   0,    0, 0x10, 0x20,
+	0x40,    0,    0, 0x80,
+};
+
+#if	defined(IRQ_SET_WORKS)
+static
+int
+wavelan_unmap_irq(int irq, unsigned char *irqval)
+{
+	if (irq < 0 || irq >= nels(irqvals) || irqvals[irq] == 0)
+		return -1;
+	
+	*irqval = (unsigned char)irqvals[irq];
+
+	return 0;
+}
+#endif	/* defined(IRQ_SET_WORKS) */
+
 /*
  * Map values from the irq parameter register to irq numbers.
  */
 static
 int
-wavelan_map_irq(unsigned short ioaddr, unsigned char irqval)
+wavelan_map_irq(unsigned char irqval)
 {
-	int		irq;
-	static int	irqvals[]	=
-	{
-		   0,    0,    0, 0x01,
-		0x02, 0x04,    0, 0x08,
-		   0,    0, 0x10, 0x20,
-		0x40,    0,    0, 0x80,
-	};
+	int	irq;
 
 	for (irq = 0; irq < nels(irqvals); irq++)
 	{
@@ -392,7 +430,7 @@
 	}
 	else
 	{
-		if (lp->promiscuous)
+		if (lp->promiscuous && lp->full_promiscuous)
 			m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
 		else
 			m.mmw_loopt_sel = 0x00;
@@ -816,6 +854,7 @@
 	int		irq;
 	int		i;
 	net_local	*lp;
+	int		enable_full_promiscuous;
 
 	if (wavelan_debug > 0)
 		printk("%s: ->wavelan_probe1(dev=0x%x, ioaddr=0x%x)\n", dev->name, (unsigned int)dev, ioaddr);
@@ -844,9 +883,27 @@
 
 	printk("%s: WaveLAN at %#x,", dev->name, ioaddr);
 
-	if ((irq = wavelan_map_irq(ioaddr, psa.psa_int_req_no)) == -1)
+	if (dev->irq != 0)
 	{
-		printk(" could not wavelan_map_irq(0x%x, %d).\n", ioaddr, psa.psa_int_req_no);
+		printk("[WARNING: explicit IRQ value %d ignored: using PSA value instead]", dev->irq);
+#if	defined(IRQ_SET_WORKS)
+Leave this out until I can get it to work -- BJ.
+		if (wavelan_unmap_irq(dev->irq, &psa.psa_int_req_no) == -1)
+		{
+			printk(" could not wavelan_unmap_irq(%d, ..) -- ignored.\n", dev->irq);
+			dev->irq = 0;
+		}
+		else
+		{
+			psa_write(ioaddr, HACR_DEFAULT, (char *)&psa.psa_int_req_no - (char *)&psa, (unsigned char *)&psa.psa_int_req_no, sizeof(psa.psa_int_req_no));
+			wavelan_reset(ioaddr);
+		}
+#endif	/* defined(IRQ_SET_WORKS) */
+	}
+
+	if ((irq = wavelan_map_irq(psa.psa_int_req_no)) == -1)
+	{
+		printk(" could not wavelan_map_irq(%d).\n", psa.psa_int_req_no);
 		if (wavelan_debug > 0)
 			printk("%s: <-wavelan_probe1(): EAGAIN\n", dev->name);
 		return EAGAIN;
@@ -858,10 +915,19 @@
 	dev->base_addr = ioaddr;
 
 	/*
-	 * Apparently the third numeric argument to LILO's
+	 * The third numeric argument to LILO's
 	 * `ether=' control line arrives here as `dev->mem_start'.
-	 * If it is non-zero we use it instead of the PSA NWID.
+	 *
+	 * If bit 16 of dev->mem_start is non-zero we enable
+	 * full promiscuity.
+	 *
+	 * If either of the least significant two bytes of
+	 * dev->mem_start are non-zero we use them instead
+	 * of the PSA NWID.
 	 */
+	enable_full_promiscuous = (dev->mem_start & ENABLE_FULL_PROMISCUOUS) == ENABLE_FULL_PROMISCUOUS;
+	dev->mem_start &= ~ENABLE_FULL_PROMISCUOUS;
+
 	if (dev->mem_start != 0)
 	{
 		psa.psa_nwid[0] = (dev->mem_start >> 8) & 0xFF;
@@ -878,6 +944,8 @@
 		printk("%s%02x", (i == 0) ? " " : ":", dev->dev_addr[i]);
 
 	printk(", IRQ %d", dev->irq);
+	if (enable_full_promiscuous)
+		printk(", promisc");
 	printk(", nwid 0x%02x%02x", psa.psa_nwid[0], psa.psa_nwid[1]);
 
 	printk(", PC");
@@ -957,6 +1025,7 @@
 
 	lp->hacr = HACR_DEFAULT;
 
+	lp->full_promiscuous = enable_full_promiscuous;
 	lp->nwid[0] = psa.psa_nwid[0];
 	lp->nwid[1] = psa.psa_nwid[1];
 
@@ -2444,4 +2513,3 @@
  * Basser Department of Computer Science           Phone:  +61-2-351-3423
  * University of Sydney, N.S.W., 2006, AUSTRALIA   Fax:    +61-2-351-3838
  */
-#endif	/* defined(CONFIG_WAVELAN) */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- v1.3.6/linux/drivers/net/wavelan.h	Wed Feb  1 09:19:36 1995
+++ linux/drivers/net/wavelan.h	Sun Jul  2 10:30:38 1995
@@ -1,4 +1,3 @@
-#if	defined(CONFIG_WAVELAN)
 #define WAVELAN_ADDR_SIZE	6	/* Size of a MAC address */
 #define SA_ADDR0		0x08	/* First octet of WaveLAN MAC addresses */
 #define SA_ADDR1		0x00	/* Second octet of WaveLAN MAC addresses */
@@ -251,4 +250,3 @@
  *
  * For more details, see wavelan.c.
  */
-#endif	/* defined(CONFIG_WAVELAN) */
diff -u --recursive --new-file v1.3.6/linux/drivers/net/znet.c linux/drivers/net/znet.c
--- v1.3.6/linux/drivers/net/znet.c	Fri Jun 30 16:22:29 1995
+++ linux/drivers/net/znet.c	Tue Jul  4 11:43:15 1995
@@ -358,7 +358,7 @@
 		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
 	else {
 		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-		unsigned char *buf = (void *)(skb+1);
+		unsigned char *buf = (void *)skb->data;
 		ushort *tx_link = zn.tx_cur - 1;
 		ushort rnd_len = (length + 1)>>1;
 
@@ -565,7 +565,7 @@
 			} else {
 				memcpy(skb_put(skb,pkt_len), zn.rx_cur, pkt_len);
 				if (znet_debug > 6) {
-					unsigned int *packet = (unsigned int *) (skb + 1);
+					unsigned int *packet = (unsigned int *) skb->data;
 					printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
 						   packet[1], packet[2], packet[3]);
 				}
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/53c7,8xx.h linux/drivers/scsi/53c7,8xx.h
--- v1.3.6/linux/drivers/scsi/53c7,8xx.h	Thu Jun 29 19:02:42 1995
+++ linux/drivers/scsi/53c7,8xx.h	Tue Jul  4 07:59:53 1995
@@ -78,9 +78,7 @@
 #define NCR53c7xx_release NULL
 #endif
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
-#define NCR53c7xx {NULL, NULL, generic_proc_info, "NCR53c7xx",          \
+#define NCR53c7xx {NULL, NULL, NULL, "NCR53c7xx",          \
  	PROC_SCSI_NCR53C7xx, "NCR53c{7,8}xx (rel 4)", NCR53c7xx_detect, \
     	NULL, /* info */ NULL, /* command, deprecated */ NULL, 		\
 	NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset,	\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v1.3.6/linux/drivers/scsi/Makefile	Thu Jun 29 19:02:45 1995
+++ linux/drivers/scsi/Makefile	Tue Jul  4 07:59:53 1995
@@ -24,6 +24,14 @@
 	-O2 -fomit-frame-pointer -m486
 
 include ../../.config
+
+.EXPORT_ALL_VARIABLES:
+
+SYMTAB_OBJS = scsi_syms.o
+
+TOPDIR = ../..
+include ../../versions.mk
+
 endif
 
 SCSI_OBJS =
@@ -36,6 +44,10 @@
 ifdef CONFIG_SCSI
 SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
 else
+ifdef CONFIG_MODVERSIONS
+# Create this before we build anything else.
+SCSI_MODULE_VER := scsi_syms.ver
+endif
 SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) scsi_mod.o
 endif
 
@@ -235,17 +247,19 @@
 	mv scriptu.h 53c8xx_u.h
 	rm fake.c
 
-scsi_mod.o: hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
-	$(LD) $(LD_RFLAG) -r -o $@ hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
+scsi_mod.o: $(SCSI_MODULE_VER) hosts.o scsi.o scsi_ioctl.o constants.o \
+		scsicam.o scsi_proc.o $(SYMTAB_OBJS)
+	$(LD) $(LD_RFLAG) -r -o $@ hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o $(SYMTAB_OBJS)
 
-sr_mod.o: sr.o sr_ioctl.o
+sr_mod.o: $(SCSI_MODULE_VER) sr.o sr_ioctl.o
 	$(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o
 
-sd_mod.o: sd.o sd_ioctl.o
+sd_mod.o: $(SCSI_MODULE_VER) sd.o sd_ioctl.o
 	$(LD) $(LD_RFLAG) -r -o $@ sd.o sd_ioctl.o
 
+$(SYMTAB_OBJS): $(SYMTAB_OBJS:.o=.c)
 
-modules: $(SCSI_MODULE_OBJS)
+modules: $(SCSI_MODULE_VER) $(SCSI_MODULE_OBJS)
 	echo $(SCSI_MODULE_OBJS) > ../../modules/SCSI_MODULES
 	(cd ../../modules;for i in $(SCSI_MODULE_OBJS); do ln -sf ../drivers/scsi/$$i .; done)
 
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h
--- v1.3.6/linux/drivers/scsi/aha152x.h	Thu Jun 29 19:02:47 1995
+++ linux/drivers/scsi/aha152x.h	Tue Jul  4 07:59:53 1995
@@ -18,8 +18,6 @@
 int        aha152x_reset(Scsi_Cmnd *);
 int        aha152x_biosparam(Disk *, int, int*);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 /* number of queueable commands
    (unless we support more than 1 cmd_per_lun this should do) */
 #define AHA152X_MAXQUEUE	7		
@@ -29,7 +27,7 @@
 /* Initial value of Scsi_Host entry */
 #define AHA152X       { /* next */		NULL,			    \
 			/* usage_count */  	NULL,			    \
-						generic_proc_info,          \
+						NULL,		            \
 						"aha152x",                  \
 						PROC_SCSI_AHA152X,          \
 			/* name */		AHA152X_REVID, 		    \
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v1.3.6/linux/drivers/scsi/aha1542.c	Thu Jun 29 19:02:47 1995
+++ linux/drivers/scsi/aha1542.c	Tue Jul  4 07:59:53 1995
@@ -15,6 +15,7 @@
  */
 
 #ifdef MODULE
+#include <linux/autoconf.h>
 #include <linux/module.h>
 #endif
 
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/aha1542.h linux/drivers/scsi/aha1542.h
--- v1.3.6/linux/drivers/scsi/aha1542.h	Thu Jun 29 19:02:47 1995
+++ linux/drivers/scsi/aha1542.h	Tue Jul  4 07:59:53 1995
@@ -136,8 +136,6 @@
 int aha1542_reset(Scsi_Cmnd *);
 int aha1542_biosparam(Disk *, int, int*);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #define AHA1542_MAILBOXES 8
 #define AHA1542_SCATTER 16
 #define AHA1542_CMDLUN 1
@@ -147,7 +145,7 @@
 #endif
 
 #define AHA1542 {  NULL, NULL,				\
-		     generic_proc_info,                 \
+		     NULL,		                \
 		     "aha1542",                         \
 		     PROC_SCSI_AHA1542,                 \
 		     "Adaptec 1542", 			\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/aha1740.h linux/drivers/scsi/aha1740.h
--- v1.3.6/linux/drivers/scsi/aha1740.h	Thu Jun 29 19:02:47 1995
+++ linux/drivers/scsi/aha1740.h	Tue Jul  4 07:59:53 1995
@@ -159,8 +159,6 @@
 int aha1740_reset(Scsi_Cmnd *);
 int aha1740_biosparam(Disk *, int, int*);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #define AHA1740_ECBS 32
 #define AHA1740_SCATTER 16
 
@@ -169,7 +167,7 @@
 #endif
 
 #define AHA1740 {NULL,	NULL,				\
-		   generic_proc_info,                   \
+		   NULL,		                \
 		   "aha1740",                           \
 		   PROC_SCSI_AHA1740,                   \
 		   "Adaptec 174x (EISA)",		\
Binary files v1.3.6/linux/drivers/scsi/aic7770 and linux/drivers/scsi/aic7770 differ
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- v1.3.6/linux/drivers/scsi/aic7xxx.h	Thu Jun 29 19:02:48 1995
+++ linux/drivers/scsi/aic7xxx.h	Tue Jul  4 07:59:53 1995
@@ -47,7 +47,7 @@
 #define AIC7XXX	{						\
 	NULL,							\
 	NULL,							\
-	generic_proc_info,					\
+	NULL,							\
 	"aic7xxx",						\
 	PROC_SCSI_AIC7XXX,					\
 	NULL,							\
@@ -77,7 +77,5 @@
 extern int aic7xxx_reset(Scsi_Cmnd *);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
-
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
 
 #endif /* _aic7xxx_h */
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c
--- v1.3.6/linux/drivers/scsi/buslogic.c	Thu Jun 29 19:02:48 1995
+++ linux/drivers/scsi/buslogic.c	Tue Jul  4 07:52:51 1995
@@ -1515,8 +1515,8 @@
 		ip[1] = 32;
 	    }
 #else
-	    ip[0] = 256;
-	    ip[1] = 64;
+	    ip[0] = 255;
+	    ip[1] = 63;
 #endif
 	} else {
 	    ip[0] = 128;
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/buslogic.h linux/drivers/scsi/buslogic.h
--- v1.3.6/linux/drivers/scsi/buslogic.h	Thu Jun 29 19:02:49 1995
+++ linux/drivers/scsi/buslogic.h	Tue Jul  4 07:59:53 1995
@@ -12,10 +12,8 @@
 int buslogic_reset(Scsi_Cmnd *);
 int buslogic_biosparam(Disk *, int, int *);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #define BUSLOGIC { NULL, NULL,			\
-		   generic_proc_info,           \
+		   NULL,		        \
 		   "buslogic",                  \
 		   PROC_SCSI_BUSLOGIC,          \
 		   "BusLogic",			\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/constants.c linux/drivers/scsi/constants.c
--- v1.3.6/linux/drivers/scsi/constants.c	Thu Jun 29 19:02:49 1995
+++ linux/drivers/scsi/constants.c	Tue Jul  4 07:59:53 1995
@@ -3,6 +3,20 @@
  * etc.
  */
 
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <linux/config.h>
 #include "../block/blk.h"
 #include <linux/kernel.h>
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/eata_pio.h linux/drivers/scsi/eata_pio.h
--- v1.3.6/linux/drivers/scsi/eata_pio.h	Fri Jun 30 16:22:29 1995
+++ linux/drivers/scsi/eata_pio.h	Tue Jul  4 07:59:53 1995
@@ -73,8 +73,6 @@
 #define eata_pio_release NULL  
 #endif
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 
 #define EATA_PIO {		     \
     NULL, NULL,			 \
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v1.3.6/linux/drivers/scsi/fdomain.c	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/fdomain.c	Wed Jul  5 11:21:10 1995
@@ -1,10 +1,10 @@
 /* fdomain.c -- Future Domain TMC-16x0 SCSI driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Fri Jun 23 17:07:09 1995 by r.faith@ieee.org
+ * Revised: Tue Jul  4 13:58:47 1995 by r.faith@ieee.org
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
  *
- * $Id: fdomain.c,v 5.31 1995/06/23 21:07:16 faith Exp $
+ * $Id: fdomain.c,v 5.33 1995/07/04 18:59:49 faith Exp $
 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -206,7 +206,7 @@
 #include <linux/bios32.h>
 #include <linux/pci.h>
   
-#define VERSION          "$Revision: 5.31 $"
+#define VERSION          "$Revision: 5.33 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
@@ -679,12 +679,12 @@
    return 1;			/* success */
 }
 
-/* PCI detection function: int fdomain_36c70_detect(int* irq, int* iobase)
-   This function gets the Interrupt Level and I/O base address from the PCI
-   configuration registers.  The I/O base address is masked with 0xfff8
-   since on my card the address read from the PCI config registers is off
-   by one from the actual I/O base address necessary for accessing the
-   status and control registers on the card (PCI config register gives
+/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int*
+   iobase) This function gets the Interrupt Level and I/O base address from
+   the PCI configuration registers.  The I/O base address is masked with
+   0xfff8 since on my card the address read from the PCI config registers
+   is off by one from the actual I/O base address necessary for accessing
+   the status and control registers on the card (PCI config register gives
    0xf801, actual address is 0xf800).  This is likely a bug in the FD
    config code that writes to the PCI registers, however using a mask
    should be safe since I think the scan done by the card to determine the
@@ -696,20 +696,20 @@
    detection function.  Comments, bug reports, etc... on this function
    should be sent to mckinley@msupa.pa.msu.edu - James T. McKinley.  */
 
-#ifdef PCI_CONFIG
-static int fdomain_36c70_detect( int *irq, int *iobase )
+#ifdef CONFIG_PCI
+static int fdomain_pci_bios_detect( int *irq, int *iobase )
 {
    int              error;
    unsigned char    pci_bus, pci_dev_fn;    /* PCI bus & device function */
    unsigned char    pci_irq;                /* PCI interrupt line */
-   unsigned long    pci_base;               /* PCI I/O base address */
+   unsigned int     pci_base;               /* PCI I/O base address */
    unsigned short   pci_vendor, pci_device; /* PCI vendor & device IDs */
 
    /* If the PCI BIOS doesn't exist, use the old-style detection routines.
       Otherwise, get the I/O base address and interrupt from the PCI config
       registers. */
    
-   if (!pcibios_present()) return fdomain_pci_detect( irq, iobase );
+   if (!pcibios_present()) return fdomain_pci_nobios_detect( irq, iobase );
 
 #if DEBUG_DETECT
    /* Tell how to print a list of the known PCI devices from bios32 and
@@ -857,7 +857,7 @@
    if (!PCI_bus) {
       flag = fdomain_isa_detect( &interrupt_level, &port_base );
    } else {
-#ifdef PCI_CONFIG
+#ifdef CONFIG_PCI
       flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
 #else
       flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );
@@ -868,7 +868,7 @@
 #if DEBUG_DETECT
       printk( " FAILED: NO PORT\n" );
 #endif
-#ifdef PCI_CONFIG
+#ifdef CONFIG_PCI
       printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" );
       printk( "Send mail to mckinley@msupa.pa.msu.edu.\n" );
 #endif
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/fdomain.h linux/drivers/scsi/fdomain.h
--- v1.3.6/linux/drivers/scsi/fdomain.h	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/fdomain.h	Tue Jul  4 07:59:53 1995
@@ -33,11 +33,9 @@
 int        fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
 int        fdomain_16x0_biosparam( Disk *, int, int * );
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #define FDOMAIN_16X0 { NULL,                             \
 		       NULL,                             \
-		       generic_proc_info,                \
+		       NULL,		                 \
 		       "fdomain",                        \
 		       PROC_SCSI_FUTURE_DOMAIN,          \
 		       NULL,				 \
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h
--- v1.3.6/linux/drivers/scsi/g_NCR5380.h	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/g_NCR5380.h	Tue Jul  4 07:59:53 1995
@@ -53,7 +53,7 @@
 
 #ifdef HOSTS_C
 
-#define GENERIC_NCR5380 {NULL, NULL, generic_proc_info, "g_NCR5380",    \
+#define GENERIC_NCR5380 {NULL, NULL, NULL, "g_NCR5380",    \
 	PROC_SCSI_GENERIC_NCR5380, "Trantor T128/T128F/T228", 		\
 	generic_NCR5380_detect, NULL, NULL, NULL, 			\
 	generic_NCR5380_queue_command, generic_NCR5380_abort, 		\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.6/linux/drivers/scsi/hosts.c	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/hosts.c	Tue Jul  4 07:59:53 1995
@@ -16,6 +16,20 @@
  *  hosts currently present in the system.
  */
 
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <linux/config.h>
 #include "../block/blk.h"
 #include <linux/kernel.h>
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- v1.3.6/linux/drivers/scsi/in2000.h	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/in2000.h	Tue Jul  4 07:59:53 1995
@@ -101,15 +101,13 @@
 int in2000_reset(Scsi_Cmnd *);
 int in2000_biosparam(Disk *, int, int*);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #ifndef NULL
 	#define NULL 0
 #endif
 
 /* next may be "SG_NONE" or "SG_ALL" or nr. of (1k) blocks per R/W Cmd. */
 #define IN2000_SG SG_ALL
-#define IN2000 {NULL, NULL, generic_proc_info,  \
+#define IN2000 {NULL, NULL, NULL,  \
 		"in2000", PROC_SCSI_IN2000, \
 		"Always IN2000", in2000_detect, NULL,	\
 		NULL, in2000_command,		\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/pas16.h linux/drivers/scsi/pas16.h
--- v1.3.6/linux/drivers/scsi/pas16.h	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/pas16.h	Tue Jul  4 07:59:53 1995
@@ -120,8 +120,6 @@
 int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int pas16_reset(Scsi_Cmnd *);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #ifndef NULL
 #define NULL 0
 #endif
@@ -142,7 +140,7 @@
 
 #ifdef HOSTS_C
 
-#define MV_PAS16 {NULL, NULL, generic_proc_info, "pas16",  PROC_SCSI_PAS16, \
+#define MV_PAS16 {NULL, NULL, NULL, "pas16",  PROC_SCSI_PAS16, \
 	"Pro Audio Spectrum-16 SCSI", 		\
 	pas16_detect, NULL, NULL,					\
 	NULL, pas16_queue_command, pas16_abort, pas16_reset, NULL, 	\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/qlogic.h linux/drivers/scsi/qlogic.h
--- v1.3.6/linux/drivers/scsi/qlogic.h	Thu Jun 29 19:02:50 1995
+++ linux/drivers/scsi/qlogic.h	Tue Jul  4 07:59:53 1995
@@ -9,8 +9,6 @@
 int qlogic_reset(Scsi_Cmnd *);
 int qlogic_biosparam(Disk *,int,int[]);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #ifndef NULL
 #define NULL (0)
 #endif
@@ -18,7 +16,7 @@
 #define QLOGIC {		\
 	NULL,			\
 	NULL,			\
-	generic_proc_info,      \
+	NULL,		        \
 	"qlogic",               \
 	PROC_SCSI_QLOGIC,       \
 	NULL,			\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.6/linux/drivers/scsi/scsi.c	Thu Jun 29 19:02:51 1995
+++ linux/drivers/scsi/scsi.c	Tue Jul  4 07:59:54 1995
@@ -20,6 +20,19 @@
  *  Native multichannel and wide scsi support added 
  *  by Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
  */
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <asm/system.h>
 #include <linux/sched.h>
@@ -36,16 +49,9 @@
 #include "hosts.h"
 #include "constants.h"
 
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-
 #undef USE_STATIC_SCSI_MEMORY
 
+
 /*
 static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.5 1993/09/24 12:45:18 drew Exp drew $";
 */
@@ -511,6 +517,8 @@
 				SDpnt->manufacturer = SCSI_MAN_NEC;
 			} else if (!strncmp(scsi_result+8,"TOSHIBA",7))
 			    SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+                        else if (!strncmp(scsi_result+8,"SONY",4))
+                            SDpnt->manufacturer = SCSI_MAN_SONY;
 			else
 			    SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
 			
@@ -2865,55 +2873,28 @@
 }
 #endif
 
-
 #ifdef MODULE
-/* 
-extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
-extern int        scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
-*/
 
-struct symbol_table scsi_symbol_table = {
-#include <linux/symtab_begin.h>
-#ifdef CONFIG_MODVERSIONS
-    { (void *)1 /* Version version :-) */, "_Using_Versions" },
-#endif
-    X(scsi_register_module),
-    X(scsi_unregister_module),
-    X(scsi_free),
-    X(scsi_malloc),
-    X(scsi_register),
-    X(scsi_unregister),
-    X(scsicam_bios_param),
-    X(allocate_device),
-    X(scsi_do_cmd),
-    X(scsi_command_size),
-    X(scsi_init_malloc),
-    X(scsi_init_free),
-    X(scsi_ioctl),
-    X(print_command),
-    X(print_sense),
-    X(dma_free_sectors),
-    X(kernel_scsi_ioctl),
-    X(need_isa_buffer),
-    X(request_queueable),
-/*
- * These are here only while I debug the rest of the scsi stuff.
- */
-    X(scsi_hostlist),
-    X(scsi_hosts),
-    X(scsi_devicelist),
-    X(scsi_devices),
-
-    /********************************************************
-     * Do not add anything below this line,
-     * as the stacked modules depend on this!
-     */
-#include <linux/symtab_end.h>
-};
 
 char kernel_version[] = UTS_RELEASE;
 
+extern struct symbol_table scsi_symbol_table;
+
 int init_module(void) {
+
+#if CONFIG_PROC_FS 
+    /*
+     * This will initialize the directory so that we do not have
+     * random crap in there.
+     */
+    build_proc_dir_entries();
+#endif
+
+    /*
+     * This makes the real /proc/scsi visible.
+     */
+    dispatch_scsi_info_ptr = dispatch_scsi_info;
+
     timer_table[SCSI_TIMER].fn = scsi_main_timeout;
     timer_table[SCSI_TIMER].expires = 0;
     register_symtab(&scsi_symbol_table);
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v1.3.6/linux/drivers/scsi/scsi.h	Fri Jun 30 16:22:29 1995
+++ linux/drivers/scsi/scsi.h	Sat Jul  1 19:05:59 1995
@@ -261,6 +261,7 @@
 #define SCSI_MAN_NEC         1
 #define SCSI_MAN_TOSHIBA     2
 #define SCSI_MAN_NEC_OLDCDR  3
+#define SCSI_MAN_SONY        4
 
 /*
  *  As the scsi do command functions are intelligent, and may need to
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- v1.3.6/linux/drivers/scsi/scsi_debug.c	Thu Jun 29 19:02:52 1995
+++ linux/drivers/scsi/scsi_debug.c	Tue Jul  4 07:59:54 1995
@@ -8,11 +8,13 @@
  */
 
 #ifdef MODULE
+#include <linux/autoconf.h>
 #include <linux/module.h>
 #endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/errno.h>
 #include <linux/timer.h>
 #include <linux/head.h>
 #include <linux/types.h>
@@ -598,6 +600,36 @@
 {
     static char buffer[] = " ";         /* looks nicer without anything here */
     return buffer;
+}
+
+/* generic_proc_info
+ * Used if the driver currently has no own support for /proc/scsi
+ */
+int scsi_debug_proc_info(char *buffer, char **start, off_t offset, 
+		     int length, int inode, int inout)
+{
+    int len, pos, begin;
+
+    if(inout == 1)
+	return(-ENOSYS);  /* This is a no-op */
+    
+    begin = 0;
+    pos = len = sprintf(buffer, 
+			"This driver is not a real scsi driver, but it plays one on TV.\n"
+                        "It is very handy for debugging specific problems because you\n"
+                        "can simulate a variety of error conditions\n");
+    if(pos < offset)
+    {
+	len = 0;
+	begin = pos;
+    }
+    
+    *start = buffer + (offset - begin);   /* Start of wanted data */
+    len -= (offset - begin);
+    if(len > length)
+	len = length;
+    
+    return(len);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi_debug.h linux/drivers/scsi/scsi_debug.h
--- v1.3.6/linux/drivers/scsi/scsi_debug.h	Thu Jun 29 19:02:52 1995
+++ linux/drivers/scsi/scsi_debug.h	Tue Jul  4 07:59:54 1995
@@ -8,16 +8,15 @@
 int scsi_debug_abort(Scsi_Cmnd *);
 int scsi_debug_biosparam(Disk *, int, int[]);
 int scsi_debug_reset(Scsi_Cmnd *);
-
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
+int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
+ 
 #ifndef NULL
 	#define NULL 0
 #endif
 
 #define SCSI_DEBUG_MAILBOXES 8
 
-#define SCSI_DEBUG {NULL, NULL, generic_proc_info, "scsi_debug", \
+#define SCSI_DEBUG {NULL, NULL, scsi_debug_proc_info, "scsi_debug", \
 		PROC_SCSI_SCSI_DEBUG, "SCSI DEBUG", scsi_debug_detect, NULL, \
 		NULL, scsi_debug_command,		\
 		scsi_debug_queuecommand,			\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v1.3.6/linux/drivers/scsi/scsi_ioctl.c	Thu Jun 29 19:02:52 1995
+++ linux/drivers/scsi/scsi_ioctl.c	Tue Jul  4 07:59:54 1995
@@ -1,3 +1,17 @@
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/system.h>
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v1.3.6/linux/drivers/scsi/scsi_proc.c	Thu Jun 29 19:02:52 1995
+++ linux/drivers/scsi/scsi_proc.c	Tue Jul  4 07:59:54 1995
@@ -13,6 +13,19 @@
  * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
  */
 
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -39,13 +52,20 @@
     struct Scsi_Host *hpnt = scsi_hostlist;
     uint x = 0;
 
+    /*
+     * Danger - this has massive race conditions in it.
+     * If the someone adds/removes entries from the scsi chain
+     * while someone else is looking at /proc/scsi, unpredictable
+     * results will be obtained.
+     */
     while (tpnt) {
 	if (ino == tpnt->low_ino) 
 		return(x);
 	x += 3;
 	while (hpnt) {
+            if(hpnt->hostt == tpnt) /* This gives us the correct index */
+                x++;
 	    hpnt = hpnt->next;
-	    x++;
 	}
 	tpnt = tpnt->next;
     }
@@ -86,16 +106,31 @@
 extern int dispatch_scsi_info(int ino, char *buffer, char **start, 
 			      off_t offset, int length, int func)
 {
+    int retval;
     struct Scsi_Host *hpnt = scsi_hostlist;
 
     if(func != 2) {    
-	if(ino == PROC_SCSI_SCSI) 
-	    return(scsi_proc_info(buffer, start, offset, length, 
-				  hpnt->host_no, func));
+	if(ino == PROC_SCSI_SCSI) {
+            /*
+             * If there are no hosts, tell the user to go away.
+             */
+            if( hpnt == NULL ) {
+                return (-ENOSYS);
+            }
+
+	    retval = scsi_proc_info(buffer, start, offset, length, 
+				  hpnt->host_no, func);
+            return(retval);
+        }
+
 	while(hpnt) {
 	    if (ino == (hpnt->host_no + PROC_SCSI_FILE)) 
-		return(hpnt->hostt->proc_info(buffer, start, offset, length, 
-					      hpnt->host_no, func));
+                if(hpnt->hostt->proc_info == NULL)
+                    return generic_proc_info(buffer, start, offset, length, 
+                                             hpnt->host_no, func);
+                else
+                    return(hpnt->hostt->proc_info(buffer, start, offset, length, 
+                                                  hpnt->host_no, func));
 	    hpnt = hpnt->next;
 	}
 	return(-EBADF);
@@ -118,7 +153,7 @@
 void build_proc_dir_hba_entries(void)
 {
     Scsi_Host_Template *tpnt = scsi_hosts;
-    struct Scsi_Host *hpnt = scsi_hostlist;
+    struct Scsi_Host *hpnt;
     static char names[PROC_SCSI_LAST - PROC_SCSI_FILE][3];
     uint x, y;
     
@@ -131,8 +166,9 @@
 	scsi_hba_dir[x].low_ino = PROC_SCSI;
 	scsi_hba_dir[x].namelen = 2;
 	scsi_hba_dir[x++].name = "..";
-	    
-	while (hpnt) {
+
+        hpnt = scsi_hostlist;
+        while (hpnt) {
 	    if (tpnt == hpnt->hostt) {
 		scsi_hba_dir[x].low_ino = PROC_SCSI_FILE + hpnt->host_no;
 		scsi_hba_dir[x].namelen = sprintf(names[y],"%d",hpnt->host_no);
@@ -142,8 +178,8 @@
 	    }
 	    hpnt = hpnt->next;
 	}
-	
-	scsi_hba_dir[x].low_ino = 0;
+
+        scsi_hba_dir[x].low_ino = 0;
 	scsi_hba_dir[x].namelen = 0;
 	scsi_hba_dir[x++].name = NULL;
 	tpnt = tpnt->next;
@@ -168,11 +204,13 @@
     scsi_dir[2].low_ino = PROC_SCSI_SCSI;
     scsi_dir[2].namelen = 4;
     scsi_dir[2].name = "scsi";
-    for(x = 3; x < newnum + 3; x++, tpnt = tpnt->next) {
+
+    for(x = 3; x < newnum + 3; x++, tpnt = tpnt->next) { 
 	scsi_dir[x].low_ino = tpnt->low_ino;
 	scsi_dir[x].namelen = strlen(tpnt->procname);
 	scsi_dir[x].name = tpnt->procname;
     }
+
     scsi_dir[x].low_ino = 0;
     scsi_dir[x].namelen = 0;
     scsi_dir[x].name = NULL;
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v1.3.6/linux/drivers/scsi/scsi_syms.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/scsi_syms.c	Tue Jul  4 07:59:54 1995
@@ -0,0 +1,84 @@
+
+#include <linux/autoconf.h>
+
+/*
+ * We should not even be trying to compile this if we are not doing
+ * a module.
+ */
+#ifndef MODULE
+#error Go away.
+#endif
+
+/*
+ * Even though we are building a module, we need to undef this, since
+ * we are building a symbol table to be used by other modules.  For
+ * the symbol table to build properly, we need to undefine this.
+ */
+#undef MODULE
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+#include "sd.h"
+/*
+ * This source file contains the symbol table used by scsi loadable
+ * modules.
+ */
+
+extern void print_command (unsigned char *command);
+extern void print_sense(char * devclass, Scsi_Cmnd * SCpnt);
+
+struct symbol_table scsi_symbol_table = {
+#include <linux/symtab_begin.h>
+#ifdef CONFIG_MODVERSIONS
+    { (void *)1 /* Version version :-) */, 
+	SYMBOL_NAME_STR("Using_Versions") },
+#endif
+    X(scsi_register_module),
+    X(scsi_unregister_module),
+    X(scsi_free),
+    X(scsi_malloc),
+    X(scsi_register),
+    X(scsi_unregister),
+    X(scsicam_bios_param),
+    X(allocate_device),
+    X(scsi_do_cmd),
+    X(scsi_command_size),
+    X(scsi_init_malloc),
+    X(scsi_init_free),
+    X(scsi_ioctl),
+    X(print_command),
+    X(print_sense),
+    X(dma_free_sectors),
+    X(kernel_scsi_ioctl),
+    X(need_isa_buffer),
+    X(request_queueable),
+/*
+ * These are here only while I debug the rest of the scsi stuff.
+ */
+    X(scsi_hostlist),
+    X(scsi_hosts),
+    X(scsi_devicelist),
+    X(scsi_devices),
+
+    /********************************************************
+     * Do not add anything below this line,
+     * as the stacked modules depend on this!
+     */
+#include <linux/symtab_end.h>
+};
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- v1.3.6/linux/drivers/scsi/scsicam.c	Thu Jun 29 19:02:52 1995
+++ linux/drivers/scsi/scsicam.c	Tue Jul  4 07:59:54 1995
@@ -10,6 +10,20 @@
  * For more information, please consult the SCSI-CAM draft.
  */
 
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <linux/fs.h>
 #include <linux/genhd.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.3.6/linux/drivers/scsi/sd.c	Thu Jun 29 19:02:52 1995
+++ linux/drivers/scsi/sd.c	Tue Jul  4 07:59:54 1995
@@ -16,6 +16,20 @@
  *       low-level scsi drivers.
  */
 
+#ifdef MODULE
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+/*
+ * This is a variable in scsi.c that is set when we are processing something
+ * after boot time.  By definition, this is true when we are a loadable module
+ * ourselves.
+ */
+#define MODULE_FLAG 1
+#else
+#define MODULE_FLAG scsi_loadable_module_flag
+#endif /* MODULE */
+
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -1174,7 +1188,7 @@
 	    rscsi_disks[i].device)
 	{
 	    i = sd_init_onedisk(i);
-	    if (scsi_loadable_module_flag 
+	    if (MODULE_FLAG
 		&& !rscsi_disks[i].has_part_table) {
 		sd_sizes[i << 4] = rscsi_disks[i].capacity;
 		revalidate_scsidisk(i << 4, 0);
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/sd_ioctl.c linux/drivers/scsi/sd_ioctl.c
--- v1.3.6/linux/drivers/scsi/sd_ioctl.c	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/sd_ioctl.c	Tue Jul  4 07:59:54 1995
@@ -1,3 +1,9 @@
+#ifdef MODULE
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* MODULE */
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/seagate.h linux/drivers/scsi/seagate.h
--- v1.3.6/linux/drivers/scsi/seagate.h	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/seagate.h	Tue Jul  4 07:59:54 1995
@@ -26,9 +26,7 @@
 
 int seagate_st0x_biosparam(Disk *, int, int*);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
-#define SEAGATE_ST0X  {  NULL, NULL, generic_proc_info, "seagate", \
+#define SEAGATE_ST0X  {  NULL, NULL, NULL, "seagate", \
 			 PROC_SCSI_SEAGATE, NULL, seagate_st0x_detect, 	\
 			 NULL, 						\
 			 seagate_st0x_info, seagate_st0x_command,  	\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.3.6/linux/drivers/scsi/sg.c	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/sg.c	Tue Jul  4 07:59:54 1995
@@ -7,6 +7,7 @@
  *  Borrows code from st driver.
  */
 #ifdef MODULE
+#include <linux/autoconf.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #endif /* MODULE */
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.3.6/linux/drivers/scsi/sr.c	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/sr.c	Tue Jul  4 07:59:54 1995
@@ -15,6 +15,12 @@
  *	    low-level scsi drivers.
  */
 
+#ifdef MODULE
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* MODULE */
+
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -450,7 +456,32 @@
 	    scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
 	}
 	break;
-	
+
+    case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.frmug.fr.net> */
+#ifdef DEBUG
+        printk("sr_photocd: use SONY code\n");
+#endif
+        memset(buf,0,40);
+        *((unsigned long*)buf)   = 0x0;   /* we send nothing...     */
+        *((unsigned long*)buf+1) = 0x0c;  /* and receive 0x0c bytes */
+        cmd[0] = 0x43; /* Read TOC */
+        cmd[8] = 0x0c;
+        cmd[9] = 0x40;
+        rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+                               SCSI_IOCTL_SEND_COMMAND, buf);
+        
+        if ((rc != 0) || ((rec[0] << 8) + rec[1] != 0x0a)) {
+            printk("sr_photocd: ioctl error (SONY): 0x%x\n",rc);
+            break;
+        }
+        sector = rec[11] + (rec[10] << 8) + (rec[9] << 16) + (rec[8] << 24);
+        is_xa = !!sector;
+#ifdef DEBUG
+        if (sector)
+            printk ("sr_photocd: multisession CD detected. start: %lu\n",sector);
+#endif
+        break;
+        
     case SCSI_MAN_NEC_OLDCDR:
     case SCSI_MAN_UNKNOWN:
     default:
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v1.3.6/linux/drivers/scsi/sr_ioctl.c	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/sr_ioctl.c	Tue Jul  4 07:59:54 1995
@@ -1,3 +1,9 @@
+#ifdef MODULE
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* MODULE */
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.3.6/linux/drivers/scsi/st.c	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/st.c	Tue Jul  4 07:59:54 1995
@@ -14,6 +14,7 @@
   Last modified: Sat Feb 18 10:51:25 1995 by root@kai.home
 */
 #ifdef MODULE
+#include <linux/autoconf.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #endif /* MODULE */
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/t128.h linux/drivers/scsi/t128.h
--- v1.3.6/linux/drivers/scsi/t128.h	Thu Jun 29 19:02:53 1995
+++ linux/drivers/scsi/t128.h	Tue Jul  4 07:59:54 1995
@@ -117,7 +117,7 @@
 
 #ifdef HOSTS_C
 
-#define TRANTOR_T128 {NULL, NULL, generic_proc_info, "t128", PROC_SCSI_T128, \
+#define TRANTOR_T128 {NULL, NULL, NULL, "t128", PROC_SCSI_T128, \
 	"Trantor T128/T128F/T228", t128_detect, NULL,  \
 	NULL,							\
 	NULL, t128_queue_command, t128_abort, t128_reset, NULL, 	\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v1.3.6/linux/drivers/scsi/u14-34f.h	Thu Jun 29 19:02:54 1995
+++ linux/drivers/scsi/u14-34f.h	Tue Jul  4 07:59:54 1995
@@ -10,14 +10,12 @@
 int u14_34f_reset(Scsi_Cmnd *);
 int u14_34f_biosparam(Disk *, int, int *);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int); 
-
 #define U14_34F_VERSION "2.01.00"
 
 #define ULTRASTOR_14_34F {                                            \
 		NULL, /* Ptr for modules */                           \
 		NULL, /* usage count for modules */		      \
-		generic_proc_info,                                    \
+		NULL,		                                      \
 		"u14_34f",                                            \
 		PROC_SCSI_U14_34F,                                    \
 		"UltraStor 14F/34F rev. " U14_34F_VERSION " ",        \
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/ultrastor.h linux/drivers/scsi/ultrastor.h
--- v1.3.6/linux/drivers/scsi/ultrastor.h	Thu Jun 29 19:02:54 1995
+++ linux/drivers/scsi/ultrastor.h	Tue Jul  4 07:59:54 1995
@@ -20,7 +20,6 @@
 int ultrastor_reset(Scsi_Cmnd *);
 int ultrastor_biosparam(Disk *, int, int *);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
 
 #define ULTRASTOR_14F_MAX_SG 16
 #define ULTRASTOR_24F_MAX_SG 33
@@ -32,7 +31,7 @@
 
 
 #define ULTRASTOR_14F { NULL, NULL, /* Ptr for modules*/ \
-			  generic_proc_info,            \
+			  NULL,                         \
 			  "ultrastor",                  \
 			  PROC_SCSI_ULTRASTOR,          \
 			  "UltraStor 14F/24F/34F", 	\
diff -u --recursive --new-file v1.3.6/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h
--- v1.3.6/linux/drivers/scsi/wd7000.h	Thu Jun 29 19:02:54 1995
+++ linux/drivers/scsi/wd7000.h	Tue Jul  4 07:59:54 1995
@@ -19,8 +19,6 @@
 int wd7000_reset(Scsi_Cmnd *);
 int wd7000_biosparam(Disk *, int, int*);
 
-extern int generic_proc_info(char *, char **, off_t, int, int, int);
-
 #ifndef NULL
 #define NULL 0L
 #endif
@@ -40,7 +38,7 @@
 #define WD7000_SG   16
 
 #define WD7000 { NULL, NULL,            \
-	generic_proc_info,              \
+	NULL,		                \
 	"wd7000",                       \
 	PROC_SCSI_7000FASST,            \
 	"Western Digital WD-7000",      \
diff -u --recursive --new-file v1.3.6/linux/fs/Makefile linux/fs/Makefile
--- v1.3.6/linux/fs/Makefile	Mon Jun  5 12:29:08 1995
+++ linux/fs/Makefile	Sat Jul  1 19:00:18 1995
@@ -7,7 +7,7 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv
+SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv smbfs
 
 ifdef CONFIG_MINIX_FS
 FS_SUBDIRS := $(FS_SUBDIRS) minix
@@ -61,6 +61,12 @@
 FS_SUBDIRS := $(FS_SUBDIRS) sysv
 else
 MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) sysv
+endif
+
+ifdef CONFIG_SMB_FS
+FS_SUBDIRS := $(FS_SUBDIRS) smbfs
+else
+MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) smbfs
 endif
 
 ifdef CONFIG_HPFS_FS
diff -u --recursive --new-file v1.3.6/linux/fs/exec.c linux/fs/exec.c
--- v1.3.6/linux/fs/exec.c	Tue Jun 27 14:11:39 1995
+++ linux/fs/exec.c	Thu Jul  6 13:24:46 1995
@@ -515,7 +515,8 @@
 	int ch;
 	char * name;
 
-	current->dumpable = 1;
+	if (current->euid == current->uid && current->egid == current->gid)
+		current->dumpable = 1;
 	name = bprm->filename;
 	for (i=0; (ch = *(name++)) != '\0';) {
 		if (ch == '/')
diff -u --recursive --new-file v1.3.6/linux/fs/filesystems.c linux/fs/filesystems.c
--- v1.3.6/linux/fs/filesystems.c	Sun Jul 24 17:39:44 1994
+++ linux/fs/filesystems.c	Sat Jul  1 19:00:18 1995
@@ -20,6 +20,7 @@
 #include <linux/iso_fs.h>
 #include <linux/sysv_fs.h>
 #include <linux/hpfs_fs.h>
+#include <linux/smb_fs.h>
 
 extern void device_setup(void);
 
@@ -71,6 +72,11 @@
 #ifdef CONFIG_NFS_FS
 	register_filesystem(&(struct file_system_type)
 		{nfs_read_super, "nfs", 0, NULL});
+#endif
+
+#ifdef CONFIG_SMB_FS
+	register_filesystem(&(struct file_system_type)
+		{smb_read_super, "smbfs", 0, NULL});
 #endif
 
 #ifdef CONFIG_ISO9660_FS
diff -u --recursive --new-file v1.3.6/linux/fs/isofs/dir.c linux/fs/isofs/dir.c
--- v1.3.6/linux/fs/isofs/dir.c	Fri Jun  9 12:08:41 1995
+++ linux/fs/isofs/dir.c	Wed Jul  5 11:27:08 1995
@@ -137,6 +137,19 @@
 		printk("Block, offset, f_pos: %x %x %x\n",
 		       block, offset, filp->f_pos);
 #endif
+		/* Next directory_record on next CDROM sector */
+		if (offset >= bufsize) {
+			brelse(bh);
+			offset = 0;
+			block = isofs_bmap(inode, (filp->f_pos) >> bufbits);
+			if (!block)
+				return 0;
+			bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size);
+			if (!bh)
+				return 0;
+			continue;
+		}
+
 		de = (struct iso_directory_record *) (bh->b_data + offset);
 		inode_number = (block << bufbits) + (offset & (bufsize - 1));
 
diff -u --recursive --new-file v1.3.6/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v1.3.6/linux/fs/proc/inode.c	Thu Jun 29 19:02:54 1995
+++ linux/fs/proc/inode.c	Thu Jul  6 13:24:46 1995
@@ -170,6 +170,15 @@
 		inode->i_op = &proc_scsi_inode_operations;
 		return;
 	}
+	/*
+	 * Special hook used when scsi is not present.
+	 */
+	if (ino == PROC_SCSI_NOT_PRESENT) {
+		inode->i_mode = S_IFREG | S_IRUGO | S_IXUGO;
+		inode->i_op = &proc_scsi_inode_operations;
+		return;
+	}
+
 	/* files within /proc/scsi */
 	if ((ino > PROC_SCSI_SCSI) && (ino < PROC_SCSI_FILE)) {
 	        inode->i_nlink = 2;
@@ -221,7 +230,8 @@
 		return;
 	}
 	ino &= 0x0000ffff;
-	if (p->dumpable && p->uid == p->euid && p->gid == p->egid) {
+	if (ino == PROC_PID_INO ||
+	    (p->dumpable && p->uid == p->euid && p->gid == p->egid)) {
 		inode->i_uid = p->uid;
 		inode->i_gid = p->gid;
 	}
diff -u --recursive --new-file v1.3.6/linux/fs/proc/scsi.c linux/fs/proc/scsi.c
--- v1.3.6/linux/fs/proc/scsi.c	Fri Jun 30 16:22:30 1995
+++ linux/fs/proc/scsi.c	Tue Jul  4 07:59:54 1995
@@ -89,30 +89,50 @@
     { 0, 0, NULL }
 };
 
-inline static uint count_dir_entries(uint inode)
+inline static uint count_dir_entries(uint inode, uint *num)
 {
     struct proc_dir_entry *dir;
-    uint i = 0;
-    
+    uint index, flag;
+
+    (uint) *num = flag = index = 0;    
     
     if(dispatch_scsi_info_ptr)
-	if (inode <= PROC_SCSI_SCSI_DEBUG)
+    {
+	if (inode == PROC_SCSI) { 
 	    dir = scsi_dir;
-	else
+            while(dir[(uint)*num].low_ino)
+                (*num)++;
+        } else {
 	    dir = scsi_hba_dir;
-    else dir = scsi_dir2;
-    
-    while(dir[i].low_ino)
-	i++;
-    
-    return(i);
+            while(dir[index].low_ino || dir[index].low_ino <= PROC_SCSI_LAST) {
+                if(dir[index].low_ino == inode)
+                    flag = 1;
+                if(dir[index].low_ino == 0) {
+                    if(flag == 1)
+                        break;
+                    else
+                        *num = 0;
+                } else {
+                    (*num)++;
+                }
+                index++;
+            }
+            return(index - (*num));
+        }
+    }
+    else {
+        dir = scsi_dir2;
+        while(dir[(uint)*num].low_ino)
+            (*num)++;
+    }   
+    return(0);
 }
 
 static int proc_lookupscsi(struct inode * dir, const char * name, int len,
 			   struct inode ** result)
 {
     struct proc_dir_entry *de = NULL;
-    
+
     *result = NULL;
     if (!dir)
 	return(-ENOENT);
@@ -121,11 +141,13 @@
 	return(-ENOENT);
     }
     if (dispatch_scsi_info_ptr != NULL)
+    {
 	if (dir->i_ino <= PROC_SCSI_SCSI)
 	    de = scsi_dir;
 	else {
 	    de = &scsi_hba_dir[dispatch_scsi_info_ptr(dir->i_ino, 0, 0, 0, 0, 2)];
 	}
+    }
     else
 	de = scsi_dir2;
     
@@ -146,19 +168,25 @@
 			    void * dirent, filldir_t filldir)
 {
     struct proc_dir_entry * de;
-    unsigned int ino;
-    
+    uint index, num;
+ 
+    index = num = 0;
+
     if (!inode || !S_ISDIR(inode->i_mode))
 	return(-EBADF);
-    ino = inode->i_ino;
-    while (((unsigned) filp->f_pos) < count_dir_entries(ino)) {
-	if (dispatch_scsi_info_ptr)
-	    if (ino <= PROC_SCSI_SCSI)
+
+    index = count_dir_entries(inode->i_ino, &num);
+
+    while (((unsigned) filp->f_pos + index) < index + num) {
+	if (dispatch_scsi_info_ptr) {
+	    if (inode->i_ino <= PROC_SCSI_SCSI)
 		de = scsi_dir + filp->f_pos;
 	    else
-		de = scsi_hba_dir + filp->f_pos;
-	else
+		de = scsi_hba_dir + filp->f_pos + index;
+        }
+	else {
 	    de = scsi_dir2 + filp->f_pos;
+        }
 	if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino)<0)
 	    break;
 	filp->f_pos++;
@@ -172,7 +200,7 @@
     
     begin = 0;
     pos = len = sprintf(buffer, 
-			"The scsi core module is currently not present\n");
+			"No low-level scsi modules are currently present\n");
     if(pos < offset) {
 	len = 0;
 	begin = pos;
@@ -257,7 +285,6 @@
 static int proc_writescsi(struct inode * inode, struct file * file,
 			 char * buf, int count)
 {
-    uint ino;
     int ret = 0;
     char * page;
     
@@ -268,11 +295,9 @@
 	return(-EOVERFLOW);
     }
 
-    ino = inode->i_ino;
-    
     if(dispatch_scsi_info_ptr != NULL) {
 	memcpy_fromfs(page, buf, count);
-	ret = dispatch_scsi_info_ptr(ino, page, 0, 0, count, 1);
+	ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1);
     } else {
 	free_page((ulong) page);   
 	return(-ENOPKG);	  /* Nothing here */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/Makefile linux/fs/smbfs/Makefile
--- v1.3.6/linux/fs/smbfs/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/Makefile	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,33 @@
+#
+# Makefile for the linux smb-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+.s.o:
+	$(AS) -o $*.o $<
+
+OBJS=	proc.o sock.o inode.o file.o dir.o \
+	ioctl.o mmap.o
+
+smbfs.o: $(OBJS)
+	$(LD) -r -o smbfs.o $(OBJS)
+
+dep:
+	$(CPP) -M *.c > .depend
+
+modules: smbfs.o
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/README linux/fs/smbfs/README
--- v1.3.6/linux/fs/smbfs/README	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/README	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,12 @@
+smbfs is a filesystem which understands the SMB protocol. This is the
+protocol Windows for Workgroups, Windows NT or Lan Manager use to talk
+to each other. It was inspired by samba, the program by Andrew
+Tridgell that turns any unix site into a file server for DOS or
+Windows clients.  See ftp://nimbus.anu.edu.au/pub/tridge/samba/ for
+this interesting program suite and lots of more information on SMB and
+NetBIOS over TCP/IP. There you also find explanation for conceps like
+netbios name or share.
+
+To use smbfs, you need a special mount program, which can be found in
+the ksmbfs package, found on sunsite.unc.edu:/pub/Linux/ALPHA/smbfs.
+
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v1.3.6/linux/fs/smbfs/dir.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/dir.c	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,940 @@
+/*
+ *  dir.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <asm/segment.h>
+#include <linux/errno.h>
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+3) & ~3)
+
+static int 
+smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count);
+
+static int 
+smb_readdir(struct inode *inode, struct file *filp,
+            void *dirent, filldir_t filldir);
+
+static int 
+get_pname(struct inode *dir, const char *name, int len,
+          char **res_path, int *res_len);
+
+static int
+get_pname_static(struct inode *dir, const char *name, int len,
+                 char *path, int *res_len);
+
+static struct inode *
+smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo);
+
+static void 
+put_pname(char *path);
+
+static struct smb_inode_info *
+smb_find_inode(struct smb_server *server, const char *path);
+
+static int
+smb_lookup(struct inode *dir, const char *__name,
+           int len, struct inode **result);
+
+static int 
+smb_create(struct inode *dir, const char *name, int len, int mode, 
+           struct inode **result);
+
+static int 
+smb_mkdir(struct inode *dir, const char *name, int len, int mode);
+
+static int 
+smb_rmdir(struct inode *dir, const char *name, int len);
+
+static int
+smb_unlink(struct inode *dir, const char *name, int len);
+
+static int
+smb_rename(struct inode *old_dir, const char *old_name, int old_len, 
+           struct inode *new_dir, const char *new_name, int new_len);
+
+static inline void str_upper(char *name)
+{
+	while (*name) {
+		if (*name >= 'a' && *name <= 'z')
+			*name -= ('a' - 'A');
+		name++;
+	}
+}
+
+static inline void str_lower(char *name)
+{
+	while (*name) {
+		if (*name >= 'A' && *name <= 'Z')
+			*name += ('a' - 'A');
+		name ++;
+	}
+}
+
+static struct file_operations smb_dir_operations = {
+        NULL,			/* lseek - default */
+	smb_dir_read,		/* read - bad */
+	NULL,			/* write - bad */
+	smb_readdir,		/* readdir */
+	NULL,			/* select - default */
+	smb_ioctl,		/* ioctl - default */
+	NULL,                   /* mmap */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	NULL			/* fsync */
+};
+
+struct inode_operations smb_dir_inode_operations = 
+{
+	&smb_dir_operations,	/* default directory file ops */
+	smb_create,		/* create */
+	smb_lookup,    		/* lookup */
+	NULL,			/* link */
+	smb_unlink,    		/* unlink */
+	NULL,			/* symlink */
+	smb_mkdir,     		/* mkdir */
+	smb_rmdir,     		/* rmdir */
+	NULL,			/* mknod */
+	smb_rename,    		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL                    /* smap */
+};
+
+
+static int 
+smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count)
+{
+	return -EISDIR;
+}
+
+/*
+ * Description:
+ *  smb_readdir provides a listing in the form of filling the dirent structure.
+ *  Note that dirent resides in the user space. This is to support reading of a
+ *  directory "stream". 
+ * Arguments:
+ *  inode   ---  Pointer to to the directory.
+ *  filp    ---  The directory stream. (filp->f_pos indicates
+ *               position in the stream.)
+ *  dirent  ---  Will hold count directory entries. (Is in user space.)
+ *  count   ---  Number of entries to be read. Should indicate the total 
+ *               buffer space available for filling with dirents.
+ * Return values:
+ *     < 0     ---  An error occured (linux/errno.h).
+ *     = 0     ---
+ *     > 0     ---  Success, amount of bytes written to dirent.
+ * Notes:
+ *     Since we want to reduce directory lookups we revert into a
+ *     dircache. It is taken rather directly out of the nfs_readdir.
+ */
+
+/* In smbfs, we have unique inodes across all mounted filesystems, for
+   all inodes that are in memory. That's why it's enough to index the
+   directory cache by the inode number. */
+
+static unsigned long      c_ino = 0;
+static int                c_size;
+static int                c_seen_eof;
+static int                c_last_returned_index;
+static struct smb_dirent* c_entry = NULL;
+
+static int
+smb_readdir1(struct inode *inode, const struct file *filp,
+	     struct smb_dirent *ret,ino_t *ino)
+{
+	int result, i = 0;
+	struct smb_dirent *entry = NULL;
+        struct smb_server *server = SMB_SERVER(inode);
+
+	DDPRINTK("smb_readdir: filp->f_pos = %d\n", (int)filp->f_pos);
+	DDPRINTK("smb_readdir: inode->i_ino = %ld, c_ino = %ld\n",
+		 inode->i_ino, c_ino);
+
+	if (!inode || !S_ISDIR(inode->i_mode)) {
+		printk("smb_readdir: inode is NULL or not a directory\n");
+		return -EBADF;
+	}
+
+	if (c_entry == NULL) 
+	{
+		i = sizeof (struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
+		c_entry = (struct smb_dirent *) smb_kmalloc(i, GFP_KERNEL);
+		for (i = 0; i < SMB_READDIR_CACHE_SIZE; i++) {
+			c_entry[i].path =
+                                (char *) smb_kmalloc(SMB_MAXNAMELEN + 1,
+                                                     GFP_KERNEL);
+                        if (c_entry[i].path == NULL) {
+                                DPRINTK("smb_readdir: could not alloc path\n");
+                        }
+                }
+	}
+
+        if (filp->f_pos == 0) {
+                smb_invalid_dir_cache(inode->i_ino);
+        }
+
+	if (inode->i_ino == c_ino) {
+		for (i = 0; i < c_size; i++) {
+			if (filp->f_pos == c_entry[i].f_pos) {
+                                entry = &c_entry[i];
+                                c_last_returned_index = i;
+                                break;
+			}
+		}
+                if ((entry == NULL) && c_seen_eof)
+                        return 0;
+	}
+
+	if (entry == NULL) {
+		DPRINTK("smb_readdir: Not found in cache.\n");
+		result = smb_proc_readdir(server, inode,
+                                          filp->f_pos, SMB_READDIR_CACHE_SIZE,
+                                          c_entry);
+
+		if (result < 0) {
+			c_ino = 0;
+			return result;
+		}
+
+		if (result > 0) {
+                        c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
+			c_ino  = inode->i_ino;
+			c_size = result;
+			entry = c_entry;
+                        c_last_returned_index = 0;
+                        for (i = 0; i < c_size; i++) {
+
+                                switch (server->case_handling) 
+                                {
+                                case CASE_UPPER:
+                                        str_upper(c_entry[i].path); break;
+                                case CASE_LOWER:
+                                        str_lower(c_entry[i].path); break;
+                                case CASE_DEFAULT:
+                                        break;
+                                }
+                        }
+		}
+	}
+	
+	if (entry) {
+
+                /* We found it.  For getwd(), we have to return the
+                   correct inode in d_ino if the inode is currently in
+                   use. Otherwise the inode number does not
+                   matter. */ 
+
+                int  path_len;
+                struct smb_inode_info *ino_info;
+                char complete_path[SMB_MAXPATHLEN];
+
+                
+
+		i = strlen(entry->path);
+                if ((result = get_pname_static(inode, entry->path, i,
+                                               complete_path,
+                                               &path_len)) < 0)
+                        return result;
+
+                ino_info = smb_find_inode(server, complete_path);
+
+                /* Some programs seem to be confused about a zero
+                   inode number, so we set it to one.  Thanks to
+                   Gordon Chaffee for this one. */
+                if (ino_info == NULL) {
+                        ino_info = (struct smb_inode_info *) 1;
+                }
+
+		DDPRINTK("smb_readdir: entry->path = %s\n", entry->path);
+		DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);
+
+		*ino = (ino_t)ino_info; /* use the pointer as the
+					  inode - dangerous if we have
+					  64 bit pointers! FIXME */
+
+		*ret = *entry;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int 
+smb_readdir(struct inode *inode, struct file *filp,
+            void *dirent, filldir_t filldir)
+{
+	struct smb_dirent d;
+	ino_t ino;
+
+	while (smb_readdir1(inode,filp,&d,&ino) == 1) {		
+		if (filldir(dirent,d.path,strlen(d.path)+1,
+			    filp->f_pos,ino) < 0) {
+			return 0;
+		}
+		filp->f_pos++;
+	}
+	return 0;
+}
+
+void
+smb_init_dir_cache(void)
+{
+        c_ino   = 0;
+        c_entry = NULL;
+}
+
+void
+smb_invalid_dir_cache(unsigned long ino)
+{
+	if (ino == c_ino) {
+                c_ino = 0;
+                c_seen_eof = 0;
+        }
+}
+
+void
+smb_free_dir_cache(void)
+{
+        int i;
+
+        DPRINTK("smb_free_dir_cache: enter\n");
+        
+        if (c_entry == NULL)
+                return;
+
+        for (i = 0; i < SMB_READDIR_CACHE_SIZE; i++) {
+                smb_kfree_s(c_entry[i].path, NAME_MAX + 1);
+        }
+
+        smb_kfree_s(c_entry,
+                    sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE);
+        c_entry = NULL;
+
+        DPRINTK("smb_free_dir_cache: exit\n");
+}
+
+
+/* get_pname_static: it expects the res_path to be a preallocated
+   string of len SMB_MAXPATHLEN. */
+
+static int
+get_pname_static(struct inode *dir, const char *name, int len,
+                 char *path, int *res_len)
+{
+        char *parentname = SMB_INOP(dir)->finfo.path;
+        int   parentlen  = SMB_INOP(dir)->finfo.len;
+
+#if 1
+        if (parentlen != strlen(parentname)) {
+                printk("get_pname: parent->finfo.len = %d instead of %d\n",
+                       parentlen, strlen(parentname));
+                parentlen = strlen(parentname);
+        }
+	
+#endif
+	DDPRINTK("get_pname_static: parentname = %s, len = %d\n",
+                 parentname, parentlen);
+	
+        if (len > SMB_MAXNAMELEN) {
+                return -ENAMETOOLONG;
+        }
+
+	/* Fast cheat for . */
+	if (len == 0 || (len == 1 && name[0] == '.')) {
+
+		memcpy(path, parentname, parentlen + 1);
+		*res_len = parentlen;
+		return 0;
+	}
+	
+	/* Hmm, what about .. ? */
+	if (len == 2 && name[0] == '.' && name[1] == '.') {
+
+		char *pos = strrchr(parentname, '\\');
+
+                if (   (pos == NULL)
+                    && (parentlen == 0)) {
+
+                        /* We're at the top */
+
+                        path[0] = '\\';
+                        path[1] = '\0';
+                        *res_len  = 2;
+                        return 0;
+                }
+                        
+                
+		if (pos == NULL) {
+			printk("smb_make_name: Bad parent SMB-name: %s",
+                               parentname);
+			return -ENODATA;
+		}
+		
+		len = pos - parentname;
+
+	        memcpy(path, parentname, len);
+		path[len] = '\0';
+	}
+	else
+	{
+		if (len + parentlen + 2 > SMB_MAXPATHLEN) 
+			return -ENAMETOOLONG;
+				
+		memcpy(path, parentname, parentlen);
+		path[parentlen] = '\\';
+		memcpy(path + parentlen + 1, name, len);
+		path[parentlen + 1 + len] = '\0';
+		len = parentlen + len + 1;
+	}
+
+	switch (SMB_SERVER(dir)->case_handling) 
+	{
+        case CASE_UPPER: 
+                str_upper(path); 
+                break;
+        case CASE_LOWER: 
+                str_lower(path); 
+                break;
+        case CASE_DEFAULT: 
+                break;
+	}
+
+	*res_len = len;
+
+	DDPRINTK("get_pname: path = %s, *pathlen = %d\n",
+                 path, *res_len);
+	return 0;
+}
+        
+static int 
+get_pname(struct inode *dir, const char *name, int len,
+          char **res_path, int *res_len)
+{
+        char result[SMB_MAXPATHLEN];
+        int  result_len;
+        int  res;
+
+        if ((res = get_pname_static(dir,name,len,result,&result_len) != 0)) {
+                return res;
+        }
+
+        if ((*res_path = smb_kmalloc(result_len+1, GFP_KERNEL)) == NULL) {
+                printk("get_pname: Out of memory while allocating name.");
+                return -ENOMEM;
+        }
+
+        strcpy(*res_path, result);
+        *res_len = result_len;
+        return 0;
+}
+
+static void
+put_pname(char *path)
+{
+	smb_kfree_s(path, 0);
+}
+
+/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
+   under dir. The caller must assure that it's not already there. We
+   assume that path is allocated for us. */
+
+static struct inode *
+smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo)
+{
+	struct smb_dirent newent = { 0 };
+	struct inode *inode;
+	int error, len;
+        struct smb_inode_info *new_inode_info;
+        struct smb_inode_info *root;
+
+	if (!dir) {
+		printk("smb_iget: dir is NULL\n");
+		return NULL;
+	}
+
+        if (!path) {
+                printk("smb_iget: path is NULL\n");
+                return NULL;
+        }
+
+        len = strlen(path);
+        
+	if (!finfo) {
+		error = smb_proc_getattr(&(SMB_SBP(dir->i_sb)->s_server),
+                                         path, len, &newent);
+		if (error) {
+			printk("smb_iget: getattr error = %d\n", -error);
+			return NULL;
+		}		
+		finfo = &newent;
+		DPRINTK("smb_iget: Read finfo:\n");
+		DPRINTK("smb_iget: finfo->attr = 0x%X\n", finfo->attr);
+	}
+
+        new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
+                                     GFP_KERNEL);
+
+        if (new_inode_info == NULL) {
+                printk("smb_iget: could not alloc mem for %s\n", path);
+                return NULL;
+        }
+
+        new_inode_info->state = INODE_LOOKED_UP;
+        new_inode_info->nused = 0;
+        new_inode_info->dir   = SMB_INOP(dir);
+
+        new_inode_info->finfo        = *finfo;
+        new_inode_info->finfo.opened = 0;
+        new_inode_info->finfo.path   = path;
+        new_inode_info->finfo.len    = len;
+
+        SMB_INOP(dir)->nused += 1;
+
+        /* We have to link the new inode_info into the doubly linked
+           list of inode_infos to make a complete linear search
+           possible. */
+
+        root = &(SMB_SERVER(dir)->root);
+
+        new_inode_info->prev = root;
+        new_inode_info->next = root->next;
+        root->next->prev = new_inode_info;
+        root->next = new_inode_info;
+        
+	if (!(inode = iget(dir->i_sb, (int)new_inode_info))) {
+		printk("smb_iget: iget failed!");
+		return NULL;
+	}
+
+	return inode;
+}
+
+void
+smb_free_inode_info(struct smb_inode_info *i)
+{
+        if (i == NULL) {
+                printk("smb_free_inode: i == NULL\n");
+                return;
+        }
+
+        i->state = INODE_CACHED;
+        while ((i->nused == 0) && (i->state == INODE_CACHED)) {
+                struct smb_inode_info *dir = i->dir;
+
+                i->next->prev = i->prev;
+                i->prev->next = i->next;
+
+                smb_kfree_s(i->finfo.path, i->finfo.len+1);
+                smb_kfree_s(i, sizeof(struct smb_inode_info));
+
+                if (dir == NULL) return;
+
+                (dir->nused)--;
+                i = dir;
+        }
+}
+        
+int
+smb_init_root(struct smb_server *server)
+{
+        struct smb_inode_info *root = &(server->root);
+        int result;
+
+        root->finfo.path = server->m.root_path;
+        root->finfo.len  = strlen(root->finfo.path);
+        root->finfo.opened = 0;
+
+        root->state = INODE_LOOKED_UP;
+        root->nused = 1;
+        root->dir   = NULL;
+        root->next = root->prev = root;
+
+        if (root->finfo.len == 0) {
+                result = smb_proc_getattr(server, "\\", 1, &(root->finfo));
+        }
+        else
+        {
+                result = smb_proc_getattr(server, 
+                                          root->finfo.path, root->finfo.len,
+                                          &(root->finfo));
+        }
+        return result;
+}
+
+void
+smb_free_all_inodes(struct smb_server *server)
+{
+        /* Here nothing should be to do. I do not know whether it's
+           better to leave some memory allocated or be stuck in an
+           endless loop */
+#if 1
+        struct smb_inode_info *root = &(server->root);
+
+        if (root->next != root) {
+                printk("smb_free_all_inodes: INODES LEFT!!!\n");
+        }
+
+        while (root->next != root) {
+                printk("smb_free_all_inodes: freeing inode\n");
+                smb_free_inode_info(root->next);
+                /* In case we have an endless loop.. */
+                schedule();
+        }
+#endif        
+        
+        return;
+}
+
+/* This has to be called when a connection has gone down, so that all
+   file-handles we got from the server are invalid */
+
+void
+smb_invalidate_all_inodes(struct smb_server *server)
+{
+        struct smb_inode_info *ino = &(server->root);
+
+        do {
+                ino->finfo.opened = 0;
+                ino = ino->next;
+        } while (ino != &(server->root));
+        
+        return;
+}
+        
+
+/* We will search the inode that belongs to this name, currently by a
+   complete linear search through the inodes belonging to this
+   filesystem. This has to be fixed. */
+
+static struct smb_inode_info *
+smb_find_inode(struct smb_server *server, const char *path)
+{
+        struct smb_inode_info *result = &(server->root);
+
+        if (path == NULL)
+                return NULL;
+
+        do {
+                if (strcmp(result->finfo.path, path) == 0)
+                        return result;
+                result = result->next;
+
+        } while (result != &(server->root));
+
+        return NULL;
+}
+
+
+static int 
+smb_lookup(struct inode *dir, const char *__name, int len,
+           struct inode **result)
+{
+	char *name = NULL;
+	struct smb_dirent finfo;
+        struct smb_inode_info *result_info;
+	int error;
+        int found_in_cache;
+
+	*result = NULL;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("smb_lookup: inode is NULL or not a directory.\n");
+		iput(dir);
+		return -ENOENT;
+	}
+
+        DDPRINTK("smb_lookup: %s\n", __name);
+
+	/* Fast cheat for . */
+	if (len == 0 || (len == 1 && __name[0] == '.')) {
+		*result = dir;
+		return 0;
+	}
+
+	/* Now we will have to build up an SMB filename. */
+	if ((error = get_pname(dir, __name, len, &name, &len)) < 0) {
+		iput(dir);
+		return error;
+	}
+
+        result_info = smb_find_inode(SMB_SERVER(dir), name);
+
+        if (result_info != 0) {
+
+                if (result_info->state == INODE_CACHED)
+                        result_info->state = INODE_LOOKED_UP;
+
+                put_pname(name);
+
+                /* Here we convert the inode_info address into an
+                   inode number */
+
+                *result = iget(dir->i_sb, (int)result_info);
+                iput(dir);
+
+                if (*result == NULL) {
+                        return -EACCES;
+                } else {
+                        return 0;
+                }
+        }
+
+	/* Ok, now we have made our name. We have to build a new
+           smb_inode_info struct and insert it into the tree, if it is
+           a name that exists on the server */
+
+        /* If the file is in the dir cache, we do not have to ask the
+           server. */
+
+        found_in_cache = 0;
+        
+        if (dir->i_ino == c_ino) {
+                int first = c_last_returned_index - 1;
+                int i;
+
+                if (first < 0) {
+                        first = c_size - 1;
+                }
+
+                i = first;
+                do {
+                        DDPRINTK("smb_lookup: trying index: %d, name: %s\n",
+                                i, c_entry[i].path);
+                        if (strcmp(c_entry[i].path, __name) == 0) {
+                                DPRINTK("smb_lookup: found in cache!\n");
+                                finfo = c_entry[i];
+                                finfo.path = NULL; /* It's not ours! */
+                                found_in_cache = 1;
+                                break;
+                        }
+                        i = (i + 1) % c_size;
+
+                } while (i != first);
+        }
+
+        if (found_in_cache == 0) {
+                error = smb_proc_getattr(SMB_SERVER(dir), name, len, &finfo);
+                if (error < 0) {
+                        put_pname(name);
+                        iput(dir);
+                        return error;
+                }
+        }
+
+	if (!(*result = smb_iget(dir, name, &finfo))) {
+		put_pname(name);
+		iput(dir);
+		return -EACCES;
+	}
+
+        DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long)result_info);
+	iput(dir);
+	return 0;
+}
+
+static int 
+smb_create(struct inode *dir, const char *name, int len, int mode,
+           struct inode **result)
+{
+	int error;
+	char *path = NULL;
+	struct smb_dirent entry;
+
+	*result = NULL;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("smb_create: inode is NULL or not a directory\n");
+		iput(dir);
+		return -ENOENT;
+	}
+
+	/* Now we will have to build up an SMB filename. */
+	if ((error = get_pname(dir, name, len, &path, &len)) < 0) {
+		iput(dir);
+		return error;
+	}
+
+        entry.attr  = 0;
+        entry.ctime = CURRENT_TIME;
+        entry.size  = 0;
+
+        error = smb_proc_create(SMB_SERVER(dir), path, len, &entry);
+	if (error < 0) {
+		put_pname(path);
+		iput(dir);
+		return error;
+	}
+
+        smb_invalid_dir_cache(dir->i_ino);
+
+	if (!(*result = smb_iget(dir, path, &entry)) < 0) {
+		put_pname(path);
+		iput(dir);
+		return error;
+	}
+	iput(dir);
+	return 0;	
+}
+
+static int
+smb_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+	int error;
+	char path[SMB_MAXPATHLEN];
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("smb_mkdir: inode is NULL or not a directory\n");
+		iput(dir);
+		return -ENOENT;
+	}
+
+	/* Now we will have to build up an SMB filename. */
+	if ((error = get_pname_static(dir, name, len, path, &len)) < 0) {
+		iput(dir);
+		return error;
+	}
+
+	if ((error = smb_proc_mkdir(SMB_SERVER(dir), path, len)) == 0) {
+                smb_invalid_dir_cache(dir->i_ino);
+        }
+
+	iput(dir);
+	return error;
+}
+
+static int
+smb_rmdir(struct inode *dir, const char *name, int len)
+{
+	int error;
+        char path[SMB_MAXPATHLEN];
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("smb_rmdir: inode is NULL or not a directory\n");
+		iput(dir);
+		return -ENOENT;
+	}
+	if ((error = get_pname_static(dir, name, len, path, &len)) < 0) {
+		iput(dir);
+		return error;
+	}
+        if (smb_find_inode(SMB_SERVER(dir), path) != NULL) {
+                error = -EBUSY;
+        } else {
+                if ((error = smb_proc_rmdir(SMB_SERVER(dir), path, len)) == 0)
+                        smb_invalid_dir_cache(dir->i_ino);
+        }
+	iput(dir);
+	return error;
+}
+
+static int
+smb_unlink(struct inode *dir, const char *name, int len)
+{
+	int error;
+	char path[SMB_MAXPATHLEN];
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("smb_unlink: inode is NULL or not a directory\n");
+		iput(dir);
+		return -ENOENT;
+	}
+	if ((error = get_pname_static(dir, name, len, path, &len)) < 0) {
+		iput(dir);
+		return error;
+	}
+        if (smb_find_inode(SMB_SERVER(dir), path) != NULL) {
+                error = -EBUSY;
+        } else {
+                if ((error = smb_proc_unlink(SMB_SERVER(dir), path, len)) == 0)
+                        smb_invalid_dir_cache(dir->i_ino);
+        }
+
+	iput(dir);
+	return error;
+}
+
+static int
+smb_rename(struct inode *old_dir, const char *old_name, int old_len,
+           struct inode *new_dir, const char *new_name, int new_len)
+{
+	int res;
+	char old_path[SMB_MAXPATHLEN], new_path[SMB_MAXPATHLEN];
+
+	if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
+		printk("smb_rename: old inode is NULL or not a directory\n");
+                res = -ENOENT;
+                goto finished;
+	}
+
+	if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
+		printk("smb_rename: new inode is NULL or not a directory\n");
+                res = -ENOENT;
+                goto finished;
+	}
+
+        res = get_pname_static(old_dir, old_name, old_len, old_path, &old_len);
+        if (res < 0) {
+                goto finished;
+	}
+
+        res = get_pname_static(new_dir, new_name, new_len, new_path, &new_len);
+	if (res < 0) {
+                goto finished;
+	}
+	
+        if (   (smb_find_inode(SMB_SERVER(old_dir), old_path) != NULL)
+            || (smb_find_inode(SMB_SERVER(new_dir), new_path) != NULL)) {
+                res = -EBUSY;
+                goto finished;
+        }
+
+	res = smb_proc_mv(SMB_SERVER(old_dir), old_path, old_len,
+                          new_path, new_len);
+
+        if (res == 0) {
+                smb_invalid_dir_cache(old_dir->i_ino);
+                smb_invalid_dir_cache(new_dir->i_ino);
+        }
+	
+ finished:
+	iput(old_dir); 
+	iput(new_dir);
+	return res;
+}
+	
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/file.c linux/fs/smbfs/file.c
--- v1.3.6/linux/fs/smbfs/file.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/file.c	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,237 @@
+/*
+ *  file.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <linux/malloc.h>
+
+static int 
+smb_fsync(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+int
+smb_make_open(struct inode *i, int right)
+{
+        struct smb_dirent *dirent;
+        int open_result;
+
+        if (i == NULL) {
+                printk("smb_make_open: got NULL inode\n");
+                return -EINVAL;
+        }
+
+        dirent = &(SMB_INOP(i)->finfo);
+
+        DDPRINTK("smb_make_open: dirent->opened = %d\n", dirent->opened);
+
+        if ((dirent->opened) == 0) {
+                /* tries max. rights */
+                open_result = smb_proc_open(SMB_SERVER(i),
+                                            dirent->path, dirent->len,
+                                            dirent);
+                if (open_result) 
+                        return open_result;
+
+                dirent->opened = 1;
+        }
+
+        if (   ((right == O_RDONLY) && (   (dirent->access == O_RDONLY)
+                                        || (dirent->access == O_RDWR)))
+            || ((right == O_WRONLY) && (   (dirent->access == O_WRONLY)
+                                        || (dirent->access == O_RDWR)))
+            || ((right == O_RDWR)   && (dirent->access == O_RDWR)))
+                return 0;
+
+        return -EACCES;
+}
+
+static int 
+smb_file_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+	int result, bufsize, to_read, already_read;
+	off_t pos;
+        int errno;
+
+        DPRINTK("smb_file_read: enter %s\n", SMB_FINFO(inode)->path);
+        
+	if (!inode) {
+		DPRINTK("smb_file_read: inode = NULL\n");
+		return -EINVAL;
+	}
+
+	if (!S_ISREG(inode->i_mode)) {
+		DPRINTK("smb_file_read: read from non-file, mode %07o\n",
+                        inode->i_mode);
+		return -EINVAL;
+	}
+
+        if ((errno = smb_make_open(inode, O_RDONLY)) != 0)
+                return errno;
+        
+	pos = file->f_pos;
+
+	if (pos + count > inode->i_size)
+		count = inode->i_size - pos;
+
+	if (count <= 0)
+		return 0;
+	bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
+
+        already_read = 0;
+
+	/* First read in as much as possible for each bufsize. */
+        while (already_read < count) {
+
+                to_read = min(bufsize, count - already_read);
+
+		result = smb_proc_read(SMB_SERVER(inode), SMB_FINFO(inode),
+                                       pos, to_read, buf, 1);
+		if (result < 0)
+			return result;
+		pos += result;
+		buf += result;
+                already_read += result;
+
+		if (result < to_read) {
+                        break;
+		}
+	}
+
+        file->f_pos = pos;
+
+        DPRINTK("smb_file_read: exit %s\n", SMB_FINFO(inode)->path);
+
+        return already_read;
+}
+
+static int 
+smb_file_write(struct inode *inode, struct file *file, char *buf, int count)
+{
+	int result, bufsize, to_write, already_written;
+        off_t pos;
+        int errno;
+			  
+	if (!inode) {
+		DPRINTK("smb_file_write: inode = NULL\n");
+		return -EINVAL;
+	}
+
+	if (!S_ISREG(inode->i_mode)) {
+		DPRINTK("smb_file_write: write to non-file, mode %07o\n",
+                       inode->i_mode);
+		return -EINVAL;
+	}
+
+        DPRINTK("smb_file_write: enter %s\n", SMB_FINFO(inode)->path);
+
+	if (count <= 0)
+		return 0;
+
+        if ((errno = smb_make_open(inode, O_RDWR)) != 0)
+                return errno;
+        
+	pos = file->f_pos;
+
+	if (file->f_flags & O_APPEND)
+		pos = inode->i_size;
+
+	bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
+
+        already_written = 0;
+
+        while (already_written < count) {
+
+                to_write = min(bufsize, count - already_written);
+
+		result = smb_proc_write(SMB_SERVER(inode), SMB_FINFO(inode), 
+					pos, to_write, buf);
+
+		if (result < 0)
+			return result;
+
+		pos += result;
+		buf += result;
+                already_written += result;
+
+		if (result < to_write) {
+			break;
+		}
+	}
+
+	file->f_pos = pos;
+
+        if (pos > inode->i_size) {
+                inode->i_size = pos;
+        }
+
+        DPRINTK("smb_file_write: exit %s\n", SMB_FINFO(inode)->path);
+
+	return already_written;
+}
+
+static struct file_operations smb_file_operations = {
+	NULL,			/* lseek - default */
+	smb_file_read,		/* read */
+	smb_file_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	smb_ioctl,		/* ioctl */
+	smb_mmap,               /* mmap */
+	NULL,                   /* open */
+	NULL,                   /* release */
+	smb_fsync,		/* fsync */
+};
+
+struct inode_operations smb_file_inode_operations = {
+	&smb_file_operations,	/* default 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 */
+	NULL,			/* bmap */
+	NULL			/* truncate */
+};
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v1.3.6/linux/fs/smbfs/inode.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/inode.c	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,462 @@
+/*
+ *  inode.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+
+extern int close_fp(struct file *filp);
+
+int smb_notify_change(struct inode *, struct iattr *);
+static void smb_put_inode(struct inode *);
+static void smb_read_inode(struct inode *);
+static void smb_put_super(struct super_block *);
+static void smb_statfs(struct super_block *, struct statfs *, int bufsiz);
+
+static struct super_operations smb_sops = {
+	smb_read_inode,         /* read inode */
+	smb_notify_change,      /* notify change */
+	NULL,			/* write inode */
+	smb_put_inode,		/* put inode */
+	smb_put_super,		/* put superblock */
+	NULL,			/* write superblock */
+	smb_statfs,		/* stat filesystem */
+	NULL
+        };
+
+/* smb_read_inode: Called from iget, it only traverses the allocated
+   smb_inode_info's and initializes the inode from the data found
+   there.  It does not allocate or deallocate anything. */
+
+static void
+smb_read_inode(struct inode *inode)
+{
+        /* Our task should be extremely simple here. We only have to
+           look up the infomation somebody else (smb_iget) put into
+           the inode tree. The address of this information is the
+           inode->i_ino. Just to make sure everything went well, we
+           check it's there. */
+
+        struct smb_inode_info *inode_info
+                = (struct smb_inode_info *)(inode->i_ino);
+
+#if 1
+        struct smb_inode_info *root = &(SMB_SERVER(inode)->root);
+        struct smb_inode_info *check_info = root;
+
+        do {
+                if (inode_info == check_info) {
+                        if (check_info->state == INODE_LOOKED_UP) {
+                                DDPRINTK("smb_read_inode: found it!\n");
+                                goto good;
+                        }
+                        else {
+                                printk("smb_read_inode: "
+                                       "state != INODE_LOOKED_UP\n");
+                                return;
+                        }
+                }
+                check_info = check_info->next;
+        } while (check_info != root);
+
+        /* Ok, now we're in trouble. The inode info is not there. What
+           should we do now??? */
+        printk("smb_read_inode: inode info not found\n");
+        return;
+
+ good:
+#endif
+        inode_info->state = INODE_VALID;
+
+        SMB_INOP(inode) = inode_info;
+
+        if (SMB_INOP(inode)->finfo.attr & aDIR)
+                inode->i_mode = SMB_SERVER(inode)->m.dir_mode;
+        else
+                inode->i_mode = SMB_SERVER(inode)->m.file_mode;
+
+        DDPRINTK("smb_read_inode: inode->i_mode = %u\n", inode->i_mode);
+
+        inode->i_nlink   = 1;
+        inode->i_uid     = SMB_SERVER(inode)->m.uid;
+        inode->i_gid     = SMB_SERVER(inode)->m.gid;
+        inode->i_size    = SMB_INOP(inode)->finfo.size;
+        inode->i_blksize = 1024;
+        inode->i_rdev    = 0;
+        if ((inode->i_blksize != 0) && (inode->i_size != 0))
+                inode->i_blocks =
+                        (inode->i_size - 1) / inode->i_blksize + 1;
+        else
+                inode->i_blocks = 0;
+
+        inode->i_mtime = SMB_INOP(inode)->finfo.mtime;
+        inode->i_ctime = SMB_INOP(inode)->finfo.ctime;
+        inode->i_atime = SMB_INOP(inode)->finfo.atime;
+
+        if (S_ISREG(inode->i_mode))
+                inode->i_op = &smb_file_inode_operations;
+        else if (S_ISDIR(inode->i_mode))
+                inode->i_op = &smb_dir_inode_operations;
+        else
+                inode->i_op = NULL;
+
+}
+
+static void
+smb_put_inode(struct inode *inode)
+{
+        struct smb_dirent *finfo = SMB_FINFO(inode);
+
+        if (finfo->opened != 0) {
+
+                /* smb_proc_close wants mtime in finfo */
+                finfo->mtime = inode->i_mtime;
+                
+                if (smb_proc_close(SMB_SERVER(inode), finfo)) {
+                        /* We can't do anything but complain. */
+                        printk("smb_put_inode: could not close\n");
+                }
+        }
+        
+        smb_free_inode_info(SMB_INOP(inode));
+
+        if (S_ISDIR(inode->i_mode)) {
+                DDPRINTK("smb_put_inode: put directory %ld\n",
+                         inode->i_ino);
+                smb_invalid_dir_cache(inode->i_ino);
+        }                
+
+	clear_inode(inode);
+}
+
+static void
+smb_put_super(struct super_block *sb)
+{
+        struct smb_server *server = &(SMB_SBP(sb)->s_server);
+
+        smb_proc_disconnect(server);
+	close_fp(server->sock_file);
+
+	lock_super(sb);
+
+        smb_free_all_inodes(server);
+
+        smb_kfree_s(server->packet, server->max_xmit);
+
+	sb->s_dev = 0;
+        smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
+
+	unlock_super(sb);
+
+        MOD_DEC_USE_COUNT;
+}
+
+
+/* Hmm, should we do this like the NFS mount command does? Guess so.. */
+struct super_block *
+smb_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+	struct smb_mount_data *data = (struct smb_mount_data *) raw_data;
+	struct smb_server *server;
+        struct smb_sb_info *smb_sb;
+	unsigned int fd;
+	struct file *filp;
+	dev_t dev = sb->s_dev;
+	int error;
+
+	if (!data) {
+		printk("smb_read_super: missing data argument\n");
+		sb->s_dev = 0;
+		return NULL;
+	}
+	fd = data->fd;
+	if (data->version != SMB_MOUNT_VERSION) {
+		printk("smb warning: mount version %s than kernel\n",
+		       (data->version < SMB_MOUNT_VERSION) ?
+                       "older" : "newer");
+	}
+	if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) {
+		printk("smb_read_super: invalid file descriptor\n");
+		sb->s_dev = 0;
+		return NULL;
+	}
+	if (!S_ISSOCK(filp->f_inode->i_mode)) {
+		printk("smb_read_super: not a socket!\n");
+		sb->s_dev = 0;
+		return NULL;
+	}
+
+        /* We must malloc our own super-block info */
+        smb_sb = (struct smb_sb_info *)smb_kmalloc(sizeof(struct smb_sb_info),
+                                                   GFP_KERNEL);
+
+        if (smb_sb == NULL) {
+                printk("smb_read_super: could not alloc smb_sb_info\n");
+                return NULL;
+        }
+
+	filp->f_count += 1; 
+
+	lock_super(sb);
+
+        SMB_SBP(sb) = smb_sb;
+        
+	sb->s_blocksize = 1024; /* Eh...  Is this correct? */
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = SMB_SUPER_MAGIC;
+	sb->s_dev = dev;
+	sb->s_op = &smb_sops;
+
+	server = &(SMB_SBP(sb)->s_server);
+	server->sock_file = filp;
+	server->lock = 0;
+	server->wait = NULL;
+        server->packet = NULL;
+	server->max_xmit = data->max_xmit;
+	if (server->max_xmit <= 0)
+		server->max_xmit = SMB_DEF_MAX_XMIT;
+   
+	server->tid = 0;
+	server->pid = current->pid;
+	server->mid = current->pid + 20;
+
+        server->m = *data;
+	server->m.file_mode = (server->m.file_mode &
+                             (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG;
+	server->m.dir_mode  = (server->m.dir_mode &
+                             (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR;
+
+        /*
+         * Make the connection to the server
+         */
+        
+	error = smb_proc_connect(server);
+
+	unlock_super(sb);
+
+	if (error < 0) {
+		sb->s_dev = 0;
+		printk("smb_read_super: Failed connection, bailing out "
+                       "(error = %d).\n", -error);
+                goto fail;
+	}
+
+        if (server->protocol >= PROTOCOL_LANMAN2)
+                server->case_handling = CASE_DEFAULT;
+        else
+                server->case_handling = CASE_LOWER;
+
+	if ((error = smb_proc_dskattr(sb, &(SMB_SBP(sb)->s_attr))) < 0) {
+		sb->s_dev = 0;
+		printk("smb_read_super: could not get super block "
+                       "attributes\n");
+                smb_kfree_s(server->packet, server->max_xmit);
+                goto fail;
+	}
+
+	DPRINTK("smb_read_super : %u %u %u %u\n",
+                SMB_SBP(sb)->s_attr.total,
+                SMB_SBP(sb)->s_attr.blocksize,
+                SMB_SBP(sb)->s_attr.allocblocks,
+                SMB_SBP(sb)->s_attr.free);
+
+        if (smb_init_root(server) != 0) {
+                sb->s_dev = 0;
+                printk("smb_read_super: invalid root directory\n");
+                smb_kfree_s(server->packet, server->max_xmit);
+                goto fail;
+        }
+
+        DPRINTK("smb_read_super: SMB_SBP(sb) = %x\n", (int)SMB_SBP(sb));
+
+	if (!(sb->s_mounted = iget(sb, (int)&(server->root)))) {
+		sb->s_dev = 0;
+		printk("smb_read_super: get root inode failed\n");
+                smb_kfree_s(server->packet, server->max_xmit);
+                goto fail;
+	}
+
+        MOD_INC_USE_COUNT;
+	return sb;
+
+ fail:
+	filp->f_count -= 1; 
+        smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
+        return NULL;
+}
+
+static void 
+smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+	int error;
+	struct smb_dskattr attr;
+	struct statfs tmp;
+	
+	error = smb_proc_dskattr(sb, &attr);
+
+	if (error) {
+		printk("smb_statfs: dskattr error = %d\n", -error);
+		attr.total = attr.allocblocks = attr.blocksize =
+                        attr.free = 0;
+	}
+
+	tmp.f_type = SMB_SUPER_MAGIC;
+	tmp.f_bsize = attr.blocksize*attr.allocblocks;
+	tmp.f_blocks = attr.total;
+	tmp.f_bfree = attr.free;
+	tmp.f_bavail = attr.free;
+	tmp.f_files = -1;
+	tmp.f_ffree = -1;
+	tmp.f_namelen = SMB_MAXPATHLEN;
+	memcpy_tofs(buf, &tmp, bufsiz);
+}
+
+/* DO MORE */
+int
+smb_notify_change(struct inode *inode, struct iattr *attr)
+{
+	int error = 0;
+
+	if ((error = inode_change_ok(inode, attr)) < 0)
+		return error;
+
+        if (!S_ISREG(inode->i_mode))
+                return -EPERM;
+
+        if ((attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) != 0) {
+                return -EPERM;
+        }
+        
+        if ((attr->ia_valid & ATTR_SIZE) != 0) {
+
+                if ((error = smb_make_open(inode, O_WRONLY)) < 0)
+                        goto fail;
+
+                if ((error = smb_proc_trunc(SMB_SERVER(inode),
+                                            SMB_FINFO(inode)->fileid,
+                                            attr->ia_size)) < 0)
+                        goto fail;
+
+        }
+
+        if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) {
+
+                struct smb_dirent finfo;
+
+                finfo.attr  = 0;
+
+                if ((attr->ia_valid & ATTR_CTIME) != 0)
+                        finfo.ctime = attr->ia_ctime;
+                else
+                        finfo.ctime = inode->i_ctime;
+
+                if ((attr->ia_valid & ATTR_MTIME) != 0)
+                        finfo.mtime = attr->ia_mtime;
+                else
+                        finfo.mtime = inode->i_mtime;
+
+                if ((attr->ia_valid & ATTR_ATIME) != 0)
+                        finfo.atime = attr->ia_atime;
+                else
+                        finfo.atime = inode->i_atime;
+
+                if ((error = smb_proc_setattr(SMB_SERVER(inode),
+                                              inode, &finfo)) >= 0) {
+                        inode->i_ctime = finfo.ctime;
+                        inode->i_mtime = finfo.mtime;
+                        inode->i_atime = finfo.atime;
+                }
+        }
+
+ fail:
+        smb_invalid_dir_cache((unsigned long)(SMB_INOP(inode)->dir));
+
+	return error;
+}
+
+		
+#ifdef DEBUG_SMB_MALLOC
+int smb_malloced;
+int smb_current_malloced;
+#endif
+
+#ifdef MODULE
+
+static char kernel_version[] = UTS_RELEASE;
+
+/* looks ugly, taken from gcc-info */
+static void *shut_up_gcc = (&shut_up_gcc, kernel_version);
+
+static struct file_system_type smb_fs_type = {
+        smb_read_super, "smbfs", 0, NULL
+        };
+
+int
+init_module( void)
+{
+        DPRINTK("smbfs: init_module called\n");
+
+#ifdef DEBUG_SMB_MALLOC
+        smb_malloced = 0;
+        smb_current_malloced = 0;
+#endif
+
+        smb_init_dir_cache();
+        register_filesystem(&smb_fs_type);
+        return 0;
+}
+
+void
+cleanup_module(void)
+{
+        DPRINTK("smbfs: cleanup_module called\n");
+        smb_free_dir_cache();
+        unregister_filesystem(&smb_fs_type);
+#ifdef DEBUG_SMB_MALLOC
+        printk("smb_malloced: %d\n", smb_malloced);
+        printk("smb_current_malloced: %d\n", smb_current_malloced);
+#endif
+}
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/ioctl.c linux/fs/smbfs/ioctl.c
--- v1.3.6/linux/fs/smbfs/ioctl.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/ioctl.c	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,59 @@
+/*
+ *  ioctl.c
+ *
+ *  Copyright (C) 1995 by Volker Lendecke
+ *
+ */
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/smb_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+int
+smb_ioctl (struct inode * inode, struct file * filp,
+           unsigned int cmd, unsigned long arg)
+{
+	int result;
+
+	switch (cmd) {
+        case SMB_IOC_GETMOUNTUID:
+                if ((result = verify_area(VERIFY_WRITE, (uid_t*) arg,
+                                          sizeof(uid_t))) != 0) {
+                        return result;
+                }
+                put_fs_word(SMB_SERVER(inode)->m.mounted_uid, (uid_t*) arg);
+                return 0;
+        default:
+		return -EINVAL;
+	}
+}
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/mmap.c linux/fs/smbfs/mmap.c
--- v1.3.6/linux/fs/smbfs/mmap.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/mmap.c	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,141 @@
+/*
+ *  mmap.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/smb_fs.h>
+#include <linux/fcntl.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long 
+smb_file_mmap_nopage(struct vm_area_struct * area,
+		     unsigned long address, unsigned long page, int no_share)
+{
+	struct inode * inode = area->vm_inode;
+	unsigned int clear;
+	unsigned long tmp;
+	int n;
+	int i;
+	int pos;
+
+	address &= PAGE_MASK;
+	pos = address - area->vm_start + area->vm_offset;
+
+	clear = 0;
+	if (address + PAGE_SIZE > area->vm_end) {
+		clear = address + PAGE_SIZE - area->vm_end;
+	}
+
+        /* what we can read in one go */
+	n = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 3 - 10;
+
+        if (smb_make_open(inode, O_RDONLY) < 0) {
+                clear = PAGE_SIZE;
+        }
+        else
+        {
+        
+                for (i = 0; i < (PAGE_SIZE - clear); i += n) {
+                        int hunk, result;
+
+                        hunk = PAGE_SIZE - i;
+                        if (hunk > n)
+                                hunk = n;
+
+                        DDPRINTK("smb_file_mmap_nopage: reading\n");
+                        DDPRINTK("smb_file_mmap_nopage: pos = %d\n", pos);
+                        result = smb_proc_read(SMB_SERVER(inode),
+                                               SMB_FINFO(inode), pos, hunk,
+                                               (char *) (page + i), 0);
+                        DDPRINTK("smb_file_mmap_nopage: result= %d\n", result);
+                        if (result < 0)
+                                break;
+                        pos += result;
+                        if (result < n) {
+                                i += result;
+                                break;
+                        }
+                }
+        }
+
+	tmp = page + PAGE_SIZE;
+	while (clear--) {
+		*(char *)--tmp = 0;
+	}
+	return page;
+}
+
+struct vm_operations_struct smb_file_mmap = {
+	NULL,			/* open */
+	NULL,			/* close */
+	NULL,			/* unmap */
+	NULL,			/* protect */
+	NULL,			/* sync */
+	NULL,			/* advise */
+	smb_file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	NULL,			/* swapout */
+	NULL,			/* swapin */
+};
+
+
+/* This is used for a general mmap of a smb file */
+int
+smb_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+        DPRINTK("smb_mmap: called\n");
+
+        /* only PAGE_COW or read-only supported now */
+	if (vma->vm_flags & VM_SHARED)	
+		return -EINVAL;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+
+	vma->vm_inode = inode;
+	inode->i_count++;
+	vma->vm_ops = &smb_file_mmap;
+	return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v1.3.6/linux/fs/smbfs/proc.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/proc.c	Sat Jul  1 19:00:18 1995
@@ -0,0 +1,1917 @@
+/*
+ *  proc.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+
+#include <linux/fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_fs.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <asm/segment.h>
+#include <asm/string.h>
+
+#define ARCH i386
+#define SMB_VWV(packet)  ((packet) + SMB_HEADER_LEN)
+#define SMB_CMD(packet)  ((packet)[8])
+#define SMB_WCT(packet)  ((packet)[SMB_HEADER_LEN - 1])
+#define SMB_BCC(packet)  smb_bcc(packet)
+#define SMB_BUF(packet)  ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
+
+#define SMB_DIRINFO_SIZE 43
+#define SMB_STATUS_SIZE  21
+
+#define HI_WORD(l) ((word)(l >> 16))
+#define LO_WORD(l) ((word)(l % 0xFFFF))
+
+void smb_printerr(int class, int num);
+int smb_request(struct smb_server *);
+static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Encoding/Decoding section                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+static byte *
+smb_encode_word(byte *p, word data)
+{
+#if (ARCH == i386)
+        *((word *)p) = data;
+#else
+	p[0] = data & 0x00ffU;
+	p[1] = (data & 0xff00U) >> 8;
+#error "Non-Intel"
+#endif
+	return &p[2];
+}
+
+static byte *
+smb_decode_word(byte *p, word *data)
+{
+#if (ARCH == i386)
+	*data = *(word *)p;
+#else
+	*data = (word) p[0] | p[1] << 8;
+#endif 
+	return &p[2];
+}
+
+static byte *
+smb_decode_dword(byte *p, dword *data)
+{
+#if (ARCH == i386)
+	*data = *((dword *)p);
+#else
+	*data = (dword)p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+#endif
+	return &p[4];
+}
+
+static byte *
+smb_encode_smb_length(byte *p, dword len)
+{
+	p[0] = p[1] = 0;
+	p[2] = (len & 0xFF00) >> 8;
+	p[3] = (len & 0xFF);
+	if (len > 0xFFFF)
+		p[1] |= 0x01;
+	return &p[4];
+}
+
+static byte *
+smb_encode_dialect(byte *p, const byte *name, int len)
+{
+	*p ++ = 2;
+	strcpy(p, name);
+	return p + len + 1;
+}
+
+static byte *
+smb_encode_ascii(byte *p, const byte *name, int len)
+{
+	*p ++ = 4;
+	strcpy(p, name);
+	return p + len + 1;
+}
+
+static byte *
+smb_encode_vblock(byte *p, const byte *data, word len, int fs)
+{
+	*p ++ = 5;
+	p = smb_encode_word(p, len);
+	if (fs)
+		memcpy_fromfs(p, data, len);
+	else
+		memcpy(p, data, len);
+	return p + len;
+}
+
+static byte *
+smb_decode_data(byte *p, byte *data, word *data_len, int fs)
+{
+        word len;
+
+	if (!(*p == 1 || *p == 5)) {
+                printk("smb_decode_data: Warning! Data block not starting "
+                       "with 1 or 5\n");
+        }
+
+        len = WVAL(p, 1);
+        p += 3;
+
+        if (fs)
+                memcpy_tofs(data, p, len);
+        else
+                memcpy(data, p, len);
+
+        *data_len = len;
+
+        return p + len;
+}
+
+static byte *
+smb_name_mangle(byte *p, const byte *name)
+{
+	int len, pad = 0;
+
+	len = strlen(name);
+
+	if (len < 16)
+		pad = 16 - len;
+
+	*p ++ = 2 * (len + pad);
+
+	while (*name) {
+		*p ++ = (*name >> 4) + 'A';
+		*p ++ = (*name & 0x0F) + 'A';
+		name ++;
+	}
+	while (pad --) {
+		*p ++ = 'C';
+		*p ++ = 'A';
+	}
+	*p++ = '\0';
+	
+	return p;
+}
+
+/* The following are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+static int
+utc2local(int time)
+{
+        return time - sys_tz.tz_minuteswest*60;
+}
+
+static int
+local2utc(int time)
+{
+        return time + sys_tz.tz_minuteswest*60;
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+static int
+date_dos2unix(unsigned short time,unsigned short date)
+{
+	int month,year,secs;
+
+	month = ((date >> 5) & 15)-1;
+	year = date >> 9;
+	secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+	    ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
+	    month < 2 ? 1 : 0)+3653);
+			/* days since 1.1.70 plus 80's leap day */
+	return local2utc(secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+static void
+date_unix2dos(int unix_date,unsigned short *time, unsigned short *date)
+{
+	int day,year,nl_day,month;
+
+	unix_date = utc2local(unix_date);
+	*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+	    (((unix_date/3600) % 24) << 11);
+	day = unix_date/86400-3652;
+	year = day/365;
+	if ((year+3)/4+365*year > day) year--;
+	day -= (year+3)/4+365*year;
+	if (day == 59 && !(year & 3)) {
+		nl_day = day;
+		month = 2;
+	}
+	else {
+		nl_day = (year & 3) || day <= 59 ? day : day-1;
+		for (month = 0; month < 12; month++)
+			if (day_n[month] > nl_day) break;
+	}
+	*date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
+
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Support section.                                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+dword
+smb_len(byte *packet) 
+{
+	return ((packet[1] & 0x1) << 16L) | (packet[2] << 8L) | (packet[3]);
+}
+
+static word
+smb_bcc(byte *packet)
+{
+	int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
+#if (ARCH == i386)
+	return *((word *)((byte *)packet + pos));
+#else
+	return packet[pos] | packet[pos+1] << 8;
+#endif
+}
+
+/* smb_valid_packet: We check if packet fulfills the basic
+   requirements of a smb packet */
+
+static int
+smb_valid_packet(byte *packet)
+{
+        DDPRINTK("len: %ld, wct: %d, bcc: %d\n",
+                 smb_len(packet), SMB_WCT(packet), SMB_BCC(packet));
+	return (   packet[4] == 0xff
+                && packet[5] == 'S'
+                && packet[6] == 'M'
+                && packet[7] == 'B'
+                && (smb_len(packet) + 4 == SMB_HEADER_LEN
+                    + SMB_WCT(packet) * 2 + SMB_BCC(packet)));
+}
+
+/* smb_verify: We check if we got the answer we expected, and if we
+   got enough data. If bcc == -1, we don't care. */
+
+static int
+smb_verify(byte *packet, int command, int wct, int bcc)
+{
+	return (SMB_CMD(packet) == command &&
+		SMB_WCT(packet) >= wct &&
+		(bcc == -1 || SMB_BCC(packet) == bcc)) ? 0 : -EIO;
+}
+
+static int
+smb_errno(int errcls, int error)
+{
+
+#if DEBUG_SMB > 1
+	if (errcls) {
+		printk("smb_errno: ");
+		smb_printerr(errcls, error);
+		printk("\n");
+	}
+#endif
+
+	if (errcls == ERRDOS) 
+		switch (error) {
+			case ERRbadfunc:    return EINVAL;
+			case ERRbadfile:    return ENOENT;
+			case ERRbadpath:    return ENOENT;
+			case ERRnofids:     return EMFILE;
+			case ERRnoaccess:   return EACCES;
+			case ERRbadfid:     return EBADF;
+			case ERRbadmcb:     return EREMOTEIO;
+			case ERRnomem:      return ENOMEM;
+			case ERRbadmem:     return EFAULT;
+			case ERRbadenv:     return EREMOTEIO;
+			case ERRbadformat:  return EREMOTEIO;
+			case ERRbadaccess:  return EACCES;
+			case ERRbaddata:    return E2BIG;
+			case ERRbaddrive:   return ENXIO;
+			case ERRremcd:      return EREMOTEIO;
+			case ERRdiffdevice: return EXDEV;
+			case ERRnofiles:    return 0;
+			case ERRbadshare:   return ETXTBSY;
+			case ERRlock:       return EDEADLOCK;
+			case ERRfilexists:  return EEXIST;
+			case 87:            return 0; /* Unknown error!! */
+			default:            return EIO;
+		}
+	else if (errcls == ERRSRV) 
+		switch (error) {
+			case ERRerror: return ENFILE;
+			case ERRbadpw: return EINVAL;
+			case ERRbadtype: return EIO;
+			case ERRaccess: return EACCES;
+			default: return EIO;
+		}
+	else if (errcls == ERRHRD) 
+		switch (error) {
+			case ERRnowrite: return EROFS; 
+			case ERRbadunit: return ENODEV;
+			case ERRnotready: return EUCLEAN;
+			case ERRbadcmd: return EIO;
+			case ERRdata: return EIO;
+			case ERRbadreq: return ERANGE;
+			case ERRbadshare: return ETXTBSY;
+			case ERRlock: return EDEADLOCK;
+			default: return EIO;
+		}
+	else if (errcls == ERRCMD) 
+	        return EIO;
+	return 0;
+}
+
+#if DEBUG_SMB > 0
+static char
+print_char(char c)
+{
+	if ((c < ' ') || (c > '~'))
+		return '.';
+	return c;
+}
+
+static void
+smb_dump_packet(byte *packet) {
+	int i, j, len;
+        int errcls, error;
+
+        errcls = (int)packet[9];
+        error  = (int)(int)(packet[11]|packet[12]<<8);
+
+	printk("smb_len = %d  valid = %d    \n", 
+	       len = smb_len(packet), smb_valid_packet(packet));
+	printk("smb_cmd = %d  smb_wct = %d  smb_bcc = %d\n", 
+	       packet[8], SMB_WCT(packet), SMB_BCC(packet)); 
+	printk("smb_rcls = %d smb_err = %d\n", errcls, error);
+
+	if (errcls) {
+		smb_printerr(errcls, error);
+		printk("\n");
+	}
+
+        if (len > 100)
+                len = 100;
+	
+	for (i = 0; i < len; i += 10) {
+		printk("%03d:", i);
+		for (j = i; j < i+10; j++)
+			if (j < len)
+				printk("%02x ", packet[j]);
+			else
+				printk("   ");
+		printk(": ");
+	        for (j = i; j < i+10; j++)
+			if (j < len)
+				printk("%c", print_char(packet[j]));
+		printk("\n");
+	}
+}
+#endif
+
+static void
+smb_lock_server(struct smb_server *server)
+{
+        while (server->lock)
+		sleep_on(&server->wait);
+	server->lock = 1;
+}
+
+static void
+smb_unlock_server(struct smb_server *server)
+{
+        if (server->lock != 1) {
+                printk("smb_unlock_server: was not locked!\n");
+        }
+
+        server->lock = 0;
+        wake_up(&server->wait);
+}
+        
+/* smb_request_ok: We expect the server to be locked. Then we do the
+   request and check the answer completely. When smb_request_ok
+   returns 0, you can be quite sure that everything went well. When
+   the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_server *s, int command, int wct, int bcc)
+{
+        int result = 0;
+        s->rcls = 0;
+        s->err  = 0;
+
+        if (smb_request(s) < 0) {
+                DPRINTK("smb_request failed\n");
+                result = -EIO;
+        }
+        else if (smb_valid_packet(s->packet) != 0) {
+                DPRINTK("not a valid packet!\n");
+                result = -EIO;
+        }
+        else if (s->rcls != 0) {
+                result =  -smb_errno(s->rcls, s->err);
+        }
+        else if (smb_verify(s->packet, command, wct, bcc) != 0) {
+                DPRINTK("smb_verify failed\n");
+                result = -EIO;
+        }
+
+        return result;
+}
+
+/* smb_retry: This function should be called when smb_request_ok has
+   indicated an error. If the error was indicated because the
+   connection was killed, we try to reconnect. If smb_retry returns 0,
+   the error was indicated for another reason, so a retry would not be
+   of any use. */
+
+static int
+smb_retry(struct smb_server *server)
+{
+        if (server->state != CONN_INVALID) {
+                return 0;
+        }
+        
+        if (smb_release(server) < 0) {
+                DPRINTK("smb_retry: smb_release failed\n");
+                server->state = CONN_RETRIED;
+                return 0;
+        }
+        if(smb_proc_reconnect(server) < 0) {
+                DPRINTK("smb_proc_reconnect failed\n");
+                server->state = CONN_RETRIED;
+                return 0;
+        }
+
+        server->state = CONN_VALID;
+        return 1;
+}
+
+static int
+smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
+{
+        int result = smb_request_ok(s, command, wct, bcc);
+
+        smb_unlock_server(s);
+
+        return result;
+}
+        
+/* smb_setup_header: We completely set up the packet. You only have to
+   insert the command-specific fields */
+
+static byte *
+smb_setup_header(struct smb_server *server, byte command, word wct, word bcc)
+{
+	dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
+	byte *p = server->packet;
+        byte *buf = server->packet;
+
+	p = smb_encode_smb_length(p, xmit_len);
+
+        BSET(p,0,0xff);
+        BSET(p,1,'S');
+        BSET(p,2,'M');
+        BSET(p,3,'B');
+        BSET(p,4,command);
+
+        p += 5;
+	memset(p, '\0', 19);
+	p += 19;
+        p += 8;
+
+        WSET(buf, smb_tid, server->tid);
+        WSET(buf, smb_pid, server->pid);
+        WSET(buf, smb_uid, server->server_uid);
+        WSET(buf, smb_mid, server->mid);
+
+        if (server->protocol > PROTOCOL_CORE) {
+                BSET(buf, smb_flg, 0x8);
+                WSET(buf, smb_flg2, 0x3);
+        }
+        
+	*p++ = wct;		/* wct */
+        p += 2*wct;
+        WSET(p, 0, bcc);
+        return p+2;
+}
+
+/* smb_setup_header_exclusive waits on server->lock and locks the
+   server, when it's free. You have to unlock it manually when you're
+   finished with server->packet! */
+
+static byte *
+smb_setup_header_exclusive(struct smb_server *server,
+                           byte command, word wct, word bcc)
+{
+        smb_lock_server(server);
+        return smb_setup_header(server, command, wct, bcc);
+}
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  File operation section.                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+int
+smb_proc_open(struct smb_server *server, const char *pathname, int len,
+              struct smb_dirent *entry)
+{
+	int error;
+	char* p;
+        char* buf = server->packet;
+	const word o_attr = aSYSTEM | aHIDDEN | aDIR;
+
+        DPRINTK("smb_proc_open: path=%s\n", pathname);
+
+        smb_lock_server(server);
+
+ retry:
+        p = smb_setup_header(server, SMBopen, 2, 2 + len);
+        WSET(buf, smb_vwv0, 0x42); /* read/write */
+        WSET(buf, smb_vwv1, o_attr);
+        smb_encode_ascii(p, pathname, len);
+
+        if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) {
+
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                
+                if (error != -EACCES) {
+                        smb_unlock_server(server);
+                        return error;
+                }
+
+                p = smb_setup_header(server, SMBopen, 2, 2 + len);
+                WSET(buf, smb_vwv0, 0x40); /* read only */
+                WSET(buf, smb_vwv1, o_attr);
+                smb_encode_ascii(p, pathname, len);
+
+                if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) {
+                        if (smb_retry(server)) {
+                                goto retry;
+                        }
+                        smb_unlock_server(server);
+                        return error;
+                }
+        }
+
+	/* We should now have data in vwv[0..6]. */
+
+        entry->fileid = WVAL(buf, smb_vwv0);
+        entry->attr   = WVAL(buf, smb_vwv1);
+        entry->ctime = entry->atime =
+                entry->mtime = local2utc(DVAL(buf, smb_vwv2));
+        entry->size   = DVAL(buf, smb_vwv4);
+        entry->access = WVAL(buf, smb_vwv6);
+
+        smb_unlock_server(server);
+
+	entry->access &= 3;
+        DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
+	return 0;
+}
+
+/* smb_proc_close: in finfo->mtime we can send a modification time to
+   the server */
+int
+smb_proc_close(struct smb_server *server, struct smb_dirent *finfo)
+{
+        char *buf = server->packet;
+
+	smb_setup_header_exclusive(server, SMBclose, 3, 0);
+        WSET(buf, smb_vwv0, finfo->fileid);
+        DSET(buf, smb_vwv1, utc2local(finfo->mtime));
+
+        return smb_request_ok_unlock(server, SMBclose, 0, 0);
+}
+
+/* In smb_proc_read and smb_proc_write we do not retry, because the
+   file-id would not be valid after a reconnection. */
+
+/* smb_proc_read: fs indicates if it should be copied with
+   memcpy_tofs. */
+
+int
+smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, 
+              off_t offset, long count, char *data, int fs)
+{
+	word returned_count, data_len;
+        char *buf = server->packet;
+        int error;
+
+	smb_setup_header_exclusive(server, SMBread, 5, 0);
+
+        WSET(buf, smb_vwv0, finfo->fileid);
+        WSET(buf, smb_vwv1, count);
+        DSET(buf, smb_vwv2, offset);
+        WSET(buf, smb_vwv4, 0);
+	
+	if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0) {
+                smb_unlock_server(server);
+		return error;
+        }
+
+	returned_count = WVAL(buf, smb_vwv0);
+	
+	smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
+
+        smb_unlock_server(server);
+
+	if (returned_count != data_len) {
+		printk("smb_proc_read: Warning, returned_count != data_len\n");
+                printk("smb_proc_read: ret_c=%d, data_len=%d\n",
+                       returned_count, data_len);
+        }
+
+        return data_len;
+}
+
+int
+smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
+               off_t offset, int count, char *data)
+{
+        int res = 0;
+        char *buf = server->packet;
+        byte *p;
+
+	p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
+        WSET(buf, smb_vwv0, finfo->fileid);
+        WSET(buf, smb_vwv1, count);
+        DSET(buf, smb_vwv2, offset);
+        WSET(buf, smb_vwv4, 0);
+
+        *p++ = 1;
+        WSET(p, 0, count);
+        memcpy_fromfs(p+2, data, count);
+
+	if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0) {
+                res = WVAL(buf, smb_vwv0);
+        }
+
+        smb_unlock_server(server);
+
+	return res;
+}
+
+/* smb_proc_do_create: We expect entry->attry & entry->ctime to be set. */
+
+static int
+smb_proc_do_create(struct smb_server *server, const char *path, int len, 
+                   struct smb_dirent *entry, word command)
+{
+	int error;
+	char *p;
+        char *buf = server->packet;
+
+	smb_lock_server(server);
+ retry:
+	p = smb_setup_header(server, command, 3, len + 2);
+        WSET(buf, smb_vwv0, entry->attr);
+        DSET(buf, smb_vwv1, utc2local(entry->ctime));
+	smb_encode_ascii(p, path, len);
+
+	if ((error = smb_request_ok(server, command, 1, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                smb_unlock_server(server);
+		return error;
+        }
+
+        entry->opened = 1;
+        entry->fileid = WVAL(buf, smb_vwv0);
+        smb_unlock_server(server);
+
+        smb_proc_close(server, entry);
+
+	return 0;
+}
+	
+int
+smb_proc_create(struct smb_server *server, const char *path, int len,
+                struct smb_dirent *entry)
+{
+	return smb_proc_do_create(server, path, len, entry, SMBcreate);
+}
+
+int
+smb_proc_mknew(struct smb_server *server, const char *path, int len,
+               struct smb_dirent *entry)
+{
+	return smb_proc_do_create(server, path, len, entry, SMBmknew);
+}
+
+int
+smb_proc_mv(struct smb_server *server, 
+            const char *opath, const int olen,
+            const char *npath, const int nlen)
+{
+	char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBmv, 1, olen + nlen + 4);
+        WSET(buf, smb_vwv0, 0);
+	p = smb_encode_ascii(p, opath, olen);
+	smb_encode_ascii(p, npath, olen);
+
+        if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_mkdir(struct smb_server *server, const char *path, const int len)
+{
+	char *p;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBmkdir, 0, 2 + len);
+	smb_encode_ascii(p, path, len);
+
+        if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_rmdir(struct smb_server *server, const char *path, const int len)
+{
+	char *p;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBrmdir, 0, 2 + len);
+	smb_encode_ascii(p, path, len);
+
+        if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_unlink(struct smb_server *server, const char *path, const int len)
+{
+	char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBunlink, 1, 2 + len);
+        WSET(buf, smb_vwv0, 0);
+	smb_encode_ascii(p, path, len);
+
+        if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_trunc(struct smb_server *server, word fid, dword length)
+{
+        char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+        p = smb_setup_header(server, SMBwrite, 5, 3);
+        WSET(buf, smb_vwv0, fid);
+        WSET(buf, smb_vwv1, 0);
+        DSET(buf, smb_vwv2, length);
+        WSET(buf, smb_vwv4, 0);
+	smb_encode_ascii(p, "", 0);
+	
+        if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+static char *
+smb_decode_dirent(char *p, struct smb_dirent *entry)
+{
+	p += SMB_STATUS_SIZE;                  /* reserved (search_status) */
+        entry->attr = BVAL(p, 0);
+        entry->mtime = entry->atime = entry->ctime =
+                date_dos2unix(WVAL(p, 1), WVAL(p, 3));
+        entry->size = DVAL(p, 5);
+        memcpy(entry->path, p+9, 13);
+	DDPRINTK("smb_decode_dirent: path = %s\n", entry->path);
+	return p + 22;
+}
+
+/* This routine is used to read in directory entries from the network.
+   Note that it is for short directory name seeks, i.e.: protocol <
+   PROTOCOL_LANMAN2 */
+
+static int
+smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
+		       int cache_size, struct smb_dirent *entry)
+{
+	char *p;
+        char *buf;
+	int error;
+        int result;
+	int i;
+	int first, total_count;
+        struct smb_dirent *current_entry;
+	word bcc;
+	word count;
+	char status[SMB_STATUS_SIZE];
+	int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
+	int dirlen = strlen(SMB_FINFO(dir)->path);
+	char mask[dirlen + 5];
+
+	strcpy(mask, SMB_FINFO(dir)->path);
+	strcat(mask, "\\*.*");
+
+ 	DPRINTK("SMB call  readdir %d @ %d\n", cache_size, fpos);        
+	DPRINTK("          mask = %s\n", mask);
+
+        buf = server->packet;
+
+        smb_lock_server(server);
+
+ retry:
+	first = 1;
+        total_count = 0;
+        current_entry = entry;
+	
+	while (1) {
+		if (first == 1) {
+			p = smb_setup_header(server, SMBsearch, 2,
+                                             5 + strlen(mask));
+                        WSET(buf, smb_vwv0, entries_asked);
+                        WSET(buf, smb_vwv1, aDIR);
+			p = smb_encode_ascii(p, mask, strlen(mask));
+			*p ++ = 5;
+		        p = smb_encode_word(p, 0);
+		} else {
+			p = smb_setup_header(server, SMBsearch, 2,
+                                             5 + SMB_STATUS_SIZE);
+                        WSET(buf, smb_vwv0, entries_asked);
+                        WSET(buf, smb_vwv1, aDIR);
+			p = smb_encode_ascii(p, "", 0);
+			p = smb_encode_vblock(p, status, SMB_STATUS_SIZE, 0);
+		}
+		
+		if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0) {
+                        if (   (server->rcls == ERRDOS)
+                            && (server->err  == ERRnofiles)) {
+                                result = total_count - fpos;
+                                goto unlock_return;
+                        }
+                        else
+                        {
+                                if (smb_retry(server)) {
+                                        goto retry;
+                                }
+                                result = error;
+                                goto unlock_return;
+                        }
+                }
+
+		p = SMB_VWV(server->packet);
+		p = smb_decode_word(p, &count); /* vwv[0] = count-returned */
+		p = smb_decode_word(p, &bcc);           
+		
+		first = 0;
+		
+		if (count <= 0) {
+			result = total_count - fpos;
+                        goto unlock_return;
+                }
+		if (bcc != count * SMB_DIRINFO_SIZE + 3) {
+			result = -EIO;
+                        goto unlock_return;
+                }
+
+		p += 3; /* Skipping VBLOCK header (5, length lo, length hi). */
+
+		/* Read the last entry into the status field. */
+		memcpy(status,
+                       SMB_BUF(server->packet) + 3 +
+                       (count - 1) * SMB_DIRINFO_SIZE, 
+                       SMB_STATUS_SIZE);
+
+		/* Now we are ready to parse smb directory entries. */
+		
+		for (i = 0; i < count; i ++) {
+			if (total_count < fpos) {
+				p += SMB_DIRINFO_SIZE;
+				DDPRINTK("smb_proc_readdir: skipped entry.\n");
+				DDPRINTK("                  total_count = %d\n"
+                                         "                i = %d, fpos = %d\n",
+                                         total_count, i, fpos);
+                        }
+                        else if (total_count >= fpos + cache_size) {
+                                result = total_count - fpos;
+                                goto unlock_return;
+			}
+			else {
+				p = smb_decode_dirent(p, current_entry);
+				current_entry->f_pos = total_count;
+				DDPRINTK("smb_proc_readdir: entry->f_pos = "
+                                         "%lu\n", entry->f_pos);	
+				current_entry += 1;
+			}
+			total_count += 1;
+		}
+	}
+ unlock_return:
+        smb_unlock_server(server);
+        return result;
+}
+
+/* interpret a long filename structure - this is mostly guesses at the
+   moment.  The length of the structure is returned.  The structure of
+   a long filename depends on the info level. 260 is used by NT and 2
+   is used by OS/2. */
+
+static char *
+smb_decode_long_dirent(char *p, struct smb_dirent *finfo, int level)
+{
+        char *result;
+
+        if (finfo) {
+                /* I have to set times to 0 here, because I do not
+                   have specs about this for all info levels. */
+                finfo->ctime = finfo->mtime = finfo->atime = 0;
+        }
+
+        switch (level)
+        {
+        case 1:                 /* OS/2 understands this */
+                if (finfo)
+                {
+                        DPRINTK("received entry\n");
+                        strcpy(finfo->path,p+27);
+                        finfo->len  = strlen(finfo->path);
+                        finfo->size = DVAL(p,16);
+                        finfo->attr = BVAL(p,24);
+
+                        finfo->ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
+                        finfo->atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
+                        finfo->mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
+                }
+                result = p + 28 + BVAL(p,26);
+                break;
+
+        case 2:                 /* this is what OS/2 uses */
+                if (finfo)
+                {
+                        strcpy(finfo->path,p+31);
+                        finfo->len  = strlen(finfo->path);
+                        finfo->size = DVAL(p,16);
+                        finfo->attr = BVAL(p,24);
+#if 0
+                        finfo->atime = make_unix_date2(p+8);
+                        finfo->mtime = make_unix_date2(p+12);
+#endif
+                }
+                result = p + 32 + BVAL(p,30);
+                break;
+
+        case 260:               /* NT uses this, but also accepts 2 */
+                result = p + WVAL(p,0);
+                if (finfo)
+                {
+                        int namelen;
+                        p += 4; /* next entry offset */
+                        p += 4; /* fileindex */
+                        /* finfo->ctime = interpret_filetime(p);*/
+                        p += 8;
+                        /* finfo->atime = interpret_filetime(p);*/
+                        p += 8;
+                        p += 8; /* write time */
+                        /* finfo->mtime = interpret_filetime(p);*/
+                        p += 8;
+                        finfo->size = DVAL(p,0);
+                        p += 8;
+                        p += 8; /* alloc size */
+                        finfo->attr = BVAL(p,0);
+                        p += 4;
+                        namelen = min(DVAL(p,0), SMB_MAXNAMELEN);
+                        p += 4;
+                        p += 4; /* EA size */
+                        p += 2; /* short name len? */
+                        p += 24; /* short name? */	  
+                        strncpy(finfo->path,p,namelen);
+                        finfo->len = namelen;
+                }
+                break;
+
+        default:
+                DPRINTK("Unknown long filename format %d\n",level);
+                result = p + WVAL(p,0);
+        }
+        return result;
+}
+
+int
+smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
+                      int cache_size, struct smb_dirent *entry)
+{
+        int max_matches = 512;
+  
+        /* NT uses 260, OS/2 uses 2. Both accept 1. */
+        int info_level = 1;
+
+	char *p;
+	int i;
+	int first, total_count;
+        struct smb_dirent *current_entry;
+
+        char *resp_data;
+        char *resp_param;
+        int resp_data_len = 0;
+        int resp_param_len=0;
+
+        int attribute = aSYSTEM | aHIDDEN | aDIR;
+        int result;
+
+        int ff_resume_key = 0;
+        int ff_searchcount=0;
+        int ff_eos=0;
+        int ff_lastname=0;
+        int ff_dir_handle=0;
+        int loop_count = 0;
+
+	int dirlen = strlen(SMB_FINFO(dir)->path);
+	char mask[dirlen + 5];
+
+	strcpy(mask, SMB_FINFO(dir)->path);
+	strcat(mask, "\\*");
+
+ 	DPRINTK("SMB call lreaddir %d @ %d\n", cache_size, fpos);        
+	DPRINTK("          mask = %s\n", mask);
+
+        resp_param = NULL;
+        resp_data  = NULL;
+
+        smb_lock_server(server);
+
+ retry:
+
+	first = 1;
+        total_count = 0;
+        current_entry = entry;
+	
+        while (ff_eos == 0)
+        {
+                int masklen = strlen(mask);
+                unsigned char *outbuf = server->packet;
+                
+                loop_count += 1;
+                if (loop_count > 200)
+                {
+                        printk("smb_proc_readdir_long: "
+                               "Looping in FIND_NEXT??\n");
+                        break;
+                }
+
+                smb_setup_header(server, SMBtrans2, 15,
+                                 5 + 12 + masklen + 1);
+
+                WSET(outbuf,smb_tpscnt,12 + masklen +1);
+                WSET(outbuf,smb_tdscnt,0);
+                WSET(outbuf,smb_mprcnt,10); 
+                WSET(outbuf,smb_mdrcnt,TRANS2_MAX_TRANSFER);
+                WSET(outbuf,smb_msrcnt,0);
+                WSET(outbuf,smb_flags,0); 
+                DSET(outbuf,smb_timeout,0);
+                WSET(outbuf,smb_pscnt,WVAL(outbuf,smb_tpscnt));
+                WSET(outbuf,smb_psoff,((SMB_BUF(outbuf)+3) - outbuf)-4);
+                WSET(outbuf,smb_dscnt,0);
+                WSET(outbuf,smb_dsoff,0);
+                WSET(outbuf,smb_suwcnt,1);
+                WSET(outbuf,smb_setup0,
+                     first == 1 ? TRANSACT2_FINDFIRST : TRANSACT2_FINDNEXT);
+
+                p = SMB_BUF(outbuf);
+                *p++=0;         /* put in a null smb_name */
+                *p++='D'; *p++ = ' '; /* this was added because OS/2 does it */
+
+                if (first != 0)
+                {
+                        WSET(p,0,attribute); /* attribute */
+                        WSET(p,2,max_matches); /* max count */
+                        WSET(p,4,8+4+2); /* resume required + close on end +
+                                            continue */
+                        WSET(p,6,info_level); 
+                        DSET(p,8,0);
+                        p += 12;
+                        strncpy(p, mask, masklen);
+                        p += masklen;
+                        *p++ = 0; *p++ = 0;
+                }
+                else
+                {
+                        DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
+                                ff_dir_handle,ff_resume_key,ff_lastname,mask);
+                        WSET(p,0,ff_dir_handle);
+                        WSET(p,2,max_matches); /* max count */
+                        WSET(p,4,info_level); 
+                        DSET(p,6,ff_resume_key); /* ff_resume_key */
+                        WSET(p,10,8+4+2); /* resume required + close on end +
+                                             continue */
+                        p += 12;
+                        strncpy(p, mask, masklen);
+                        p += masklen;
+                        *p++ = 0; *p++ = 0;
+                }
+
+                result = smb_trans2_request(server,
+                                            &resp_data_len,&resp_param_len,
+                                            &resp_data,&resp_param);
+
+                if (result < 0) {
+                        if (smb_retry(server)) {
+                                goto retry;
+                        }
+                        DPRINTK("smb_proc_readdir_long: "
+                                "got error from trans2_request\n");
+                        break;
+                }
+
+                if (server->rcls != 0)
+                {
+                        result = -EIO;
+                        break;
+                }
+
+                /* parse out some important return info */
+                p = resp_param;
+                if (first != 0)
+                {
+                        ff_dir_handle = WVAL(p,0);
+                        ff_searchcount = WVAL(p,2);
+                        ff_eos = WVAL(p,4);
+                        ff_lastname = WVAL(p,8);
+                }
+                else
+                {
+                        ff_searchcount = WVAL(p,0);
+                        ff_eos = WVAL(p,2);
+                        ff_lastname = WVAL(p,6);
+                }
+
+                if (ff_searchcount == 0) 
+                        break;
+
+                /* point to the data bytes */
+                p = resp_data;
+
+                /* we might need the lastname for continuations */
+                if (ff_lastname > 0)
+                {
+                        switch(info_level)
+                        {
+                        case 260:
+                                ff_resume_key =0;
+                                strcpy(mask,p+ff_lastname+94);
+                                break;
+                        case 1:
+                                strcpy(mask,p + ff_lastname + 1);
+                                ff_resume_key = 0;
+                                break;
+                        }
+                }
+                else
+                        strcpy(mask,"");
+  
+		/* Now we are ready to parse smb directory entries. */
+		
+		for (i = 0; i < ff_searchcount; i ++) {
+			if (total_count < fpos) {
+				p = smb_decode_long_dirent(p, NULL,
+                                                           info_level);
+				DPRINTK("smb_proc_readdir: skipped entry.\n");
+				DDPRINTK("                  total_count = %d\n"
+                                         "                i = %d, fpos = %d\n",
+                                         total_count, i, fpos);
+                        }
+                        else if (total_count >= fpos + cache_size) {
+                                goto finished;
+			}
+			else {
+				p = smb_decode_long_dirent(p, current_entry,
+                                                           info_level);
+				current_entry->f_pos = total_count;
+				DDPRINTK("smb_proc_readdir: entry->f_pos = "
+                                         "%lu\n", entry->f_pos);	
+				current_entry += 1;
+			}
+			total_count += 1;
+		}
+
+                if (resp_data != NULL) {
+                        smb_kfree_s(resp_data,  0);
+                        resp_data = NULL;
+                }
+                if (resp_param != NULL) {
+                        smb_kfree_s(resp_param, 0);
+                        resp_param = NULL;
+                }
+
+                DPRINTK("received %d entries (eos=%d resume=%d)\n",
+                        ff_searchcount,ff_eos,ff_resume_key);
+
+                first = 0;
+        }
+
+ finished:
+        if (resp_data != NULL) {
+                smb_kfree_s(resp_data,  0);
+                resp_data = NULL;
+        }
+        if (resp_param != NULL) {
+                smb_kfree_s(resp_param, 0);
+                resp_param = NULL;
+        }
+
+        smb_unlock_server(server);
+
+        return total_count - fpos;
+}
+
+int
+smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
+		 int cache_size, struct smb_dirent *entry)
+{
+        if (server->protocol >= PROTOCOL_LANMAN2)
+                return smb_proc_readdir_long(server, dir, fpos, cache_size,
+                                             entry);
+        else
+                return smb_proc_readdir_short(server, dir, fpos, cache_size,
+                                              entry);
+}
+		
+static int
+smb_proc_getattr_core(struct smb_server *server, const char *path, int len, 
+                      struct smb_dirent *entry)
+{
+	int result;
+	char *p;
+        char *buf = server->packet;
+
+        smb_lock_server(server);
+
+        DDPRINTK("smb_proc_getattr: %s\n", path);
+
+ retry:
+	p = smb_setup_header(server, SMBgetatr, 0, 2 + len);
+	smb_encode_ascii(p, path, len);
+	
+	if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                smb_unlock_server(server);
+		return result;
+        }
+
+        entry->attr         = WVAL(buf, smb_vwv0);
+        entry->ctime = entry->atime = /* The server only tells us 1 time */
+                entry->mtime = local2utc(DVAL(buf, smb_vwv1));
+
+        entry->size         = DVAL(buf, smb_vwv3);
+        smb_unlock_server(server);
+	return 0;
+}
+
+/* smb_proc_getattrE: entry->fid must be valid */
+
+static int
+smb_proc_getattrE(struct smb_server *server, struct smb_dirent *entry)
+{
+        char* buf = server->packet;
+        int result;
+
+        smb_setup_header_exclusive(server, SMBgetattrE, 1, 0);
+        WSET(buf, smb_vwv0, entry->fileid);
+
+        if ((result = smb_request_ok(server, SMBgetattrE, 11, 0)) != 0) {
+                smb_unlock_server(server);
+                return result;
+        }
+
+        entry->ctime = date_dos2unix(WVAL(buf, smb_vwv1), WVAL(buf, smb_vwv0));
+        entry->atime = date_dos2unix(WVAL(buf, smb_vwv3), WVAL(buf, smb_vwv2));
+        entry->mtime = date_dos2unix(WVAL(buf, smb_vwv5), WVAL(buf, smb_vwv4));
+        entry->size  = DVAL(buf, smb_vwv6);
+        entry->attr  = WVAL(buf, smb_vwv10);
+
+        smb_unlock_server(server);
+        return 0;
+}
+
+int
+smb_proc_getattr(struct smb_server *server, const char *path, int len, 
+                 struct smb_dirent *entry)
+{
+        if (server->protocol >= PROTOCOL_LANMAN1) {
+
+                int result = 0;
+                struct smb_dirent temp_entry;
+
+                if ((result=smb_proc_open(server,path,len,
+                                          &temp_entry)) < 0) {
+                        /* We cannot open directories, so we try to use the
+                           core variant */
+                        return smb_proc_getattr_core(server,path,len,entry);
+                }
+
+                if ((result=smb_proc_getattrE(server, &temp_entry)) >= 0) {
+                        entry->attr  = temp_entry.attr;
+                        entry->atime = temp_entry.atime;
+                        entry->mtime = temp_entry.mtime;
+                        entry->ctime = temp_entry.ctime;
+                        entry->size  = temp_entry.size;
+                }
+                
+                smb_proc_close(server, &temp_entry);
+                return result;
+
+        } else {
+                return smb_proc_getattr_core(server, path, len, entry);
+        }
+}
+
+
+/* In core protocol, there is only 1 time to be set, we use
+   entry->mtime, to make touch work. */
+static int
+smb_proc_setattr_core(struct smb_server *server,
+                      const char *path, int len,
+                      struct smb_dirent *new_finfo)
+{
+        char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+        p = smb_setup_header(server, SMBsetatr, 8, 4 + len);
+        WSET(buf, smb_vwv0, new_finfo->attr);
+        DSET(buf, smb_vwv1, utc2local(new_finfo->mtime));
+        p = smb_encode_ascii(p, path, len);
+        p = smb_encode_ascii(p, "", 0);
+
+        if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+/* smb_proc_setattrE: we do not retry here, because we rely on fid,
+   which would not be valid after a retry. */
+static int
+smb_proc_setattrE(struct smb_server *server, word fid,
+                  struct smb_dirent *new_entry)
+{
+        char *buf = server->packet;
+        word date, time;
+
+        smb_setup_header_exclusive(server, SMBsetattrE, 7, 0);
+
+        WSET(buf, smb_vwv0, fid);
+
+        date_unix2dos(new_entry->ctime, &time, &date);
+        WSET(buf, smb_vwv1, date);
+        WSET(buf, smb_vwv2, time);
+        
+        date_unix2dos(new_entry->atime, &time, &date);
+        WSET(buf, smb_vwv3, date);
+        WSET(buf, smb_vwv4, time);
+        
+        date_unix2dos(new_entry->mtime, &time, &date);
+        WSET(buf, smb_vwv5, date);
+        WSET(buf, smb_vwv6, time);
+
+        return smb_request_ok_unlock(server, SMBsetattrE, 0, 0);
+}
+
+/* smb_proc_setattr: for protocol >= LANMAN1 we expect the file to be
+   opened for writing. */
+int
+smb_proc_setattr(struct smb_server *server, struct inode *inode,
+                 struct smb_dirent *new_finfo)
+{
+        struct smb_dirent *finfo = SMB_FINFO(inode);
+        int result;
+
+        if (server->protocol >= PROTOCOL_LANMAN1) {
+                if ((result = smb_make_open(inode, O_RDWR)) < 0)
+                        return result;
+                return smb_proc_setattrE(server, finfo->fileid, new_finfo);
+        } else {
+                return smb_proc_setattr_core(server, finfo->path, finfo->len,
+                                             new_finfo);
+        }
+}
+
+int
+smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
+{
+	int error;
+	char *p;
+	struct smb_server *server = &(SMB_SBP(super)->s_server);
+
+        smb_lock_server(server);
+
+ retry:
+	smb_setup_header(server, SMBdskattr, 0, 0);
+	
+	if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                smb_unlock_server(server);
+		return error;
+        }
+	
+	p = SMB_VWV(server->packet);
+	p = smb_decode_word(p, &attr->total);
+	p = smb_decode_word(p, &attr->allocblocks);
+	p = smb_decode_word(p, &attr->blocksize);
+	p = smb_decode_word(p, &attr->free);
+        smb_unlock_server(server);
+	return 0;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Mount/umount operations.                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+struct smb_prots {
+	enum smb_protocol prot;
+	char *name;
+};
+
+/* smb_proc_reconnect: We expect the server to be locked, so that you
+   can call the routine from within smb_retry. The socket must be
+   created, like after a user-level socket()-call. It may not be
+   connected. */
+
+int
+smb_proc_reconnect(struct smb_server *server)
+{
+	struct smb_prots prots[] =
+        { { PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
+          { PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+#ifdef LANMAN1
+          { PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+          { PROTOCOL_LANMAN1,"LANMAN1.0"},
+#endif
+#ifdef LANMAN2
+          { PROTOCOL_LANMAN2,"LM1.2X002"},
+#endif
+          {-1, NULL} };
+	char dev[] = "A:";
+	int i, plength;
+	int max_xmit = 1024;	/* Space needed for first request. */
+        int given_max_xmit = server->m.max_xmit;
+	int result;
+	byte *p;
+
+        if ((result = smb_connect(server)) < 0) {
+                DPRINTK("smb_proc_reconnect: could not smb_connect\n");
+                goto fail;
+        }
+
+        /* Here we assume that the connection is valid */
+        server->state = CONN_VALID;
+
+        if (server->packet != NULL) {
+                smb_kfree_s(server->packet, server->max_xmit);
+        }
+        
+	server->packet = smb_kmalloc(max_xmit, GFP_KERNEL);
+
+	if (server->packet == NULL) {
+		printk("smb_proc_connect: No memory! Bailing out.\n");
+                result = -ENOMEM;
+                goto fail;
+	}
+
+        server->max_xmit = max_xmit;
+
+	/*
+	 * Start with an RFC1002 session request packet.
+	 */
+	p = server->packet + 4;
+
+	p = smb_name_mangle(p, server->m.server_name);
+	p = smb_name_mangle(p, server->m.client_name);
+	
+	smb_encode_smb_length(server->packet,
+                              (void *)p - (void *)(server->packet));
+	
+	server->packet[0] = 0x81; /* SESSION REQUEST */
+
+        if (smb_catch_keepalive(server) < 0) {
+                printk("smb_proc_connect: could not catch_keepalives\n");
+        }
+        
+	if ((result = smb_request(server)) < 0) {
+		printk("smb_proc_connect: Failed to send SESSION REQUEST.\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	}
+	
+	if (server->packet[0] != 0x82) {
+		printk("smb_proc_connect: Did not recieve positive response "
+                       "(err = %x)\n", 
+		       server->packet[0]);
+                smb_dont_catch_keepalive(server);
+#if DEBUG_SMB > 0
+                smb_dump_packet(server->packet);
+#endif
+                result = -EIO;
+                goto fail;
+	}
+
+        DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
+	
+	/* Now we are ready to send a SMB Negotiate Protocol packet. */
+	memset(server->packet, 0, SMB_HEADER_LEN);
+
+	plength = 0;
+	for (i = 0; prots[i].name != NULL; i++) {
+		plength += strlen(prots[i].name) + 2;
+        }
+
+	smb_setup_header(server, SMBnegprot, 0, plength);
+
+	p = SMB_BUF(server->packet);
+	
+	for (i = 0; prots[i].name != NULL; i++) {
+		p = smb_encode_dialect(p,prots[i].name, strlen(prots[i].name));
+        }
+	
+	if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0) {
+         	printk("smb_proc_connect: Failure requesting SMBnegprot\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	} else {
+                DDPRINTK("smb_proc_connect: Request SMBnegprot..");
+        }
+
+        DDPRINTK("Verified!\n");
+
+        p = SMB_VWV(server->packet);
+	p = smb_decode_word(p, (word *)&i);
+        server->protocol = prots[i].prot;
+
+	DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
+                prots[i].name);
+
+        if (server->protocol > PROTOCOL_LANMAN1) {
+
+                word passlen = strlen(server->m.password);
+                word userlen = strlen(server->m.username);
+                
+                DPRINTK("smb_proc_connect: password = %s\n",
+                        server->m.password);
+                DPRINTK("smb_proc_connect: usernam = %s\n",
+                        server->m.username);
+
+                p = smb_decode_word(p, &(server->maxxmt));
+                p = smb_decode_word(p, &(server->maxmux));
+                p = smb_decode_word(p, &(server->maxvcs));
+                p = smb_decode_word(p, &(server->blkmode));
+                p = smb_decode_dword(p, &(server->sesskey));
+
+                smb_setup_header(server, SMBsesssetupX, 10,
+                                 2 + userlen + passlen);
+
+                WSET(server->packet, smb_vwv0, 0x00ff);
+                WSET(server->packet, smb_vwv1, 0);
+                WSET(server->packet, smb_vwv2, given_max_xmit);
+                WSET(server->packet, smb_vwv3, 2);
+                WSET(server->packet, smb_vwv4, server->pid);
+                DSET(server->packet, smb_vwv5, server->sesskey);
+                WSET(server->packet, smb_vwv7, passlen + 1);
+                WSET(server->packet, smb_vwv8, 0);
+                WSET(server->packet, smb_vwv9, 0);
+
+                p = SMB_BUF(server->packet);
+                strcpy(p, server->m.password);
+                p += passlen + 1;
+                strcpy(p, server->m.username);
+
+                if ((result = smb_request_ok(server,SMBsesssetupX,3,0)) < 0) {
+                        DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
+                        smb_dont_catch_keepalive(server);
+                        goto fail;
+                }
+                smb_decode_word(server->packet+32, &(server->server_uid));
+        }
+
+	/* Fine! We have a connection, send a tcon message. */
+
+	smb_setup_header(server, SMBtcon, 0,
+                         6 + strlen(server->m.service) +
+                         strlen(server->m.password) + strlen(dev));
+	p = SMB_BUF(server->packet);
+	p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
+	p = smb_encode_ascii(p,server->m.password, strlen(server->m.password));
+	p = smb_encode_ascii(p, dev, strlen(dev));
+
+	if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0) {
+		DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	}
+
+        DDPRINTK("OK! Managed to set up SMBtcon!\n");
+   
+	p = SMB_VWV(server->packet);
+	p = smb_decode_word(p, &server->max_xmit);
+
+        if (server->max_xmit > given_max_xmit)
+                server->max_xmit = given_max_xmit;
+        
+	p = smb_decode_word(p, &server->tid);
+
+	/* Ok, everything is fine. max_xmit does not include */
+	/* the TCP-SMB header of 4 bytes. */
+	server->max_xmit += 4;
+
+	DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
+
+	/* Now make a new packet with the correct size. */
+	smb_kfree_s(server->packet, max_xmit); 
+
+	server->packet = smb_kmalloc(server->max_xmit, GFP_KERNEL);
+	if (server->packet == NULL) {
+		printk("smb_proc_connect: No memory left in end of "
+                       "connection phase :-(\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	}
+
+        DPRINTK("smb_proc_connect: Normal exit\n");
+        return 0;
+
+ fail:
+        server->state = CONN_INVALID;
+        return result;
+}
+
+/* smb_proc_reconnect: server->packet is allocated with
+   server->max_xmit bytes if and only if we return >= 0 */
+int
+smb_proc_connect(struct smb_server *server)
+{
+        int result;
+        smb_lock_server(server);
+        result = smb_proc_reconnect(server);
+        if ((result < 0) && (server->packet != NULL)) {
+                smb_kfree_s(server->packet, server->max_xmit);
+                server->packet = NULL;
+        }
+        smb_unlock_server(server);
+        return result;
+}
+	
+int
+smb_proc_disconnect(struct smb_server *server)
+{
+	smb_setup_header_exclusive(server, SMBtdis, 0, 0);
+	return smb_request_ok_unlock(server, SMBtdis, 0, 0);
+}
+
+/* error code stuff - put together by Merik Karman
+   merik@blackadder.dsh.oz.au */
+
+#if DEBUG_SMB > 0
+
+typedef struct {
+	char *name;
+	int code;
+	char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+err_code_struct dos_msgs[] = {
+  { "ERRbadfunc",1,"Invalid function."},
+  { "ERRbadfile",2,"File not found."},
+  { "ERRbadpath",3,"Directory invalid."},
+  { "ERRnofids",4,"No file descriptors available"},
+  { "ERRnoaccess",5,"Access denied."},
+  { "ERRbadfid",6,"Invalid file handle."},
+  { "ERRbadmcb",7,"Memory control blocks destroyed."},
+  { "ERRnomem",8,"Insufficient server memory to perform the requested function."},
+  { "ERRbadmem",9,"Invalid memory block address."},
+  { "ERRbadenv",10,"Invalid environment."},
+  { "ERRbadformat",11,"Invalid format."},
+  { "ERRbadaccess",12,"Invalid open mode."},
+  { "ERRbaddata",13,"Invalid data."},
+  { "ERR",14,"reserved."},
+  { "ERRbaddrive",15,"Invalid drive specified."},
+  { "ERRremcd",16,"A Delete Directory request attempted  to  remove  the  server's  current directory."},
+  { "ERRdiffdevice",17,"Not same device."},
+  { "ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."},
+  { "ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing  FIDs  on the file."},
+  { "ERRlock",33,"A Lock request conflicted with an existing lock or specified an  invalid mode,  or an Unlock requested attempted to remove a lock held by another process."},
+  { "ERRfilexists",80,"The file named in a Create Directory, Make  New  File  or  Link  request already exists."},
+  { "ERRbadpipe",230,"Pipe invalid."},
+  { "ERRpipebusy",231,"All instances of the requested pipe are busy."},
+  { "ERRpipeclosing",232,"Pipe close in progress."},
+  { "ERRnotconnected",233,"No process on other end of pipe."},
+  { "ERRmoredata",234,"There is more data to be returned."},
+  { NULL,-1,NULL}};
+
+/* Server Error Messages */
+err_code_struct server_msgs[] = { 
+  { "ERRerror",1,"Non-specific error code."},
+  { "ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+  { "ERRbadtype",3,"reserved."},
+  { "ERRaccess",4,"The requester does not have  the  necessary  access  rights  within  the specified  context for the requested function. The context is defined by the TID or the UID."},
+  { "ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+  { "ERRinvnetname",6,"Invalid network name in tree connect."},
+  { "ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or  non-printer request made to printer connection."},
+  { "ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+  { "ERRqtoobig",50,"Print queue full -- no space."},
+  { "ERRqeof",51,"EOF on print queue dump."},
+  { "ERRinvpfid",52,"Invalid print file FID."},
+  { "ERRsmbcmd",64,"The server did not recognize the command received."},
+  { "ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+  { "ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid  combination of values."},
+  { "ERRreserved",68,"reserved."},
+  { "ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination.  The server cannot set the requested attribute."},
+  { "ERRreserved",70,"reserved."},
+  { "ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+  { "ERRpaused",81,"Server is paused."},
+  { "ERRmsgoff",82,"Not receiving messages."},
+  { "ERRnoroom",83,"No room to buffer message."},
+  { "ERRrmuns",87,"Too many remote user names."},
+  { "ERRtimeout",88,"Operation timed out."},
+  { "ERRnoresource",89,"No resources currently available for request."},
+  { "ERRtoomanyuids",90,"Too many UIDs active on this session."},
+  { "ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+  { "ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+  { "ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+  { "ERRcontmpx",252,"Continue in MPX mode."},
+  { "ERRreserved",253,"reserved."},
+  { "ERRreserved",254,"reserved."},
+  { "ERRnosupport",0xFFFF,"Function not supported."},
+  { NULL,-1,NULL}};
+
+/* Hard Error Messages */
+err_code_struct hard_msgs[] = { 
+  { "ERRnowrite",19,"Attempt to write on write-protected diskette."},
+  { "ERRbadunit",20,"Unknown unit."},
+  { "ERRnotready",21,"Drive not ready."},
+  { "ERRbadcmd",22,"Unknown command."},
+  { "ERRdata",23,"Data error (CRC)."},
+  { "ERRbadreq",24,"Bad request structure length."},
+  { "ERRseek",25 ,"Seek error."},
+  { "ERRbadmedia",26,"Unknown media type."},
+  { "ERRbadsector",27,"Sector not found."},
+  { "ERRnopaper",28,"Printer out of paper."},
+  { "ERRwrite",29,"Write fault."},
+  { "ERRread",30,"Read fault."},
+  { "ERRgeneral",31,"General failure."},
+  { "ERRbadshare",32,"A open conflicts with an existing open."},
+  { "ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+  { "ERRwrongdisk",34,"The wrong disk was found in a drive."},
+  { "ERRFCBUnavail",35,"No FCBs are available to process request."},
+  { "ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+  { NULL,-1,NULL}
+};
+
+
+struct { 
+	int code;
+	char *class;
+	err_code_struct *err_msgs;
+} err_classes[] = {  
+  { 0,"SUCCESS",NULL},
+  { 0x01,"ERRDOS",dos_msgs},
+  { 0x02,"ERRSRV",server_msgs},
+  { 0x03,"ERRHRD",hard_msgs},
+  { 0x04,"ERRXOS",NULL},
+  { 0xE1,"ERRRMX1",NULL},
+  { 0xE2,"ERRRMX2",NULL},
+  { 0xE3,"ERRRMX3",NULL},
+  { 0xFF,"ERRCMD",NULL},
+  { -1,NULL,NULL}
+};
+
+void
+smb_printerr(int class, int num)
+{
+	int i,j;
+	err_code_struct *err;
+
+	for (i=0; err_classes[i].class; i++) {
+		if (err_classes[i].code != class)
+			continue;
+		if (!err_classes[i].err_msgs) {
+			printk("%s - %d", err_classes[i].class, num);
+			return;
+		}
+
+		err = err_classes[i].err_msgs;
+		for (j=0; err[j].name; j++) {
+			if (num != err[j].code)
+				continue;
+			printk("%s - %s (%s)",
+			       err_classes[i].class, err[j].name,
+                               err[j].message);
+			return;
+		}
+	}
+	
+	printk("Unknown error - (%d,%d)", class, num);
+	return;
+}
+
+#endif /* DEBUG_SMB > 0 */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v1.3.6/linux/fs/smbfs/sock.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/smbfs/sock.c	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,600 @@
+/*
+ *  sock.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <asm/segment.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include "/usr/src/linux/include/net/ip.h"
+
+#include <linux/smb.h>
+#include <linux/smbno.h>
+
+
+#define _S(nr) (1<<((nr)-1))
+
+static void
+smb_data_callback(struct sock *sk,int len)
+{
+        struct socket *sock = sk->socket;
+
+	if(!sk->dead)
+	{
+                unsigned char peek_buf[4];
+                int result;
+                unsigned short fs;
+
+                fs = get_fs();
+                set_fs(get_ds());
+
+		result = sock->ops->recvfrom(sock, (void *)peek_buf, 1, 1,
+                                             MSG_PEEK, NULL, NULL);
+
+                while ((result != -EAGAIN) && (peek_buf[0] == 0x85)) {
+
+                        /* got SESSION KEEP ALIVE */
+                        result = sock->ops->recvfrom(sock, (void *)peek_buf,
+                                                     4, 1, 0, NULL, NULL);
+
+                        DDPRINTK("smb_data_callback:"
+                                 " got SESSION KEEP ALIVE\n");
+
+                        if (result == -EAGAIN)
+                                break;
+
+                        result = sock->ops->recvfrom(sock, (void *)peek_buf,
+                                                     1, 1, MSG_PEEK,
+                                                     NULL, NULL);
+
+                }
+
+                set_fs(fs);
+
+                if (result != -EAGAIN) {
+                        wake_up_interruptible(sk->sleep);
+                }
+	}
+}
+
+int
+smb_catch_keepalive(struct smb_server *server)
+{
+        struct file   *file;
+        struct inode  *inode;
+        struct socket *sock;
+        struct sock   *sk;
+
+        if (   (server == NULL)
+            || ((file  = server->sock_file) == NULL)
+            || ((inode = file->f_inode) == NULL)
+            || (!S_ISSOCK(inode->i_mode))) {
+
+                printk("smb_catch_keepalive: did not get valid server!\n");
+                server->data_ready = NULL;
+                return -EINVAL;
+        }
+
+        sock = &(inode->u.socket_i);
+
+        if (sock->type != SOCK_STREAM) {
+                printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
+                server->data_ready = NULL;
+                return -EINVAL;
+        }
+
+        sk   = (struct sock *)(sock->data);
+
+        if (sk == NULL) {
+                printk("smb_catch_keepalive: sk == NULL");
+                server->data_ready = NULL;
+                return -EINVAL;
+        }
+
+        DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
+                 (unsigned int)(sk->data_ready),
+                 (unsigned int)(server->data_ready));
+
+        if (sk->data_ready == smb_data_callback) {
+                printk("smb_catch_keepalive: already done\n");
+                return -EINVAL;
+        }
+
+        server->data_ready = sk->data_ready;
+        sk->data_ready = smb_data_callback;
+        return 0;
+}
+                
+int
+smb_dont_catch_keepalive(struct smb_server *server)
+{
+        struct file   *file;
+        struct inode  *inode;
+        struct socket *sock;
+        struct sock   *sk;
+
+        if (   (server == NULL)
+            || ((file  = server->sock_file) == NULL)
+            || ((inode = file->f_inode) == NULL)
+            || (!S_ISSOCK(inode->i_mode))) {
+
+                printk("smb_dont_catch_keepalive: "
+                       "did not get valid server!\n");
+                return -EINVAL;
+        }
+
+        sock = &(inode->u.socket_i);
+
+        if (sock->type != SOCK_STREAM) {
+                printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
+                return -EINVAL;
+        }
+
+        sk   = (struct sock *)(sock->data);
+
+        if (sk == NULL) {
+                printk("smb_dont_catch_keepalive: sk == NULL");
+                return -EINVAL;
+        }
+
+        if (server->data_ready == NULL) {
+                printk("smb_dont_catch_keepalive: "
+                       "server->data_ready == NULL\n");
+                return -EINVAL;
+        }
+
+        if (sk->data_ready != smb_data_callback) {
+                printk("smb_dont_catch_keepalive: "
+                       "sk->data_callback != smb_data_callback\n");
+                return -EINVAL;
+        }
+
+        DDPRINTK("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
+                 (unsigned int)(sk->data_ready),
+                 (unsigned int)(server->data_ready));
+
+        sk->data_ready = server->data_ready;
+        server->data_ready = NULL;
+        return 0;
+}
+
+/*
+ * smb_receive
+ * fs points to the correct segment, server != NULL, sock!=NULL
+ */
+static int
+smb_receive(struct smb_server *server, struct socket *sock)
+{
+        int len, result;
+        unsigned char peek_buf[4];
+
+ re_recv:
+
+        result = sock->ops->recvfrom(sock, (void *)peek_buf, 4, 0,
+                                     MSG_PEEK, NULL, NULL);
+
+        if (result < 0) {
+                DPRINTK("smb_receive: recv error = %d\n", -result);
+                return result;
+        }
+
+        if (result == 0) {
+                DPRINTK("smb_receive: got 0 bytes\n");
+                return -EIO;
+        }
+
+        switch (peek_buf[0]) {
+
+        case 0x00:
+        case 0x82:
+                break;
+
+        case 0x85:
+                DPRINTK("smb_receive: Got SESSION KEEP ALIVE\n");
+                sock->ops->recvfrom(sock, (void *)peek_buf, 4, 1,
+                                    0, NULL, NULL);
+                goto re_recv;
+                
+        default:
+                printk("smb_receive: Invalid packet\n");
+                return -EIO;
+        }
+
+        /* Length not including first four bytes. */
+	len = smb_len(peek_buf) + 4; 
+        if (len > server->max_xmit) { 
+                printk("smb_receive: Received length (%d) > max_xmit (%d)!\n", 
+		       len, server->max_xmit);
+                return -EIO;
+	}
+        else
+        {
+                int already_read = 0;
+
+                while (already_read < len) {
+                
+                        result = sock->ops->
+                                recvfrom(sock,
+                                         (void *)(server->packet+already_read),
+                                         len - already_read, 0, 0,
+                                         NULL, NULL);
+   
+                        if (result < 0) {
+                                printk("SMB: notice message: error = %d\n",
+                                       -result);
+                                return result;
+                        }
+
+                        already_read += result;
+                }
+                result = already_read;
+        }
+
+        server->rcls = *((unsigned char *)(server->packet+9));
+        server->err  = *((unsigned short *)(server->packet+11));
+
+        if (server->rcls != 0) {
+                DPRINTK("smb_response: rcls=%d, err=%d\n",
+                        server->rcls, server->err);
+        }
+
+        return result;
+}
+
+
+/*
+ * smb_receive's preconditions also apply here.
+ */
+static int
+smb_receive_trans2(struct smb_server *server, struct socket *sock,
+                   int *data_len, int *param_len,
+                   char **data, char **param)
+{
+        int total_data=0;
+        int total_param=0;
+        int result;
+        unsigned char *inbuf = server->packet;
+
+        *data_len = *param_len = 0;
+
+        DDPRINTK("smb_receive_trans2: enter\n");
+        
+        if ((result = smb_receive(server, sock)) < 0) {
+                return result;
+        }
+
+        if (server->rcls != 0) {
+                return result;
+        }
+
+        /* parse out the lengths */
+        total_data = WVAL(inbuf,smb_tdrcnt);
+        total_param = WVAL(inbuf,smb_tprcnt);
+
+        if (   (total_data  > TRANS2_MAX_TRANSFER)
+            || (total_param > TRANS2_MAX_TRANSFER)) {
+                printk("smb_receive_trans2: data/param too long\n");
+                return -EIO;
+        }
+
+        /* allocate it */
+        if ((*data  = smb_kmalloc(total_data, GFP_KERNEL)) == NULL) {
+                printk("smb_receive_trans2: could not alloc data area\n");
+                return -ENOMEM;
+        }
+
+        if ((*param = smb_kmalloc(total_param, GFP_KERNEL)) == NULL) {
+                printk("smb_receive_trans2: could not alloc param area\n");
+                smb_kfree_s(*data, total_data);
+                return -ENOMEM;
+        }
+
+        DDPRINTK("smb_rec_trans2: total_data/param: %d/%d\n",
+                 total_data, total_param);
+
+        while (1)
+        {
+                if (WVAL(inbuf,smb_prdisp)+WVAL(inbuf, smb_prcnt)
+                    > total_param) {
+                        printk("smb_receive_trans2: invalid parameters\n");
+                        result = -EIO;
+                        goto fail;
+                }
+                memcpy(*param + WVAL(inbuf,smb_prdisp),
+                       smb_base(inbuf) + WVAL(inbuf,smb_proff),
+                       WVAL(inbuf,smb_prcnt));
+                *param_len += WVAL(inbuf,smb_prcnt);
+
+
+                if (WVAL(inbuf,smb_drdisp)+WVAL(inbuf, smb_drcnt)>total_data) {
+                        printk("smb_receive_trans2: invalid data block\n");
+                        result = -EIO;
+                        goto fail;
+                }
+                memcpy(*data + WVAL(inbuf,smb_drdisp),
+                       smb_base(inbuf) + WVAL(inbuf,smb_droff),
+                       WVAL(inbuf,smb_drcnt));
+                *data_len += WVAL(inbuf,smb_drcnt);
+
+                DDPRINTK("smb_rec_trans2: drcnt/prcnt: %d/%d\n",
+                         WVAL(inbuf, smb_drcnt), WVAL(inbuf, smb_prcnt));
+
+                /* parse out the total lengths again - they can shrink! */
+
+                if (   (WVAL(inbuf,smb_tdrcnt) > total_data)
+                    || (WVAL(inbuf,smb_tprcnt) > total_param)) {
+                        printk("smb_receive_trans2: data/params grew!\n");
+                        result = -EIO;
+                        goto fail;
+                }
+
+                total_data = WVAL(inbuf,smb_tdrcnt);
+                total_param = WVAL(inbuf,smb_tprcnt);
+
+                if (total_data <= *data_len && total_param <= *param_len)
+                        break;
+
+                if ((result = smb_receive(server, sock)) < 0) {
+                        goto fail;
+                }
+                if (server->rcls != 0) {
+                        result = -EIO;
+                        goto fail;
+                }
+        }
+
+        DDPRINTK("smb_receive_trans2: normal exit\n");
+
+        return 0;
+
+ fail:
+        DPRINTK("smb_receive_trans2: failed exit\n");
+
+        smb_kfree_s(*param, 0); *param = NULL;
+        smb_kfree_s(*data, 0);  *data = NULL;
+        return result;
+}
+
+static inline struct socket *
+server_sock(struct smb_server *server)
+{
+        struct file *file;
+        struct inode *inode;
+
+        if (server == NULL)
+                return NULL;
+        if ((file = server->sock_file) == NULL)
+                return NULL;
+        if ((inode = file->f_inode) == NULL)
+                return NULL;
+        return &(inode->u.socket_i);
+}
+
+int
+smb_release(struct smb_server *server)
+{
+        struct socket *sock = server_sock(server);
+        int result;
+
+        if (sock == NULL)
+                return -EINVAL;
+
+        result = sock->ops->release(sock, NULL);
+        DPRINTK("smb_release: sock->ops->release = %d\n", result);
+
+        /* inet_release does not set sock->state.  Maybe someone is
+           confused about sock->state being SS_CONNECTED while there
+           is nothing behind it, so I set it to SS_UNCONNECTED.*/
+        sock->state = SS_UNCONNECTED;
+
+        result = sock->ops->create(sock, 0);
+        DPRINTK("smb_release: sock->ops->create = %d\n", result);
+        return result;
+}
+
+int
+smb_connect(struct smb_server *server)
+{
+        struct socket *sock = server_sock(server);
+        if (sock == NULL)
+                return -EINVAL;
+        if (sock->state != SS_UNCONNECTED) {
+                DPRINTK("smb_connect: socket is not unconnected: %d\n",
+                        sock->state);
+        }
+        return sock->ops->connect(sock, (struct sockaddr *)&(server->m.addr),
+                                  sizeof(struct sockaddr_in), 0);
+}
+        
+/*****************************************************************************/
+/*                                                                           */
+/*  This routine was once taken from nfs, wich is for udp. Here TCP does     */
+/*  most of the ugly stuff for us (thanks, Alan!)                            */
+/*                                                                           */
+/*****************************************************************************/
+int
+smb_request(struct smb_server *server)
+{
+	unsigned long old_mask;
+	unsigned short fs;      /* We fool the kernel to believe
+                                   we call from user space. */
+	int len, result, result2;
+
+	struct socket *sock = server_sock(server);
+	unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+
+	if ((sock == NULL) || (buffer == NULL)) {
+		printk("smb_request: Bad server!\n");
+		return -EBADF;
+	}
+
+        if (server->state != CONN_VALID)
+                return -EIO;
+        	
+#if 0
+	while (server->lock)
+		sleep_on(&server->wait);
+	server->lock = 1;
+#endif
+
+        if ((result = smb_dont_catch_keepalive(server)) != 0) {
+                server->state = CONN_INVALID;
+                smb_invalidate_all_inodes(server);
+                return result;
+        }
+
+        len = smb_len(buffer) + 4;
+
+        DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
+
+	old_mask = current->blocked;
+	current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+	fs = get_fs();
+	set_fs(get_ds());
+
+        result = sock->ops->send(sock, (void *)buffer, len, 0, 0);
+        if (result < 0) {
+                printk("smb_request: send error = %d\n", result);
+        }
+        else {
+                result = smb_receive(server, sock);
+        }
+
+        /* read/write errors are handled by errno */
+        current->signal &= ~_S(SIGPIPE);
+
+	current->blocked = old_mask;
+	set_fs(fs);
+
+        if ((result2 = smb_catch_keepalive(server)) < 0) {
+                result = result2;
+        }
+
+#if 0
+	server->lock = 0;
+	wake_up(&server->wait);
+#endif
+
+        if (result < 0) {
+                server->state = CONN_INVALID;
+                smb_invalidate_all_inodes(server);
+        }
+        
+        DDPRINTK("smb_request: result = %d\n", result);
+
+	return result;
+}
+
+/*
+ * This is not really a trans2 request, we assume that you only have
+ * one packet to send.
+ */ 
+int
+smb_trans2_request(struct smb_server *server,
+                   int *data_len, int *param_len,
+                   char **data, char **param)
+{
+	unsigned long old_mask;
+	unsigned short fs;      /* We fool the kernel to believe
+                                   we call from user space. */
+	int len, result, result2;
+
+	struct socket *sock = server_sock(server);
+	unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+
+	if ((sock == NULL) || (buffer == NULL)) {
+		printk("smb_trans2_request: Bad server!\n");
+		return -EBADF;
+	}
+
+        if (server->state != CONN_VALID)
+                return -EIO;
+        	
+#if 0
+	while (server->lock)
+		sleep_on(&server->wait);
+	server->lock = 1;
+#endif
+
+        if ((result = smb_dont_catch_keepalive(server)) != 0) {
+                server->state = CONN_INVALID;
+                smb_invalidate_all_inodes(server);
+                return result;
+        }
+
+        len = smb_len(buffer) + 4;
+
+        DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
+
+	old_mask = current->blocked;
+	current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+	fs = get_fs();
+	set_fs(get_ds());
+
+        result = sock->ops->send(sock, (void *)buffer, len, 0, 0);
+        if (result < 0) {
+                printk("smb_trans2_request: send error = %d\n", result);
+        }
+        else {
+                result = smb_receive_trans2(server, sock,
+                                            data_len, param_len,
+                                            data, param);
+        }
+
+        /* read/write errors are handled by errno */
+        current->signal &= ~_S(SIGPIPE);
+
+	current->blocked = old_mask;
+	set_fs(fs);
+
+        if ((result2 = smb_catch_keepalive(server)) < 0) {
+                result = result2;
+        }
+
+#if 0
+	server->lock = 0;
+	wake_up(&server->wait);
+#endif
+
+        if (result < 0) {
+                server->state = CONN_INVALID;
+                smb_invalidate_all_inodes(server);
+        }
+        
+        DDPRINTK("smb_trans2_request: result = %d\n", result);
+
+	return result;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v1.3.6/linux/fs/umsdos/dir.c	Tue Jun 27 14:11:44 1995
+++ linux/fs/umsdos/dir.c	Tue Jul  4 07:48:57 1995
@@ -50,7 +50,7 @@
 */
 static int umsdos_dir_once(
 	void * buf,
-	char * name,
+	const char * name,
 	int name_len,
 	off_t offset,
 	ino_t ino)
@@ -58,10 +58,12 @@
 	int ret = -EINVAL;
 	struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
 	if (d->count == 0){
-		char zname[100];
-		memcpy (zname,name,name_len);
-		zname[name_len] = '\0';
-		PRINTK (("dir_once :%s: offset %ld\n",zname,offset));
+		#if 0
+			char zname[100];
+			memcpy (zname,name,name_len);
+			zname[name_len] = '\0';
+			Printk (("dir_once :%s: offset %Ld\n",zname,offset));
+		#endif
 		ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
 		d->stop = ret < 0;
 		d->count = 1;
@@ -160,6 +162,7 @@
 	}else{
 		struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
 		if (emd_dir != NULL){
+			off_t start_fpos = filp->f_pos;
 			if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
 			PRINTK (("f_pos %lu i_size %ld\n",filp->f_pos,emd_dir->i_size));
 			ret = 0;
@@ -246,12 +249,12 @@
 				(see comments at the beginning), we put back
 				the special offset.
 			*/
-			if (filp->f_pos == 0) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+			if (filp->f_pos == 0) filp->f_pos = start_fpos;
 			iput(emd_dir);
 		}
 	}
 	umsdos_endlookup(dir);	
-	PRINTK (("read dir %p pos %lu ret %d\n",dir,filp->f_pos,ret));
+	PRINTK (("read dir %p pos %Ld ret %d\n",dir,filp->f_pos,ret));
 	return ret;
 }
 /*
@@ -279,7 +282,7 @@
 		if (bufk.count == 0) break;
 		count += bufk.count;
 	}
-	PRINTK (("UMSDOS_readdir out %d count %d pos %lu\n",ret,count
+	PRINTK (("UMSDOS_readdir out %d count %d pos %Ld\n",ret,count
 		,filp->f_pos));
 	return count == 0 ? -ENOENT : ret;
 }
@@ -349,6 +352,42 @@
 			inode->i_mtime = entry->mtime;
 			inode->i_uid   = entry->uid;
 			inode->i_gid   = entry->gid;
+			/* #Specification: umsdos / conversion mode
+				The msdos fs can do some inline conversion
+				of the data of a file. It can translate
+				silently from MsDOS text file format to Unix
+				one (crlf -> lf) while reading, and the reverse
+				while writting. This is activated using the mount
+				option conv=....
+
+				This is not useful for Linux file in promoted
+				directory. It can even be harmful. For this
+				reason, the binary (no conversion) mode is
+				always activated.
+			*/
+			/* #Specification: umsdos / conversion mode / todo
+				A flag could be added to file and directories
+				forcing an automatic conversion mode (as
+				done with the msdos fs).
+				
+				This flag could be setup on a directory basis
+				(instead of file) and all file in it would
+				logically inherited. If the conversion mode
+				is active (conv=) then the i_binary flag would
+				be left untouched in those directories.
+				
+				It was proposed that the sticky bit was used
+				to set this. The problem is that new file would
+				be written incorrectly. The other problem is that
+				the sticky bit has a meaning for directories. So
+				another bit should be used (there is some space
+				in the EMD file for it) and a special utilities
+				would be used to assign the flag to a directory).
+				I don't think it is useful to assign this flag
+				on a single file.
+			*/
+
+			MSDOS_I(inode)->i_binary = 1;
 			/* #Specification: umsdos / i_nlink
 				The nlink field of an inode is maintain by the MSDOS file system
 				for directory and by UMSDOS for other file. The logic is that
@@ -380,7 +419,7 @@
 */
 static int umsdos_filldir_k(
 	void * buf,
-	char * name,
+	const char * name,
 	int name_len,
 	off_t offset,
 	ino_t ino)
@@ -399,7 +438,7 @@
 
 static int umsdos_dir_search (
 	void * buf,
-	char * name,
+	const char * name,
 	int name_len,
 	off_t offset,
 	ino_t ino)
diff -u --recursive --new-file v1.3.6/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v1.3.6/linux/fs/umsdos/emd.c	Tue Jun 27 14:11:44 1995
+++ linux/fs/umsdos/emd.c	Tue Jul  4 07:48:57 1995
@@ -89,7 +89,7 @@
 	filp->f_flags = 0;
 	sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
 	if (sizeread != count){
-		printk ("UMSDOS: problem with EMD file. Can't read pos = %ld (%d != %d)\n"
+		printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %d)\n"
 			,filp->f_pos,sizeread,count);
 		ret = -EIO;
 	}
diff -u --recursive --new-file v1.3.6/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v1.3.6/linux/fs/umsdos/ioctl.c	Tue Jun 27 14:11:44 1995
+++ linux/fs/umsdos/ioctl.c	Tue Jul  4 07:48:57 1995
@@ -11,6 +11,7 @@
 
 #include <asm/segment.h>
 #include <linux/errno.h>
+#include <linux/mm.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
@@ -31,7 +32,7 @@
 */
 static int umsdos_ioctl_fill(
 	void * buf,
-	char * name,
+	const char * name,
 	int name_len,
 	off_t offset,
 	ino_t ino)
@@ -65,7 +66,13 @@
 		Only root (effective id) is allowed to do IOCTL on directory
 		in UMSDOS. EPERM is returned for other user.
 	*/
-	if (current->euid == 0
+	/*
+		Well, not all case require write access, but it simplify the code
+		and let's face it, there is only one client (umssync) for all this
+	*/
+	if (verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl)) < 0){
+		ret = -EFAULT;
+	}else if (current->euid == 0
 		|| cmd == UMSDOS_GETVERSION){
 		struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
 		ret = -EINVAL;
diff -u --recursive --new-file v1.3.6/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v1.3.6/linux/fs/umsdos/rdir.c	Tue Jun 27 14:11:44 1995
+++ linux/fs/umsdos/rdir.c	Tue Jul  4 07:48:57 1995
@@ -36,7 +36,7 @@
 
 static int rdir_filldir(
 	void * buf,
-	char * name,
+	const char * name,
 	int name_len,
 	off_t offset,
 	ino_t ino)
diff -u --recursive --new-file v1.3.6/linux/include/asm-alpha/checksum.h linux/include/asm-alpha/checksum.h
--- v1.3.6/linux/include/asm-alpha/checksum.h	Fri Jun 16 22:02:55 1995
+++ linux/include/asm-alpha/checksum.h	Thu Jul  6 13:22:05 1995
@@ -6,7 +6,7 @@
  *	This is a version of ip_compute_csum() optimized for IP headers,
  *	which always checksum on 4 octet boundaries.
  */
-extern inline unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl);
+extern unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl);
 
 /*
  * computes the checksum of the TCP/UDP pseudo-header
@@ -33,14 +33,19 @@
 extern unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum);
 
 /*
- * the same as csum_partial, but copies from fs:src while it
+ * the same as csum_partial, but copies from src while it
  * checksums
  *
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
+unsigned int csum_partial_copy( char *src, char *dst, int len, int sum);
 
-extern unsigned int csum_partial_copyffs( char *src, char *dst, int len, int sum);
+/*
+ * the same as csum_partial, but copies from user space (but on the alpha
+ * we have just one address space, so this is identical to the above)
+ */
+#define csum_partial_copy_fromuser csum_partial_copy
 
 
 /*
diff -u --recursive --new-file v1.3.6/linux/include/asm-alpha/elf.h linux/include/asm-alpha/elf.h
--- v1.3.6/linux/include/asm-alpha/elf.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-alpha/elf.h	Wed Jul  5 12:53:22 1995
@@ -0,0 +1,17 @@
+#ifndef __ASMaxp_ELF_H
+#define __ASMaxp_ELF_H
+
+/*
+ * ELF register definitions..
+ */
+
+#define ELF_NGREG	32
+#define ELF_NFPREG	32
+
+typedef unsigned long elf_greg_t;
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+typedef double elf_fpreg_t;
+typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
+
+#endif
diff -u --recursive --new-file v1.3.6/linux/include/asm-alpha/io.h linux/include/asm-alpha/io.h
--- v1.3.6/linux/include/asm-alpha/io.h	Thu Jun 29 19:02:54 1995
+++ linux/include/asm-alpha/io.h	Thu Jul  6 13:22:05 1995
@@ -107,4 +107,20 @@
 extern void outsw (unsigned long port, void *dst, unsigned long count);
 extern void outsl (unsigned long port, void *dst, unsigned long count);
 
+/*
+ * The "address" in IO memory space is not clearly either a integer or a
+ * pointer. We will accept both, thus the casts.
+ */
+#define readb(addr) ((unsigned char) (readb)((unsigned long)(addr)))
+#define readw(addr) ((unsigned short) (readw)((unsigned long)(addr)))
+#define readl(addr) ((unsigned int) (readl)((unsigned long)(addr)))
+
+#define writeb(b,addr) (writeb)((b),(unsigned long)(addr))
+#define writew(w,addr) (writew)((w),(unsigned long)(addr))
+#define writel(l,addr) (writel)((l),(unsigned long)(addr))
+
+#define memset_io(addr,c,len)		(memset_io)((unsigned long)(addr),(c),(len))
+#define memcpy_fromio(to,from,len)	(memcpy_fromio)((to),(unsigned long)(from),(len))
+#define memcpy_toio(to,from,len)	(memcpy_toio)((unsigned long)(to),(from),(len))
+
 #endif
diff -u --recursive --new-file v1.3.6/linux/include/asm-alpha/jensen.h linux/include/asm-alpha/jensen.h
--- v1.3.6/linux/include/asm-alpha/jensen.h	Fri Jun  2 13:51:16 1995
+++ linux/include/asm-alpha/jensen.h	Thu Jul  6 13:22:05 1995
@@ -263,7 +263,7 @@
 extern unsigned long readw(unsigned long addr);
 extern unsigned long readl(unsigned long addr);
 
-extern void writeb(unsigned short b, unsigned long addr);
+extern void writeb(unsigned char b, unsigned long addr);
 extern void writew(unsigned short b, unsigned long addr);
 extern void writel(unsigned int b, unsigned long addr);
 
diff -u --recursive --new-file v1.3.6/linux/include/asm-alpha/lca.h linux/include/asm-alpha/lca.h
--- v1.3.6/linux/include/asm-alpha/lca.h	Thu Jun 29 19:02:54 1995
+++ linux/include/asm-alpha/lca.h	Thu Jul  6 13:22:05 1995
@@ -292,7 +292,7 @@
 extern unsigned long readb(unsigned long addr);
 extern unsigned long readw(unsigned long addr);
 
-extern void writeb(unsigned short b, unsigned long addr);
+extern void writeb(unsigned char b, unsigned long addr);
 extern void writew(unsigned short b, unsigned long addr);
 
 #define inb(port) \
diff -u --recursive --new-file v1.3.6/linux/include/asm-alpha/string.h linux/include/asm-alpha/string.h
--- v1.3.6/linux/include/asm-alpha/string.h	Fri Jun  2 13:51:17 1995
+++ linux/include/asm-alpha/string.h	Wed Jul  5 12:53:22 1995
@@ -7,7 +7,7 @@
 
 #define memset(s, c, count) \
 (__builtin_constant_p(c) ? \
- __constant_c_memset((s),(0x01010101UL*(unsigned char)c),(count)) : \
+ __constant_c_memset((s),(0x0101010101010101UL*(unsigned char)c),(count)) : \
  __memset((s),(c),(count)))
 
 #define memcpy(d,s,count) __memcpy((d),(s),(count))
diff -u --recursive --new-file v1.3.6/linux/include/asm-i386/checksum.h linux/include/asm-i386/checksum.h
--- v1.3.6/linux/include/asm-i386/checksum.h	Fri Jun 30 16:22:30 1995
+++ linux/include/asm-i386/checksum.h	Thu Jul  6 13:22:05 1995
@@ -16,15 +16,24 @@
 unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum);
 
 /*
- * the same as csum_partial, but copies from fs:src while it
+ * the same as csum_partial, but copies from src while it
  * checksums
  *
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
 
-unsigned int csum_partial_copyffs( char *src, char *dst, int len, int sum);
+unsigned int csum_partial_copy( char *src, char *dst, int len, int sum);
 
+
+/*
+ * the same as csum_partial_copy, but copies from user space.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+unsigned int csum_partial_copy_fromuser( char *src, char *dst, int len, int sum);
 
 /*
  *	This is a version of ip_compute_csum() optimized for IP headers,
diff -u --recursive --new-file v1.3.6/linux/include/asm-i386/elf.h linux/include/asm-i386/elf.h
--- v1.3.6/linux/include/asm-i386/elf.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-i386/elf.h	Wed Jul  5 12:53:22 1995
@@ -0,0 +1,17 @@
+#ifndef __ASMi386_ELF_H
+#define __ASMi386_ELF_H
+
+/*
+ * ELF register definitions..
+ */
+
+#include <asm/ptrace.h>
+
+typedef unsigned long elf_greg_t;
+
+#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+typedef struct user_i387_struct elf_fpregset_t;
+
+#endif
diff -u --recursive --new-file v1.3.6/linux/include/asm-i386/segment.h linux/include/asm-i386/segment.h
--- v1.3.6/linux/include/asm-i386/segment.h	Tue Jun 27 14:11:44 1995
+++ linux/include/asm-i386/segment.h	Sun Jul  2 21:26:28 1995
@@ -30,6 +30,7 @@
  */
 struct __segment_dummy { unsigned long a[100]; };
 #define __sd(x) ((struct __segment_dummy *) (x))
+#define __const_sd(x) ((const struct __segment_dummy *) (x))
 
 static inline void __put_user(unsigned long x, void * y, int size)
 {
@@ -62,17 +63,17 @@
 		case 1:
 			__asm__ ("movb %%fs:%1,%b0"
 				:"=q" (result)
-				:"m" (*__sd(y)));
+				:"m" (*__const_sd(y)));
 			return (unsigned char) result;
 		case 2:
 			__asm__ ("movw %%fs:%1,%w0"
 				:"=r" (result)
-				:"m" (*__sd(y)));
+				:"m" (*__const_sd(y)));
 			return (unsigned short) result;
 		case 4:
 			__asm__ ("movl %%fs:%1,%0"
 				:"=r" (result)
-				:"m" (*__sd(y)));
+				:"m" (*__const_sd(y)));
 			return result;
 		default:
 			return bad_user_access_length();
diff -u --recursive --new-file v1.3.6/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v1.3.6/linux/include/asm-i386/string.h	Tue Jun 27 14:11:44 1995
+++ linux/include/asm-i386/string.h	Sun Jul  2 21:26:28 1995
@@ -365,17 +365,17 @@
 		case 0:
 			return to;
 		case 1:
-			*(unsigned char *)to = *(unsigned char *)from;
+			*(unsigned char *)to = *(const unsigned char *)from;
 			return to;
 		case 2:
-			*(unsigned short *)to = *(unsigned short *)from;
+			*(unsigned short *)to = *(const unsigned short *)from;
 			return to;
 		case 3:
-			*(unsigned short *)to = *(unsigned short *)from;
-			*(2+(unsigned char *)to) = *(2+(unsigned char *)from);
+			*(unsigned short *)to = *(const unsigned short *)from;
+			*(2+(unsigned char *)to) = *(2+(const unsigned char *)from);
 			return to;
 		case 4:
-			*(unsigned long *)to = *(unsigned long *)from;
+			*(unsigned long *)to = *(const unsigned long *)from;
 			return to;
 	}
 #define COMMON(x) \
diff -u --recursive --new-file v1.3.6/linux/include/linux/atalk.h linux/include/linux/atalk.h
--- v1.3.6/linux/include/linux/atalk.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/atalk.h	Wed Jul  5 13:06:27 1995
@@ -0,0 +1,144 @@
+/*
+ *	Appletalk networking structures
+ *
+ *	The following are directly referenced from the University Of Michigan
+ *	netatalk for compatibility reasons.
+ */
+
+#ifndef __LINUX_ATALK_H__
+#define __LINUX_ATALK_H__
+
+#define ATPORT_FIRST	1
+#define ATPORT_RESERVED	128
+#define ATPORT_LAST	255
+#define ATADDR_ANYNET	(__u16)0
+#define ATADDR_ANYNODE	(__u8)0
+#define ATADDR_ANYPORT  (__u8)0
+#define ATADDR_BCAST	(__u8)255
+#define DDP_MAXSZ	587
+
+struct at_addr 
+{
+	__u16	s_net;
+	__u8	s_node;
+};
+
+struct sockaddr_at 
+{
+	short		sat_family;
+	__u8		sat_port;
+	struct at_addr	sat_addr;
+	char		sat_zero[ 8 ];
+};
+
+struct netrange 
+{
+	__u8	nr_phase;
+	__u16	nr_firstnet;
+	__u16	nr_lastnet;
+};
+
+struct atalk_route
+{
+	struct device *dev;
+	struct at_addr target;
+	struct at_addr gateway;
+	int flags;
+	struct atalk_route *next;
+};
+
+struct atalk_iface
+{
+	struct device *dev;
+	struct at_addr address;		/* Our address */
+	int status;			/* What are we doing ?? */
+#define ATIF_PROBE	1		/* Probing for an address */
+#define ATIF_PROBE_FAIL	2		/* Probe collided */
+	struct netrange nets;		/* Associated direct netrange */
+	struct atalk_iface *next;
+};
+	
+struct atalk_sock
+{
+	unsigned short dest_net;
+	unsigned short src_net;
+	unsigned char dest_node;
+	unsigned char src_node;
+	unsigned char dest_port;
+	unsigned char src_port;
+};
+
+#define DDP_MAXHOPS	15	/* 4 bits of hop counter */
+
+#ifdef __KERNEL__
+
+struct ddpehdr
+{
+	/* FIXME for bigendians */
+	/*__u16	deh_pad:2,deh_hops:4,deh_len:10;*/
+	__u16	deh_len:10,deh_hops:4,deh_pad:2;
+	__u16	deh_sum;
+	__u16	deh_dnet;
+	__u16	deh_snet;
+	__u8	deh_dnode;
+	__u8	deh_snode;
+	__u8	deh_dport;
+	__u8	deh_sport;
+	/* And netatalk apps expect to stick the type in themselves */
+};
+
+/*
+ *	Unused (and currently unsupported)
+ */
+ 
+struct ddpshdr
+{
+	/* FIXME for bigendians */
+	__u8	dsh_sport;
+	__u8	dsh_dport;
+	__u16	dsh_len:10, dsh_pad:6;
+	/* And netatalk apps expect to stick the type in themselves */
+};
+
+/* Appletalk AARP headers */
+
+struct elapaarp
+{
+	__u16	hw_type;
+#define AARP_HW_TYPE_ETHERNET		1
+#define AARP_HW_TYPE_TOKENRING		2
+	__u16	pa_type;
+	__u8	hw_len;
+	__u8	pa_len;
+#define AARP_PA_ALEN			4
+	__u16	function;
+#define AARP_REQUEST			1
+#define AARP_REPLY			2
+#define AARP_PROBE			3
+	__u8	hw_src[ETH_ALEN]	__attribute__ ((packed));
+	__u8	pa_src_zero		__attribute__ ((packed));
+	__u16	pa_src_net		__attribute__ ((packed));
+	__u8	pa_src_node		__attribute__ ((packed));
+	__u8	hw_dst[ETH_ALEN]	__attribute__ ((packed));
+	__u8	pa_dst_zero		__attribute__ ((packed));
+	__u16	pa_dst_net		__attribute__ ((packed));
+	__u8	pa_dst_node		__attribute__ ((packed));	
+};
+
+typedef struct sock	atalk_socket;
+
+#define AARP_EXPIRY_TIME	(5*60*HZ)	/* Not specified - how long till we drop a resolved entry */
+#define AARP_HASH_SIZE		16		/* Size of hash table */
+#define AARP_TICK_TIME		(HZ/5)		/* Fast retransmission timer when resolving */
+#define AARP_RETRANSMIT_LIMIT	10		/* Send 10 requests then give up (2 seconds) */
+#define AARP_RESOLVE_TIME	(10*HZ)		/* Some value bigger than total retransmit time + a bit for last reply to appear and to stop continual requests */
+
+extern struct datalink_proto *ddp_dl, *aarp_dl;
+extern void aarp_proto_init(void);
+/* Inter module exports */
+extern struct atalk_iface *atalk_find_dev(struct device *dev);
+extern struct at_addr *atalk_find_dev_addr(struct device *dev);
+extern int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, void *hwaddr);
+extern void aarp_send_probe(struct device *dev, struct at_addr *addr);
+#endif
+#endif
diff -u --recursive --new-file v1.3.6/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.3.6/linux/include/linux/cdrom.h	Wed Jan 18 07:51:45 1995
+++ linux/include/linux/cdrom.h	Sat Jul  1 19:05:59 1995
@@ -414,4 +414,14 @@
  */
 #define	CDROMMULTISESSION	0x5310 /* (struct cdrom_multisession) */
 
+/*
+ * obtain the "universal product code" number
+ * (only some data disks have it coded)
+ */
+#define	CDROM_GET_UPC		0x5311 /* 8 bytes returned */
+
+#define	CDROMRESET		0x5312 /* hard-reset the drive */
+#define	CDROMVOLREAD		0x5313 /* let the drive tell its volume setting */
+					/* (struct cdrom_volctrl) */
+
 #endif  _LINUX_CDROM_H
diff -u --recursive --new-file v1.3.6/linux/include/linux/cm206.h linux/include/linux/cm206.h
--- v1.3.6/linux/include/linux/cm206.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/cm206.h	Sat Jul  1 19:05:59 1995
@@ -0,0 +1,126 @@
+/* cm206.h Header file for cm206.c.
+   Copyright (c) 1995 David van Leeuwen 
+*/
+
+/* First, the cm260 stuff */
+/* The ports and irq used. If it is not defined, make it variable, 
+   and initialize them at some reasonable value */
+
+#define CM206_BASE 0x340
+#define CM206_IRQ 11
+
+#define r_data_status (CM206_BASE)
+#define r_uart_receive (CM206_BASE+0x2)
+#define r_fifo_output_buffer (CM206_BASE+0x4)
+#define r_line_status (CM206_BASE+0x6)
+#define r_data_control (CM206_BASE+0x8)
+#define r_uart_transmit (CM206_BASE+0xa)
+#define r_test_clock (CM206_BASE+0xc)
+#define r_test_control (CM206_BASE+0xe)
+
+/* the data_status flags */
+#define ds_ram_size 0x4000
+#define ds_toc_ready 0x2000
+#define ds_fifo_empty 0x1000
+#define ds_sync_error 0x800
+#define ds_crc_error 0x400
+#define ds_data_error 0x200
+#define ds_fifo_overflow 0x100
+#define ds_data_ready 0x80
+
+/* the line_status flags */
+#define ls_attention 0x10
+#define ls_parity_error 0x8
+#define ls_overrun 0x4
+#define ls_receive_buffer_full 0x2
+#define ls_transmitter_buffer_empty 0x1
+
+/* the data control register flags */
+#define dc_read_q_channel 0x4000
+#define dc_mask_sync_error 0x2000
+#define dc_toc_enable 0x1000
+#define dc_no_stop_on_error 0x800
+#define dc_break 0x400
+#define dc_initialize 0x200
+#define dc_mask_transmit_ready 0x100
+#define dc_flag_enable 0x80
+
+#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \
+		   dc_mask_transmit_ready)
+
+/* now some constants related to the cm206 */
+/* another drive status byte, echoed by the cm206 on most commmands */
+
+#define dsb_error_condition 0x1
+#define dsb_play_in_progress 0x4
+#define dsb_possible_media_change 0x8
+#define dsb_disc_present 0x10
+#define dsb_drive_not_ready 0x20
+#define dsb_tray_locked 0x40
+#define dsb_tray_not_closed 0x80
+
+#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed)
+
+/* the cm206 command set */
+
+#define c_close_tray 0
+#define c_lock_tray 0x01
+#define c_unlock_tray 0x04
+#define c_open_tray 0x05
+#define c_seek 0x10
+#define c_read_data 0x20
+#define c_force_1x 0x21
+#define c_force_2x 0x22
+#define c_auto_mode 0x23
+#define c_play 0x30
+#define c_set_audio_mode 0x31
+#define c_read_current_q 0x41
+#define c_stream_q 0x42
+#define c_drive_status 0x50
+#define c_disc_status 0x51
+#define c_audio_status 0x52
+#define c_drive_configuration 0x53
+#define c_read_upc 0x60
+#define c_stop 0x70
+#define c_calc_checksum 0xe5
+
+#define c_gimme 0xf8
+
+/* finally, the (error) condition that the drive can be in      *
+ * OK, this is not always an error, but let's prefix it with e_ */
+
+#define e_none 0
+#define e_illegal_command 0x01
+#define e_sync 0x02
+#define e_seek 0x03
+#define e_parity 0x04
+#define e_focus 0x05
+#define e_header_sync 0x06
+#define e_code_incompatibility 0x07
+#define e_reset_done 0x08
+#define e_bad_parameter 0x09
+#define e_radial 0x0a
+#define e_sub_code 0x0b
+#define e_no_data_track 0x0c
+#define e_scan 0x0d
+#define e_tray_open 0x0f
+#define e_no_disc 0x10
+#define e_tray stalled 0x11
+
+/* drive configuration masks */
+
+#define dcf_revision_code 0x7
+#define dcf_transfer_rate 0x60
+#define dcf_motorized_tray 0x80
+
+/* disc status byte */
+
+#define cds_multi_session 0x2
+#define cds_all_audio 0x8
+#define cds_xa_mode 0xf0
+
+/* finally some ioctls for the driver */
+
+#define CM206CTL_GET_STAT 0x2000
+#define CM206CTL_GET_LAST_STAT 0x2001
+#define CM206_RESET_DRIVE 0x2002 /* use with care */
diff -u --recursive --new-file v1.3.6/linux/include/linux/elfcore.h linux/include/linux/elfcore.h
--- v1.3.6/linux/include/linux/elfcore.h	Thu Jun 29 19:02:55 1995
+++ linux/include/linux/elfcore.h	Wed Jul  5 12:53:22 1995
@@ -14,16 +14,7 @@
 	int	si_errno;			/* errno */
 };
 
-typedef unsigned long elf_greg_t;
-#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
-typedef elf_greg_t elf_gregset_t[ELF_NGREG];
-typedef struct
-#ifdef __i386__
-	user_i387_struct
-#else
-#error	"The FPU in this arch is not supported by ELF core dump.".
-#endif
-elf_fpregset_t;
+#include <asm/elf.h>
 
 #ifndef __KERNEL__
 typedef elf_greg_t greg_t;
diff -u --recursive --new-file v1.3.6/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v1.3.6/linux/include/linux/genhd.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/genhd.h	Wed Jul  5 12:53:22 1995
@@ -8,6 +8,12 @@
  *
  *		<drew@colorado.edu>
  */
+
+#define CONFIG_MSDOS_PARTITION 1
+
+#ifdef __alpha__
+#define CONFIG_OSF_PARTITION 1
+#endif
 	
 #define EXTENDED_PARTITION 5
 	
diff -u --recursive --new-file v1.3.6/linux/include/linux/gscd.h linux/include/linux/gscd.h
--- v1.3.6/linux/include/linux/gscd.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/gscd.h	Sat Jul  1 19:06:00 1995
@@ -0,0 +1,110 @@
+/*
+ * Definitions for a GoldStar R420 CD-ROM interface
+ *
+ *   Copyright (C) 1995  Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
+ *                       Eberhard Moenkeberg <emoenke@gwdg.de>
+ *
+ *  Published under the GPL.
+ *
+ */
+
+
+/* The Interface Card default address is 0x340. This will work fo most
+   applications. Address selection is accomplished by jumpers PN801-1 to
+   PN801-4 on the GoldStar Interface Card.
+   Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360
+   0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0             */
+
+/* insert here the I/O port address */
+#define GSCD_BASE_ADDR	        0x340
+
+/* change this to set the dma-channel */
+#define GSCD_DMA_CHANNEL        3                  /* not used */ 
+
+/************** nothing to set up below here *********************/
+
+/* port access macro */
+#define GSCDPORT(x)		(gscd_port + (x))
+
+/*
+ * commands
+ * the lower nibble holds the command length
+ */
+#define CMD_STATUS     0x01
+#define CMD_READSUBQ   0x02 /* 1: ?, 2: UPC, 5: ? */
+#define CMD_SEEK       0x05 /* read_mode M-S-F */
+#define CMD_READ       0x07 /* read_mode M-S-F nsec_h nsec_l */
+#define CMD_RESET      0x11
+#define CMD_SETMODE    0x15
+#define CMD_PLAY       0x17 /* M-S-F M-S-F */
+#define CMD_LOCK_CTL   0x22 /* 0: unlock, 1: lock */
+#define CMD_IDENT      0x31
+#define CMD_SETSPEED   0x32 /* 0: auto */ /* ??? */
+#define CMD_GETMODE    0x41
+#define CMD_PAUSE      0x51
+#define CMD_READTOC    0x61
+#define CMD_DISKINFO   0x71
+#define CMD_TRAY_CTL   0x81
+
+/*
+ * disk_state:
+ */
+#define ST_PLAYING	0x80
+#define ST_UNLOCKED	0x40
+#define ST_NO_DISK	0x20
+#define ST_DOOR_OPEN	0x10
+#define ST_x08  0x08
+#define ST_x04	0x04
+#define ST_INVALID	0x02
+#define ST_x01	0x01
+
+/*
+ * cmd_type:
+ */
+#define TYPE_INFO	0x01
+#define TYPE_DATA	0x02
+
+/*
+ * read_mode:
+ */
+#define MOD_POLLED	0x80
+#define MOD_x08	0x08
+#define MOD_RAW	0x04
+
+#define READ_DATA(port, buf, nr) insb(port, buf, nr)
+
+#define SET_TIMER(func, jifs) \
+	((timer_table[GSCD_TIMER].expires = jiffies + jifs), \
+	(timer_table[GSCD_TIMER].fn = func), \
+	(timer_active |= 1<<GSCD_TIMER))
+
+#define CLEAR_TIMER		timer_active &= ~(1<<GSCD_TIMER)
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct gscd_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct gscd_DiskInfo {
+	unsigned char	first;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+};
+
+struct gscd_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
+
diff -u --recursive --new-file v1.3.6/linux/include/linux/if_ether.h linux/include/linux/if_ether.h
--- v1.3.6/linux/include/linux/if_ether.h	Tue Jun  6 11:22:11 1995
+++ linux/include/linux/if_ether.h	Wed Jul  5 13:06:27 1995
@@ -9,6 +9,8 @@
  *
  * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Donald Becker, <becker@super.org>
+ *		Alan Cox, <alan@cymru.net>
+ *		Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -33,9 +35,18 @@
 #define ETH_P_ECHO	0x0200		/* Ethernet Echo packet		*/
 #define ETH_P_PUP	0x0400		/* Xerox PUP packet		*/
 #define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
+#define ETH_P_X25	0x0805		/* CCITT X.25			*/
 #define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
+#define	ETH_P_BPQ	0x08FF		/* G8BPQ AX.25 Ethernet Packet	[ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_DEC       0x6000          /* DEC Assigned proto           */
+#define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */
+#define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */
+#define ETH_P_DNA_RT    0x6003          /* DEC DNA Routing              */
+#define ETH_P_LAT       0x6004          /* DEC LAT                      */
+#define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
+#define ETH_P_CUST      0x6006          /* DEC Customer use             */
+#define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
 #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
-#define ETH_P_X25	0x0805		/* CCITT X.25			*/
 #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
 #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/
 #define ETH_P_IPX	0x8137		/* IPX over DIX			*/
@@ -44,6 +55,8 @@
 #define ETH_P_ALL	0x0003		/* Every packet (be careful!!!) */
 #define ETH_P_802_2	0x0004		/* 802.2 frames 		*/
 #define ETH_P_SNAP	0x0005		/* Internal only		*/
+#define ETH_P_DDCMP     0x0006          /* DEC DDCMP: Internal only     */
+
 /* This is an Ethernet frame header. */
 struct ethhdr {
   unsigned char		h_dest[ETH_ALEN];	/* destination eth addr	*/
diff -u --recursive --new-file v1.3.6/linux/include/linux/in.h linux/include/linux/in.h
--- v1.3.6/linux/include/linux/in.h	Thu Feb 23 13:32:05 1995
+++ linux/include/linux/in.h	Wed Jul  5 12:53:22 1995
@@ -18,6 +18,7 @@
 #ifndef _LINUX_IN_H
 #define _LINUX_IN_H
 
+#include <linux/types.h>
 
 /* Standard well-defined IP protocols.  */
 enum {
@@ -38,7 +39,7 @@
 
 /* Internet address. */
 struct in_addr {
-	unsigned long int	s_addr;
+	__u32	s_addr;
 };
 
 /* Request struct for multicast socket ops */
diff -u --recursive --new-file v1.3.6/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v1.3.6/linux/include/linux/interrupt.h	Mon May 29 11:13:51 1995
+++ linux/include/linux/interrupt.h	Sat Jul  1 19:06:00 1995
@@ -27,7 +27,8 @@
 	NET_BH,
 	IMMEDIATE_BH,
 	KEYBOARD_BH,
-	CYCLADES_BH
+	CYCLADES_BH,
+	CM206_BH
 };
 
 extern inline void mark_bh(int nr)
diff -u --recursive --new-file v1.3.6/linux/include/linux/major.h linux/include/linux/major.h
--- v1.3.6/linux/include/linux/major.h	Tue Apr 11 08:03:29 1995
+++ linux/include/linux/major.h	Sat Jul  1 19:06:00 1995
@@ -33,11 +33,11 @@
  * 13 -                        xt disk
  * 14 - sound card
  * 15 -                        cdu31a cdrom
- * 16 - sockets
- * 17 - af_unix
- * 18 - af_inet
+ * 16 - sockets                goldstar cdrom
+ * 17 - af_unix                optics cdrom
+ * 18 - af_inet                sanyo cdrom
  * 19 - cyclades /dev/ttyC*
- * 20 - cyclades /dev/cub*
+ * 20 - cyclades /dev/cub*     mitsumi (mcdx) cdrom
  * 21 - scsi generic
  * 22 -                        (at2disk)
  * 23 -                        mitsumi cdrom
@@ -47,6 +47,7 @@
  * 27 - qic117 tape            matsushita cdrom 3     minors 0..3
  * 28 -                        matsushita cdrom 4     minors 0..3
  * 29 -                        aztech/orchid/okano/wearnes cdrom
+ * 32 -                        philips/lms cm206 cdrom
  */
 
 #define UNNAMED_MAJOR	0
@@ -67,20 +68,25 @@
 #define SOUND_MAJOR	14
 #define CDU31A_CDROM_MAJOR 15
 #define SOCKET_MAJOR	16
+#define GOLDSTAR_CDROM_MAJOR 16
 #define AF_UNIX_MAJOR	17
+#define OPTICS_CDROM_MAJOR 17
 #define AF_INET_MAJOR	18
+#define SANYO_CDROM_MAJOR 18
 #define CYCLADES_MAJOR  19
 #define CYCLADESAUX_MAJOR 20
+#define MITSUMI_X_CDROM_MAJOR 20
 #define SCSI_GENERIC_MAJOR 21
 #define IDE1_MAJOR	22
 #define MITSUMI_CDROM_MAJOR 23
 #define CDU535_CDROM_MAJOR 24
 #define MATSUSHITA_CDROM_MAJOR 25
 #define MATSUSHITA_CDROM2_MAJOR 26
+#define QIC117_TAPE_MAJOR 27
 #define MATSUSHITA_CDROM3_MAJOR 27
 #define MATSUSHITA_CDROM4_MAJOR 28
-#define QIC117_TAPE_MAJOR 27
 #define AZTECH_CDROM_MAJOR 29
+#define CM206_CDROM_MAJOR 32
 
 /*
  * Tests for SCSI devices.
diff -u --recursive --new-file v1.3.6/linux/include/linux/mcdx.h linux/include/linux/mcdx.h
--- v1.3.6/linux/include/linux/mcdx.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/mcdx.h	Sat Jul  1 19:06:00 1995
@@ -0,0 +1,161 @@
+/*
+ * Definitions for the Mitsumi CDROM interface
+ * Copyright (C) 1995 Heiko Schlittermann
+ * VERSION: @VERSION@
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to
+ *  The Linux Community at all and ...
+ *  Martin Harris (he wrote the first Mitsumi Driver)
+ *  Eberhard Moenkeberg (he gave me much support and the initial kick)
+ *  Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they
+ *      improved the original driver)
+ *  John Tombs, Bjorn Ekwall (module support)
+ *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
+ *  Gerd Knorr (he lent me his PhotoCD)
+ *  Nils Faerber and Roger E. Wolff (extensivly tested the LU portion)
+ *  ... somebody forgotten?
+ *  
+ */
+
+/*
+ *	The following lines are for user configuration
+ *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *	{0|1} -- 1 if you want the driver detect your drive, may crash and
+ *	needs a long time to seek.  The higher the address the longer the
+ *	seek.
+ *
+ *  WARNING: AUTOPROBE doesn't work.
+ */
+#define MCDX_AUTOPROBE 0
+
+/*
+ *	Drive specific settings according to the jumpers on the controller
+ *	board(s).
+ *	o	MCDX_NDRIVES  :  number of used entries of the following table
+ *	o	MCDX_DRIVEMAP :  table of {i/o base, irq} per controller
+ *
+ *	NOTE: I didn't get a drive at irq 9(2) working.  Not even alone.
+ */
+#if MCDX_AUTOPROBE == 0
+	#define MCDX_NDRIVES 1
+	#define MCDX_DRIVEMAP {		\
+			{0x300, 10},	\
+			{0x304, 05},  	\
+			{0x000, 00},  	\
+			{0x000, 00},  	\
+			{0x000, 00},  	\
+	  	}
+#else
+	#error Autoprobing is not implemented yet.
+#endif
+
+/*	The name of the device */
+#define MCD "mcdx"	
+
+#ifdef NOWARN
+#define WARN(x)
+#else
+#define WARN(x) warn x
+#endif
+
+#if MCDX_DEBUG
+#define TRACE(x) trace x
+#define INIT 		0
+#define MALLOC 		0
+#define IOCTL 		0
+#define OPENCLOSE 	0
+#define HW		0
+#define TALK		0
+#define IRQ 		0
+#define TRANSFER 	0
+#define REQUEST	 	0
+#define MCDX_DEBUG_TALK 0
+#else
+#define TRACE(x)
+#endif
+
+/*	The following addresses are taken from the Mitsumi Reference 
+ *  and describe the possible i/o range for the controller.
+ */
+#define MCDX_IO_BEGIN	((char*) 0x300)	/* first base of i/o addr */
+#define MCDX_IO_END		((char*) 0x3fc)	/* last base of i/o addr */
+
+/*	Per controller 4 bytes i/o are needed. */
+#define MCDX_IO_SIZE		4
+
+/*
+ *	The Ports & bits
+ */
+
+#define MCDX_RBIT_OPEN       0x80
+#define MCDX_RBIT_DISKSET    0x40
+#define MCDX_RBIT_CHANGED    0x20
+#define MCDX_RBIT_CHECK      0x10
+#define MCDX_RBIT_AUDIOTR    0x08
+#define MCDX_RBIT_RDERR      0x04
+#define MCDX_RBIT_AUDIOBS    0x02
+#define MCDX_RBIT_CMDERR     0x01
+#define MCDX_RBIT_DOOR       0x10
+#define MCDX_RBIT_STEN       0x04
+#define MCDX_RBIT_DTEN       0x02
+
+
+/*
+ *	The commands.
+ */
+
+#define OPCODE	1		/* offset of opcode */
+#define MCDX_CMD_REQUEST_TOC		1, 0x10
+#define MCDX_CMD_REQUEST_STATUS		1, 0x40 
+#define MCDX_CMD_RESET				1, 0x60
+#define MCDX_CMD_REQUEST_DRIVE_MODE	1, 0xc2
+#define MCDX_CMD_SET_INTERLEAVE		2, 0xc8, 0
+#define MCDX_CMD_DATAMODE_SET		2, 0xa0, 0
+	#define MCDX_DATAMODE1		0x01
+	#define MCDX_DATAMODE2		0x02
+#define MCDX_CMD_LOCK_DOOR		2, 0xfe, 0
+
+#define READ_AHEAD			4	/* 8 Sectors (4K) */
+
+/*	Useful macros */
+#define e_door(x)		((x) & MCDX_RBIT_OPEN)
+#define e_check(x)		(~(x) & MCDX_RBIT_CHECK)
+#define e_notset(x)		(~(x) & MCDX_RBIT_DISKSET)
+#define e_changed(x)	((x) & MCDX_RBIT_CHANGED)
+#define e_audio(x)		((x) & MCDX_RBIT_AUDIOTR)
+#define e_audiobusy(x)	((x) & MCDX_RBIT_AUDIOBS)
+#define e_cmderr(x)		((x) & MCDX_RBIT_CMDERR)
+#define e_readerr(x)	((x) & MCDX_RBIT_RDERR)
+
+/**	no drive specific */
+#define MCDX_CDBLK	2048	/* 2048 cooked data each blk */
+
+#define MCDX_DATA_TIMEOUT	10	/* jiffies */
+
+/*
+ * Access to the msf array
+ */
+#define MSF_MIN		0			/* minute */
+#define MSF_SEC		1			/* second */
+#define MSF_FRM		2			/* frame  */
+
+/*
+ * Errors
+ */
+#define MCDX_E		1			/* unspec error */
+#define MCDX_EOM	2			/* end of media */
diff -u --recursive --new-file v1.3.6/linux/include/linux/msg.h linux/include/linux/msg.h
--- v1.3.6/linux/include/linux/msg.h	Fri Jun 16 22:02:55 1995
+++ linux/include/linux/msg.h	Sun Jul  2 21:26:28 1995
@@ -67,11 +67,11 @@
 #define MSG_STAT 11
 #define MSG_INFO 12
 
-extern asmlinkage int sys_msgget (key_t key, int msgflg);
-extern asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
-extern asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp,
+asmlinkage int sys_msgget (key_t key, int msgflg);
+asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
+asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp,
 		       int msgflg);
-extern asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf);
+asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.3.6/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.3.6/linux/include/linux/netdevice.h	Fri Jun 30 16:22:30 1995
+++ linux/include/linux/netdevice.h	Thu Jul  6 13:22:05 1995
@@ -73,11 +73,11 @@
   unsigned long		  mem_end;		/* sahared mem end	*/
   unsigned long		  mem_start;		/* shared mem start	*/
   unsigned long		  base_addr;		/* device I/O address	*/
+  unsigned long		  tbusy;		/* transmitter busy must be long for bitops */
   unsigned char		  irq;			/* device IRQ number	*/
 
   /* Low-level status flags. */
   volatile unsigned char  start,		/* start an operation	*/
-                          tbusy,		/* transmitter busy	*/
                           interrupt;		/* interrupt arrived	*/
 
   struct device		  *next;
diff -u --recursive --new-file v1.3.6/linux/include/linux/optcd.h linux/include/linux/optcd.h
--- v1.3.6/linux/include/linux/optcd.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/optcd.h	Sat Jul  1 19:06:00 1995
@@ -0,0 +1,200 @@
+
+/* Defines for the Optics Storage 8000AT CDROM drive. */
+
+#ifndef _LINUX_OPTCD_H
+
+#define _LINUX_OPTCD_H
+
+/* Drive registers */
+#define OPTCD_PORTBASE	0x340
+/* Read */
+#define DATA_PORT	optcd_port	/* Read data/status */
+#define STATUS_PORT	optcd_port+1	/* Indicate data/status availability */
+/* Write */
+#define COMIN_PORT	optcd_port	/* For passing command/parameter */
+#define RESET_PORT	optcd_port+1	/* Write anything and wait 0.5 sec */
+#define HCON_PORT	optcd_port+2	/* Host Xfer Configuration */
+
+
+/* Command completion/status read from DATA register */
+#define ST_DRVERR		0x80
+#define ST_DOOR_OPEN		0x40
+#define ST_MIXEDMODE_DISK	0x20
+#define ST_MODE_BITS		0x1c
+#define ST_M_STOP		0x00
+#define ST_M_READ		0x04
+#define ST_M_AUDIO		0x04
+#define ST_M_PAUSE		0x08
+#define ST_M_INITIAL		0x0c
+#define ST_M_ERROR		0x10
+#define ST_M_OTHERS		0x14
+#define	ST_MODE2TRACK		0x02
+#define	ST_DSK_CHG		0x01
+#define ST_L_LOCK		0x01
+#define ST_CMD_OK		0x00
+#define ST_OP_OK		0x01
+#define ST_PA_OK		0x02
+#define ST_OP_ERROR		0x05
+#define ST_PA_ERROR		0x06
+
+/* Error codes (appear as command completion code from DATA register) */
+/* Player related errors */
+#define ERR_ILLCMD	0x11	/* Illegal command to player module */
+#define ERR_ILLPARM	0x12	/* Illegal parameter to player module */
+#define ERR_SLEDGE	0x13
+#define ERR_FOCUS	0x14
+#define ERR_MOTOR	0x15
+#define ERR_RADIAL	0x16
+#define ERR_PLL		0x17	/* PLL lock error */
+#define ERR_SUB_TIM	0x18	/* Subcode timeout error */
+#define ERR_SUB_NF	0x19	/* Subcode not found error */
+#define ERR_TRAY	0x1a
+#define ERR_TOC		0x1b	/* Table of Contents read error */
+#define ERR_JUMP	0x1c
+/* Data errors */
+#define ERR_MODE	0x21
+#define ERR_FORM	0x22
+#define ERR_HEADADDR	0x23	/* Header Address not found */
+#define ERR_CRC		0x24
+#define ERR_ECC		0x25	/* Uncorrectable ECC error */
+#define ERR_CRC_UNC	0x26	/* CRC error and uncorrectable error */
+#define ERR_ILLBSYNC	0x27	/* Illegal block sync error */
+#define ERR_VDST	0x28	/* VDST not found */
+/* Timeout errors */
+#define ERR_READ_TIM	0x31	/* Read timeout error */
+#define ERR_DEC_STP	0x32	/* Decoder stopped */
+#define ERR_DEC_TIM	0x33	/* Decoder interrupt timeout error */
+/* Function abort codes */
+#define ERR_KEY		0x41	/* Key -Detected abort */
+#define ERR_READ_FINISH	0x42	/* Read Finish */
+/* Second Byte diagnostic codes */
+#define ERR_NOBSYNC	0x01	/* No block sync */
+#define ERR_SHORTB	0x02	/* Short block */
+#define ERR_LONGB	0x03	/* Long block */
+#define ERR_SHORTDSP	0x04	/* Short DSP word */
+#define ERR_LONGDSP	0x05	/* Long DSP word */
+
+
+/* Status availability flags read from STATUS register */
+#define FL_EJECT	0x20
+#define FL_WAIT		0x10	/* active low */
+#define FL_EOP		0x08	/* active low */
+#define FL_STEN		0x04	/* Status available when low */
+#define FL_DTEN		0x02	/* Data available when low */
+#define FL_DRQ		0x01	/* active low */
+#define FL_RESET	0xde	/* These bits are high after a reset */
+#define FL_STDT		(FL_STEN|FL_DTEN)
+
+
+/* Transfer mode, written to HCON register */
+#define HCON_DTS	0x08
+#define HCON_SDRQB	0x04
+#define HCON_LOHI	0x02
+#define HCON_DMA16	0x01
+
+
+/* Drive command set, written to COMIN register */
+/* Quick response commands */
+#define COMDRVST	0x20	/* Drive Status Read */
+#define COMERRST	0x21	/* Error Status Read */
+#define COMIOCTLISTAT	0x22	/* Status Read; reset disk changed bit */
+#define COMINITSINGLE	0x28	/* Initialize Single Speed */
+#define COMINITDOUBLE	0x29	/* Initialize Double Speed */
+#define COMUNLOCK	0x30	/* Unlock */
+#define COMLOCK		0x31	/* Lock */
+#define COMLOCKST	0x32	/* Lock/Unlock Status */
+#define COMVERSION	0x40	/* Get Firmware Revision */
+#define COMVOIDREADMODE	0x50	/* Void Data Read Mode */
+/* Read commands */
+#define COMFETCH	0x60	/* Prefetch Data */
+#define COMREAD		0x61	/* Read */
+#define COMREADRAW	0x62	/* Read Raw Data */
+#define COMREADALL	0x63	/* Read All 2646 Bytes */
+/* Player control commands */
+#define COMLEADIN	0x70	/* Seek To Lead-in */
+#define COMSEEK		0x71	/* Seek */
+#define COMPAUSEON	0x80	/* Pause On */
+#define COMPAUSEOFF	0x81	/* Pause Off */
+#define COMSTOP		0x82	/* Stop */
+#define COMOPEN		0x90	/* Open Tray Door */
+#define COMCLOSE	0x91	/* Close Tray Door */
+#define COMPLAY		0xa0	/* Audio Play */
+#define COMPLAY_TNO	0xa2	/* Audio Play By Track Number */
+#define COMSUBQ		0xb0	/* Read Sub-q Code */
+#define COMLOCATION	0xb1	/* Read Head Position */
+/* Audio control commands */
+#define COMCHCTRL	0xc0	/* Audio Channel Control */
+/* Miscellaneous (test) commands */
+#define COMDRVTEST	0xd0	/* Write Test Bytes */
+#define COMTEST		0xd1	/* Diagnostic Test */
+
+
+#define BUSY_TIMEOUT		10000000	/* for busy wait */
+#define SLEEP_TIMEOUT		400		/* for timer wait */
+#define READ_TIMEOUT		3000		/* for poll wait */
+#define RESET_WAIT		1000
+
+#define SET_TIMER(func, jifs) \
+	delay_timer.expires = jifs; \
+	delay_timer.function = (void *) func; \
+	add_timer(&delay_timer);
+#define CLEAR_TIMER		del_timer(&delay_timer)
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct opt_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct opt_DiskInfo {
+	unsigned char	first;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+};
+
+struct opt_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
+
+
+#define CURRENT_VALID \
+	(CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR \
+	 && CURRENT -> cmd == READ && CURRENT -> sector != -1)
+
+
+#undef	DEBUG_DRIVE_IF		/* Low level drive interface */
+#undef	DEBUG_COMMANDS		/* Commands sent to drive */
+#undef	DEBUG_VFS		/* VFS interface */
+#undef	DEBUG_CONV		/* Address conversions */
+#undef	DEBUG_TOC		/* Q-channel and Table of Contents */
+#undef	DEBUG_BUFFERS		/* Buffering and block size conversion */
+#undef	DEBUG_REQUEST		/* Request mechanism */
+#undef	DEBUG_STATE		/* State machine */
+
+
+/* Low level drive interface */
+
+/* Errors that can occur in the low level interface */
+#define ERR_IF_CMD_TIMEOUT	0x100
+#define ERR_IF_ERR_TIMEOUT	0x101
+#define ERR_IF_RESP_TIMEOUT	0x102
+#define ERR_IF_DATA_TIMEOUT	0x103
+#define ERR_IF_NOSTAT		0x104
+/* Errors in table of contents */
+#define ERR_TOC_MISSINGINFO	0x120
+#define ERR_TOC_MISSINGENTRY	0x121
+
+/* End .h defines */
+#endif _LINUX_OPTCD_H
diff -u --recursive --new-file v1.3.6/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v1.3.6/linux/include/linux/sbpcd.h	Tue Jun 27 14:11:46 1995
+++ linux/include/linux/sbpcd.h	Sat Jul  1 19:35:18 1995
@@ -121,7 +121,7 @@
 #define _LINUX_SBPCD_H
 /*==========================================================================*/
 /*==========================================================================*/
-#define LONG_TIMING 1 /* test against timeouts with "gold" CDs on CR-521 */
+#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
 #undef  FUTURE
 
 #define TEST_UPC 0
@@ -139,8 +139,6 @@
 /*
  * "private" IOCTL functions
  */
-#define CDROMRESET		0x5380 /* hard-rest the drive */
-#define CDROMVOLREAD		0x5381 /* let the drive tell its volume settings */
 #define CDROMAUDIOBUFSIZ	0x5382 /* set the audio buffer size */
 
 /*==========================================================================*/
diff -u --recursive --new-file v1.3.6/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.3.6/linux/include/linux/sched.h	Fri Jun 16 22:02:55 1995
+++ linux/include/linux/sched.h	Wed Jul  5 12:53:22 1995
@@ -215,6 +215,12 @@
 #define COPYFD		0x00000200	/* set if fd's should be copied, not shared (NI) */
 
 /*
+ * Limit the stack by to some sane default: root can always
+ * increase this limit if needed..  8MB seems reasonable.
+ */
+#define _STK_LIM	(8*1024*1024)
+
+/*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
  */
@@ -233,7 +239,7 @@
 /* uid etc */	0,0,0,0,0,0,0,0, \
 /* timeout */	0,0,0,0,0,0,0,0,0,0,0,0, \
 /* rlimits */   { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
-		  {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
+		  {LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM},  \
 		  {       0, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
 		  {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, {NR_OPEN, NR_OPEN}}, \
 /* math */	0, \
diff -u --recursive --new-file v1.3.6/linux/include/linux/sem.h linux/include/linux/sem.h
--- v1.3.6/linux/include/linux/sem.h	Fri Jun 16 22:02:55 1995
+++ linux/include/linux/sem.h	Sun Jul  2 21:26:28 1995
@@ -103,9 +103,9 @@
     short *	       semadj;	  /* array of adjustments, one per semaphore */
 };
 
-extern asmlinkage int sys_semget (key_t key, int nsems, int semflg);
-extern asmlinkage int sys_semop (int semid, struct sembuf *sops, unsigned nsops);
-extern asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg);
+asmlinkage int sys_semget (key_t key, int nsems, int semflg);
+asmlinkage int sys_semop (int semid, struct sembuf *sops, unsigned nsops);
+asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.3.6/linux/include/linux/shm.h linux/include/linux/shm.h
--- v1.3.6/linux/include/linux/shm.h	Fri Jun 16 22:02:55 1995
+++ linux/include/linux/shm.h	Sun Jul  2 21:26:28 1995
@@ -59,10 +59,10 @@
 	ulong swap_successes;
 };
 
-extern asmlinkage int sys_shmget (key_t key, int size, int flag);
-extern asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr);
-extern asmlinkage int sys_shmdt (char *shmaddr);
-extern asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
+asmlinkage int sys_shmget (key_t key, int size, int flag);
+asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr);
+asmlinkage int sys_shmdt (char *shmaddr);
+asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.3.6/linux/include/linux/sjcd.h linux/include/linux/sjcd.h
--- v1.3.6/linux/include/linux/sjcd.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/sjcd.h	Sat Jul  1 19:06:00 1995
@@ -0,0 +1,133 @@
+/*
+ * Definitions for a Sanyo CD-ROM interface
+ *
+ *   Copyright (C) 1995  Vadim V. Model
+ *
+ *   model@cecmow.enet.dec.com
+ *   vadim@rbrf.msk.su
+ *   vadim@ipsun.ras.ru
+ *
+ */
+
+#ifndef __SJCD_H__
+#define __SJCD_H__
+
+/*
+ * Change this to set the I/O port address.
+ */
+#define SJCD_BASE_ADDR      0x340
+
+/*
+ * Change this to set the irq.
+ */
+#define SJCD_INTR_NR        10
+
+/*
+ * port access macros
+ */
+#define SJCDPORT( x )       ( sjcd_port + ( x ) )
+
+/* status bits */
+
+#define SST_NOT_READY       0x10        /* no disk in the drive */
+#define SST_MEDIA_CHANGED   0x20        /* disk is changed */
+#define SST_DOOR_OPENED     0x40        /* door is open */
+
+/* flag bits */
+
+/* commands */
+
+#define SCMD_EJECT_TRAY     0xD0        /* eject tray if not locked */
+#define SCMD_LOCK_TRAY      0xD2        /* lock tray when in */
+#define SCMD_UNLOCK_TRAY    0xD4        /* unlock tray when in */
+#define SCMD_CLOSE_TRAY     0xD6        /* load tray in */
+
+#define SCMD_RESET          0xFA        /* soft reset */
+#define SCMD_GET_STATUS     0x80
+#define SCMD_GET_VERSION    0xCC
+
+#define SCMD_DATA_READ      0xA0
+#define SCMD_SEEK           0xA0
+#define SCMD_PLAY           0xA0
+
+#define SCMD_GET_QINFO      0xA8
+
+#define SCMD_SET_MODE       0xC4
+#define SCMD_MODE_PLAY      0xE0
+#define SCMD_MODE_COOKED    0xF8
+#define SCMD_MODE_RAW       0xF9
+#define SCMD_MODE_x20_BIT   0x20
+
+#define SCMD_SET_VOLUME     0xAE
+#define SCMD_PAUSE          0xE0
+#define SCMD_STOP           0xE0
+
+#define SCMD_GET_DISK_INFO  0xAA
+#define SCMD_GET_1_TRACK    0xA0    /* get the first track information */
+#define SCMD_GET_L_TRACK    0xA1    /* get the last track information */
+#define SCMD_GET_D_SIZE     0xA2    /* get the whole disk information */
+
+/*
+ * borrowed from hd.c
+ */
+#define S_READ_DATA( port, buf, nr )      insb( port, buf, nr )
+
+#define SJCD_MAX_TRACKS		100
+
+struct msf {
+  unsigned char   min;
+  unsigned char   sec;
+  unsigned char   frame;
+};
+
+struct sjcd_hw_disk_info {
+  unsigned char track_control;
+  unsigned char track_no;
+  unsigned char x, y, z;
+  union {
+    unsigned char track_no;
+    struct msf track_msf;
+  } un;
+};
+
+struct sjcd_hw_qinfo {
+  unsigned char track_control;
+  unsigned char track_no;
+  unsigned char x;
+  struct msf rel;
+  struct msf abs;
+};
+
+struct sjcd_play_msf {
+  struct msf  start;
+  struct msf  end;
+};
+
+struct sjcd_disk_info {
+  unsigned char   first;
+  unsigned char   last;
+  struct msf      disk_length;
+  struct msf      first_track;
+};
+
+struct sjcd_toc {
+  unsigned char   ctrl_addr;
+  unsigned char   track;
+  unsigned char   point_index;
+  struct msf      track_time;
+  struct msf      disk_time;
+};
+
+struct sjcd_stat {
+  int ticks;
+  int tticks[ 8 ];
+  int idle_ticks;
+  int start_ticks;
+  int mode_ticks;
+  int read_ticks;
+  int data_ticks;
+  int stop_ticks;
+  int stopping_ticks;
+};
+
+#endif
diff -u --recursive --new-file v1.3.6/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.6/linux/include/linux/skbuff.h	Fri Jun 30 16:22:30 1995
+++ linux/include/linux/skbuff.h	Wed Jul  5 13:06:27 1995
@@ -18,9 +18,10 @@
 #include <linux/time.h>
 #include <linux/config.h>
 
-/* #define CONFIG_SKB_CHECK 1 */
+#define CONFIG_SKB_CHECK 0
 
 #define HAVE_ALLOC_SKB		/* For the drivers to know */
+#define HAVE_ALIGNABLE_SKB	/* Ditto 8)		   */
 
 
 #define FREE_READ	1
@@ -55,11 +56,18 @@
 	unsigned char	*raw;
 	unsigned long	seq;
   } h;
+  
+  union {						/* As yet incomplete physical layer views 	*/
+  	unsigned char 		*raw;
+  	struct ethhdr		*ethernet;
+  } mac;
+  
   struct iphdr			*ip_hdr;		/* For IPPROTO_RAW 				*/
   unsigned long 		len;			/* Length of actual data			*/
   unsigned long 		saddr;			/* IP source address				*/
   unsigned long 		daddr;			/* IP target address				*/
   unsigned long			raddr;			/* IP next hop address				*/
+  unsigned long			csum;			/* Checksum 					*/
   volatile char 		acked,			/* Are we acked ?				*/
 				used,			/* Are we in use ?				*/
 				free,			/* How to free this buffer			*/
@@ -67,7 +75,8 @@
   unsigned char			tries,			/* Times tried					*/
   				lock,			/* Are we locked ?				*/
   				localroute,		/* Local routing asserted for this frame	*/
-  				pkt_type;		/* Packet class					*/
+  				pkt_type,		/* Packet class					*/
+  				ip_summed;		/* Driver fed us an IP checksum			*/
 #define PACKET_HOST		0			/* To us					*/
 #define PACKET_BROADCAST	1			/* To all					*/
 #define PACKET_MULTICAST	2			/* To group					*/
@@ -84,7 +93,7 @@
 #define SK_WMEM_MAX	32767
 #define SK_RMEM_MAX	32767
 
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 #define SK_FREED_SKB	0x0DE2C0DE
 #define SK_GOOD_SKB	0xDEC0DED1
 #define SK_HEAD_SKB	0x12231298
diff -u --recursive --new-file v1.3.6/linux/include/linux/smb.h linux/include/linux/smb.h
--- v1.3.6/linux/include/linux/smb.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/smb.h	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,123 @@
+/*
+ *  smb.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_H
+#define _LINUX_SMB_H
+
+#define SMB_PORT 139
+#define SMB_MAXNAMELEN 255
+#define SMB_MAXPATHLEN 1024
+
+#define SMB_DEF_MAX_XMIT 32768
+
+/* Allocate max. 1 page */
+#define TRANS2_MAX_TRANSFER (4096-17)
+
+#include <asm/types.h>
+#ifdef __KERNEL__
+typedef u8  byte;
+typedef u16 word;
+typedef u32 dword;
+#else
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long dword;
+#endif
+
+/*
+ * Set/Get values in SMB-byte order
+ */
+#define ARCH i386
+#if (ARCH == i386)
+#define BVAL(p,off)      (*((byte  *)(((void *)p)+off)))
+#define WVAL(p,off)      (*((word  *)(((void *)p)+off)))
+#define DVAL(p,off)      (*((dword *)(((void *)p)+off)))
+#define BSET(p,off,new)  (*((byte  *)(((void *)p)+off))=(new))
+#define WSET(p,off,new)  (*((word  *)(((void *)p)+off))=(new))
+#define DSET(p,off,new)  (*((dword *)(((void *)p)+off))=(new))
+
+/* where to find the base of the SMB packet proper */
+#define smb_base(buf) ((byte *)(((byte *)(buf))+4))
+
+#else
+#error "Currently only on 386, sorry"
+#endif
+
+
+#define LANMAN1
+#define LANMAN2
+
+enum smb_protocol { 
+	PROTOCOL_NONE, 
+	PROTOCOL_CORE, 
+	PROTOCOL_COREPLUS, 
+	PROTOCOL_LANMAN1, 
+	PROTOCOL_LANMAN2, 
+	PROTOCOL_NT1 
+};
+
+enum smb_case_hndl {
+	CASE_DEFAULT,
+	CASE_LOWER,
+	CASE_UPPER
+};
+
+#ifdef __KERNEL__
+
+enum smb_conn_state {
+        CONN_VALID,             /* everything's fine */
+        CONN_INVALID,           /* Something went wrong, but did not
+                                   try to reconnect yet. */
+        CONN_RETRIED            /* Tried a reconnection, but was refused */
+};
+
+struct smb_dskattr {
+        word total;
+        word allocblocks;
+        word blocksize;
+        word free;
+};
+
+/*
+ * Contains all relevant data on a SMB networked file.
+ */
+struct smb_dirent {
+        int             opened; /* is it open on the fileserver? */
+	word            fileid;	/* What id to handle a file with? */
+	word            attr;	/* Attribute fields, DOS value */
+
+        time_t atime, mtime,    /* Times, as seen by the server, normalized */
+               ctime;           /* to UTC. The ugly conversion happens in */
+                                /* proc.c */
+
+	unsigned long   size;	/* File size. */
+	unsigned short  access;	/* Access bits. */
+        unsigned long   f_pos;	/* File position. (For readdir.) */
+	char*           path;   /* Complete path, MS-DOS notation, with '\' */
+	int             len;	/* Namelength. */
+};
+
+#endif  /* __KERNEL__ */
+#endif  /* _LINUX_SMB_H */
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h
--- v1.3.6/linux/include/linux/smb_fs.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/smb_fs.h	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,174 @@
+/*
+ *  smb_fs.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_FS_H
+#define _LINUX_SMB_FS_H
+
+#include <linux/smb.h>
+#include <linux/fs.h>
+#include <linux/in.h>
+#include <linux/types.h>
+
+#include <linux/smb_mount.h>
+#include <linux/smb_fs_sb.h>
+#include <linux/smb_fs_i.h>
+
+/*
+ * ioctl commands
+ */
+#define	SMB_IOC_GETMOUNTUID		_IOR('u', 1, uid_t)
+
+#ifdef __KERNEL__
+
+/*
+ * The readdir cache size controls how many directory entries are cached.
+ */
+#define SMB_READDIR_CACHE_SIZE        64
+
+/*
+ * This defines the number of filenames cached in memory to avoid 
+ * constructing filenames from \
+ */
+#define SMB_CACHE_TABLE_SIZE          64
+
+#define SMB_SUPER_MAGIC               0x517B
+
+
+
+#define SMB_SBP(sb)          ((struct smb_sb_info *)(sb->u.generic_sbp))
+#define SMB_INOP(inode)      ((struct smb_inode_info *)(inode->u.generic_ip))
+
+#define SMB_SERVER(inode)    (&(SMB_SBP(inode->i_sb)->s_server))
+#define SMB_SERVATTR(inode)  (&(SMB_SBP(inode->i_sb)->s_attr))
+
+#define SMB_FINFO(inode)     (&(SMB_INOP(inode)->finfo))
+
+#define SMB_HEADER_LEN   37     /* includes everything up to, but not
+                                 * including smb_bcc */
+
+static inline int min(int a, int b) {
+  return a<b ? a : b;
+}
+
+#ifdef DEBUG_SMB_MALLOC
+
+#include <linux/malloc.h>
+
+extern int smb_malloced;
+extern int smb_current_malloced;
+
+static inline void *
+smb_kmalloc(unsigned int size, int priority)
+{
+        smb_malloced += 1;
+        smb_current_malloced += 1;
+        return kmalloc(size, priority);
+}
+
+static inline void
+smb_kfree_s(void *obj, int size)
+{
+        smb_current_malloced -= 1;
+        kfree_s(obj, size);
+}
+
+#else /* DEBUG_SMB_MALLOC */
+
+#define smb_kmalloc(s,p) kmalloc(s,p)
+#define smb_kfree_s(o,s) kfree_s(o,s)
+
+#endif /* DEBUG_SMB_MALLOC */
+
+#if DEBUG_SMB > 0
+#define DPRINTK(format, args...) printk(format , ## args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+#if DEBUG_SMB > 1
+#define DDPRINTK(format, args...) printk(format , ## args)
+#else
+#define DDPRINTK(format, args...)
+#endif
+
+
+/* linux/fs/smbfs/file.c */
+extern struct inode_operations smb_file_inode_operations;
+int smb_make_open(struct inode *i, int right);
+
+/* linux/fs/smbfs/dir.c */
+extern struct inode_operations smb_dir_inode_operations;
+void smb_free_inode_info(struct smb_inode_info *i);
+void smb_free_all_inodes(struct smb_server *server);
+int  smb_init_root(struct smb_server *server);
+void smb_init_dir_cache(void);
+void smb_invalid_dir_cache(unsigned long ino);
+void smb_invalidate_all_inodes(struct smb_server *server);
+void smb_free_dir_cache(void);
+
+/* linux/fs/smbfs/ioctl.c */
+int smb_ioctl (struct inode * inode, struct file * filp,
+               unsigned int cmd, unsigned long arg);
+
+/* linux/fs/smbfs/inode.c */
+struct super_block *smb_read_super(struct super_block *sb,
+                                   void *raw_data, int silent);
+int smb_notify_change(struct inode *inode, struct iattr *attr);
+void smb_invalidate_connection(struct smb_server *server);
+int smb_conn_is_valid(struct smb_server *server);
+
+/* linux/fs/smbfs/proc.c */
+dword smb_len(unsigned char *packet);
+int smb_proc_open(struct smb_server *server, const char *pathname,
+                  int len, struct smb_dirent *entry);
+int smb_proc_close(struct smb_server *server, struct smb_dirent *finfo);
+int smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, 
+		  off_t offset, long count, char *data, int fs);
+int smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
+		   off_t offset, int count, char *data);
+int smb_proc_create(struct smb_server *server, const char *path,
+                    int len, struct smb_dirent *entry);
+int smb_proc_mknew(struct smb_server *server, const char *path, int len,
+                   struct smb_dirent *entry);
+int smb_proc_mv(struct smb_server *server, const char *opath, const int olen, 
+		const char *npath, const int nlen);
+int smb_proc_mkdir(struct smb_server *server, const char *path, const int len);
+int smb_proc_rmdir(struct smb_server *server, const char *path, const int len);
+int smb_proc_unlink(struct smb_server *server, const char *path,
+                    const int len);
+int smb_proc_readdir(struct smb_server *server, struct inode *dir,
+                     int fpos, int cache_size, 
+		     struct smb_dirent *entry);
+int smb_proc_getattr(struct smb_server *server, const char *path,
+                     int len, struct smb_dirent *entry);
+int smb_proc_setattr(struct smb_server *server,
+                     struct inode *ino,
+                     struct smb_dirent *new_finfo);
+int smb_proc_chkpath(struct smb_server *server, char *path, int len,
+                     int *result);
+int smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr);
+int smb_proc_reconnect(struct smb_server *server);
+int smb_proc_connect(struct smb_server *server);
+int smb_proc_disconnect(struct smb_server *server);
+int smb_proc_trunc(struct smb_server *server, word fid, dword length);
+
+/* linux/fs/smbfs/sock.c */
+int smb_release(struct smb_server *server);
+int smb_connect(struct smb_server *server);
+int smb_request(struct smb_server *server);
+int smb_catch_keepalive(struct smb_server *server);
+int smb_dont_catch_keepalive(struct smb_server *server);
+int smb_trans2_request(struct smb_server *server,
+                       int *data_len, int *param_len,
+                       char **data, char **param);
+
+/* linux/fs/smbfs/mmap.c */
+int smb_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SMB_FS_H */
diff -u --recursive --new-file v1.3.6/linux/include/linux/smb_fs_i.h linux/include/linux/smb_fs_i.h
--- v1.3.6/linux/include/linux/smb_fs_i.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/smb_fs_i.h	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,51 @@
+/*
+ *  smb_fs_i.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_FS_I
+#define _LINUX_SMB_FS_I
+
+#ifdef __KERNEL__
+#include <linux/smb.h>
+
+enum smb_inode_state {
+        INODE_VALID = 19,       /* Inode currently in use */
+        INODE_LOOKED_UP,        /* directly before iget */
+        INODE_CACHED,           /* in a path to an inode which is in use */
+        INODE_INVALID
+};
+
+/*
+ * smb fs inode data (in memory only)
+ */
+struct smb_inode_info {
+        enum smb_inode_state state;
+        int nused;              /* for directories:
+                                   number of references in memory */
+        struct smb_inode_info *dir;
+        struct smb_inode_info *next, *prev;
+        struct smb_dirent finfo;
+};
+
+#endif
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v1.3.6/linux/include/linux/smb_fs_sb.h linux/include/linux/smb_fs_sb.h
--- v1.3.6/linux/include/linux/smb_fs_sb.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/smb_fs_sb.h	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,78 @@
+/*
+ *  smb_fs_sb.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#ifndef _SMB_FS_SB
+#define _SMB_FS_SB
+
+#include <linux/smb.h>
+#include <linux/smb_fs_i.h>
+#include <linux/smb_mount.h>
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+
+struct smb_server {
+	enum smb_protocol  protocol;       /* The protocol this
+                                              connection accepts. */
+	enum smb_case_hndl case_handling;
+	struct file *      sock_file;	   /* The socket we transfer
+                                              data on. */
+	int                lock;       	   /* To prevent mismatch in
+                                              protocols. */
+	struct wait_queue *wait;
+
+	word               max_xmit;
+	char               hostname[256];
+	word               pid;
+	word               server_uid;
+	word               mid;
+	word               tid;
+
+        struct smb_mount_data m; /* We store the complete information here
+                                  * to be able to reconnect.
+                                  */
+
+        unsigned short     rcls; /* The error codes we received */
+        unsigned short     err;
+	unsigned char *    packet;
+
+        enum smb_conn_state state;
+        unsigned long reconnect_time; /* The time of the last attempt */
+
+        /* The following are LANMAN 1.0 options transferred to us in
+           SMBnegprot */
+        word   secmode;
+        word   maxxmt;
+        word   maxmux;
+        word   maxvcs;
+        word   blkmode;
+        dword  sesskey;
+
+        /* We use our on data_ready callback, but need the original one */
+        void *data_ready;
+
+        /* We do not have unique numbers for files in the smb protocol
+           like NFS-filehandles. (SMB was designed for DOS, not for
+           UNIX!) So we have to create our own inode numbers. We keep
+           a complete path of smb_inode_info's to each active
+           inode. The inode number is then created by the address of
+           this structure. */
+        struct smb_inode_info root;
+};
+
+/*
+ * This is the part of the super-block (in memory) for the SMB file system.
+ */
+
+struct smb_sb_info {
+	struct smb_server  s_server;
+	struct smb_dskattr s_attr;
+};
+
+#endif /* __KERNEL__ */
+
+#endif
diff -u --recursive --new-file v1.3.6/linux/include/linux/smb_mount.h linux/include/linux/smb_mount.h
--- v1.3.6/linux/include/linux/smb_mount.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/smb_mount.h	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,40 @@
+/*
+ *  smb_mount.h
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_MOUNT_H
+#define _LINUX_SMB_MOUNT_H
+
+#include <linux/types.h>
+#include <linux/in.h>
+
+#define SMB_MOUNT_VERSION 4
+
+struct smb_mount_data {
+	int version;
+	unsigned int fd;
+        uid_t mounted_uid;      /* Who may umount() this filesystem? */
+	struct sockaddr_in addr;
+
+	char server_name[17];
+        char client_name[17];
+	char service[64];
+        char root_path[64];
+
+        char username[64];
+	char password[64];
+
+	unsigned short max_xmit;
+
+        uid_t uid;
+        gid_t gid;
+        mode_t file_mode;
+        mode_t dir_mode;
+};
+
+#endif
+
+
diff -u --recursive --new-file v1.3.6/linux/include/linux/smbno.h linux/include/linux/smbno.h
--- v1.3.6/linux/include/linux/smbno.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/smbno.h	Sat Jul  1 19:00:19 1995
@@ -0,0 +1,269 @@
+#ifndef _SMBNO_H_
+#define _SMBNO_H_
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY (1L<<0)
+#define aHIDDEN (1L<<1)
+#define aSYSTEM (1L<<2)
+#define aVOLID (1L<<3)
+#define aDIR (1L<<4)
+#define aARCH (1L<<5)
+
+/* error classes */
+#define SUCCESS 0  /* The request was successful. */
+#define ERRDOS 0x01 /*  Error is from the core DOS operating system set. */
+#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/
+#define ERRHRD 0x03  /* Error is an hardware error. */
+#define ERRCMD 0xFF  /* Command was not in the "SMB" format. */
+
+/* SMB X/Open error codes for the ERRdos error class */
+
+#define ERRbadfunc 1            /* Invalid function (or system call) */
+#define ERRbadfile 2            /* File not found (pathname error) */
+#define ERRbadpath 3            /* Directory not found */
+#define ERRnofids 4             /* Too many open files */
+#define ERRnoaccess 5           /* Access denied */
+#define ERRbadfid 6             /* Invalid fid */
+#define ERRbadmcb 7             /* Memory control blocks destroyed */
+#define ERRnomem 8              /* Out of memory */
+#define ERRbadmem 9             /* Invalid memory block address */
+#define ERRbadenv 10            /* Invalid environment */
+#define ERRbadformat 11         /* Invalid format */
+#define ERRbadaccess 12         /* Invalid open mode */
+#define ERRbaddata 13           /* Invalid data (only from ioctl call) */
+#define ERRres 14               /* reserved */
+#define ERRbaddrive 15          /* Invalid drive */
+#define ERRremcd 16             /* Attempt to delete current directory */
+#define ERRdiffdevice 17        /* rename/move across different filesystems */
+#define ERRnofiles 18           /* no more files found in file search */
+#define ERRbadshare 32          /* Share mode on file conflict with open mode */
+#define ERRlock 33              /* Lock request conflicts with existing lock */
+#define ERRfilexists 80         /* File in operation already exists */
+#define ERRbadpipe 230          /* Named pipe invalid */
+#define ERRpipebusy 231         /* All instances of pipe are busy */
+#define ERRpipeclosing 232      /* named pipe close in progress */
+#define ERRnotconnected 233     /* No process on other end of named pipe */
+#define ERRmoredata 234         /* More data to be returned */
+#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */
+#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not suppored */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1              /* Non specific error code */
+#define ERRbadpw 2              /* Bad password */
+#define ERRbadtype 3            /* reserved */
+#define ERRaccess 4          /* No permissions to do the requested operation */
+#define ERRinvnid 5             /* tid invalid */
+#define ERRinvnetname 6         /* Invalid servername */
+#define ERRinvdevice 7          /* Invalid device */
+#define ERRqfull 49             /* Print queue full */
+#define ERRqtoobig 50           /* Queued item too big */
+#define ERRinvpfid 52           /* Invalid print file in smb_fid */
+#define ERRsmbcmd 64            /* Unrecognised command */
+#define ERRsrverror 65          /* smb server internal error */
+#define ERRfilespecs 67         /* fid and pathname invalid combination */
+#define ERRbadlink 68           /* reserved */
+#define ERRbadpermits 69        /* Access specified for a file is not valid */
+#define ERRbadpid 70            /* reserved */
+#define ERRsetattrmode 71       /* attribute mode invalid */
+#define ERRpaused 81            /* Message server paused */
+#define ERRmsgoff 82            /* Not receiving messages */
+#define ERRnoroom 83            /* No room for message */
+#define ERRrmuns 87             /* too many remote usernames */
+#define ERRtimeout 88           /* operation timed out */
+#define ERRnoresource  89   /* No resources currently available for request. */
+#define ERRtoomanyuids 90       /* too many userids */
+#define ERRbaduid 91            /* bad userid */
+#define ERRuseMPX 250    /* temporarily unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251    /* temporarily unable to use raw mode, use std.mode */
+#define ERRcontMPX 252          /* resume MPX mode */
+#define ERRbadPW                /* reserved */
+#define ERRnosupport 0xFFFF
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19           /* read only media */
+#define ERRbadunit 20           /* Unknown device */
+#define ERRnotready 21          /* Drive not ready */
+#define ERRbadcmd 22            /* Unknown command */
+#define ERRdata 23              /* Data (CRC) error */
+#define ERRbadreq 24            /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29             /* write fault */
+#define ERRread 30              /* read fault */
+#define ERRgeneral 31           /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36       /* share buffer exceeded */
+#define ERRdiskfull 39
+
+/* offsets into message for common items */
+#define smb_com 8
+#define smb_rcls 9
+#define smb_reh 10
+#define smb_err 11
+#define smb_flg 13
+#define smb_flg2 14
+#define smb_reb 13
+#define smb_tid 28
+#define smb_pid 30
+#define smb_uid 32
+#define smb_mid 34
+#define smb_wct 36
+#define smb_vwv 37
+#define smb_vwv0 37
+#define smb_vwv1 39
+#define smb_vwv2 41
+#define smb_vwv3 43
+#define smb_vwv4 45
+#define smb_vwv5 47
+#define smb_vwv6 49
+#define smb_vwv7 51
+#define smb_vwv8 53
+#define smb_vwv9 55
+#define smb_vwv10 57
+#define smb_vwv11 59
+#define smb_vwv12 61
+#define smb_vwv13 63
+#define smb_vwv14 65
+
+/* these are the trans2 sub fields for primary requests */
+#define smb_tpscnt smb_vwv0
+#define smb_tdscnt smb_vwv1
+#define smb_mprcnt smb_vwv2
+#define smb_mdrcnt smb_vwv3
+#define smb_msrcnt smb_vwv4
+#define smb_flags smb_vwv5
+#define smb_timeout smb_vwv6
+#define smb_pscnt smb_vwv9
+#define smb_psoff smb_vwv10
+#define smb_dscnt smb_vwv11
+#define smb_dsoff smb_vwv12
+#define smb_suwcnt smb_vwv13
+#define smb_setup smb_vwv14
+#define smb_setup0 smb_setup
+#define smb_setup1 (smb_setup+2)
+#define smb_setup2 (smb_setup+4)
+
+/* these are for the secondary requests */
+#define smb_spscnt smb_vwv2
+#define smb_spsoff smb_vwv3
+#define smb_spsdisp smb_vwv4
+#define smb_sdscnt smb_vwv5
+#define smb_sdsoff smb_vwv6
+#define smb_sdsdisp smb_vwv7
+#define smb_sfid smb_vwv8
+
+/* and these for responses */
+#define smb_tprcnt smb_vwv0
+#define smb_tdrcnt smb_vwv1
+#define smb_prcnt smb_vwv3
+#define smb_proff smb_vwv4
+#define smb_prdisp smb_vwv5
+#define smb_drcnt smb_vwv6
+#define smb_droff smb_vwv7
+#define smb_drdisp smb_vwv8
+
+/* the complete */
+#define SMBmkdir      0x00   /* create directory */
+#define SMBrmdir      0x01   /* delete directory */
+#define SMBopen       0x02   /* open file */
+#define SMBcreate     0x03   /* create file */
+#define SMBclose      0x04   /* close file */
+#define SMBflush      0x05   /* flush file */
+#define SMBunlink     0x06   /* delete file */
+#define SMBmv         0x07   /* rename file */
+#define SMBgetatr     0x08   /* get file attributes */
+#define SMBsetatr     0x09   /* set file attributes */
+#define SMBread       0x0A   /* read from file */
+#define SMBwrite      0x0B   /* write to file */
+#define SMBlock       0x0C   /* lock byte range */
+#define SMBunlock     0x0D   /* unlock byte range */
+#define SMBctemp      0x0E   /* create temporary file */
+#define SMBmknew      0x0F   /* make new file */
+#define SMBchkpth     0x10   /* check directory path */
+#define SMBexit       0x11   /* process exit */
+#define SMBlseek      0x12   /* seek */
+#define SMBtcon       0x70   /* tree connect */
+#define SMBtconX      0x75   /* tree connect and X*/
+#define SMBtdis       0x71   /* tree disconnect */
+#define SMBnegprot    0x72   /* negotiate protocol */
+#define SMBdskattr    0x80   /* get disk attributes */
+#define SMBsearch     0x81   /* search directory */
+#define SMBsplopen    0xC0   /* open print spool file */
+#define SMBsplwr      0xC1   /* write to print spool file */
+#define SMBsplclose   0xC2   /* close print spool file */
+#define SMBsplretq    0xC3   /* return print queue */
+#define SMBsends      0xD0   /* send single block message */
+#define SMBsendb      0xD1   /* send broadcast message */
+#define SMBfwdname    0xD2   /* forward user name */
+#define SMBcancelf    0xD3   /* cancel forward */
+#define SMBgetmac     0xD4   /* get machine name */
+#define SMBsendstrt   0xD5   /* send start of multi-block message */
+#define SMBsendend    0xD6   /* send end of multi-block message */
+#define SMBsendtxt    0xD7   /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread	  0x13   /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* Unlock a range then write */
+#define SMBreadbraw   0x1a  /* read a block of data with no smb header */
+#define SMBwritebraw  0x1d  /* write a block of data with no smb header */
+#define SMBwritec     0x20  /* secondary write request */
+#define SMBwriteclose 0x2c  /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw      0x1A   /* read block raw */
+#define SMBreadBmpx      0x1B   /* read block multiplexed */
+#define SMBreadBs        0x1C   /* read block (secondary response) */
+#define SMBwriteBraw     0x1D   /* write block raw */
+#define SMBwriteBmpx     0x1E   /* write block multiplexed */
+#define SMBwriteBs       0x1F   /* write block (secondary request) */
+#define SMBwriteC        0x20   /* write complete response */
+#define SMBsetattrE      0x22   /* set file attributes expanded */
+#define SMBgetattrE      0x23   /* get file attributes expanded */
+#define SMBlockingX      0x24   /* lock/unlock byte ranges and X */
+#define SMBtrans         0x25   /* transaction - name, bytes in/out */
+#define SMBtranss        0x26   /* transaction (secondary request/response) */
+#define SMBioctl         0x27   /* IOCTL */
+#define SMBioctls        0x28   /* IOCTL  (secondary request/response) */
+#define SMBcopy          0x29   /* copy */
+#define SMBmove          0x2A   /* move */
+#define SMBecho          0x2B   /* echo */
+#define SMBopenX         0x2D   /* open and X */
+#define SMBreadX         0x2E   /* read and X */
+#define SMBwriteX        0x2F   /* write and X */
+#define SMBsesssetupX    0x73   /* Session Set Up & X (including User Logon) */
+#define SMBtconX         0x75   /* tree connect and X */
+#define SMBffirst        0x82   /* find first */
+#define SMBfunique       0x83   /* find unique */
+#define SMBfclose        0x84   /* find close */
+#define SMBinvalid       0xFE   /* invalid command */
+
+
+/* Extended 2.0 protocol */
+#define SMBtrans2        0x32   /* TRANS2 protocol set */
+#define SMBtranss2       0x33   /* TRANS2 protocol set, secondary command */
+#define SMBfindclose     0x34   /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose    0x35   /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX      0x74   /* user logoff */
+
+/* these are the TRANS2 sub commands */
+#define TRANSACT2_OPEN          0
+#define TRANSACT2_FINDFIRST     1
+#define TRANSACT2_FINDNEXT      2
+#define TRANSACT2_QFSINFO       3
+#define TRANSACT2_SETFSINFO     4
+#define TRANSACT2_QPATHINFO     5
+#define TRANSACT2_SETPATHINFO   6
+#define TRANSACT2_QFILEINFO     7
+#define TRANSACT2_SETFILEINFO   8
+#define TRANSACT2_FSCTL         9
+#define TRANSACT2_IOCTL           10
+#define TRANSACT2_FINDNOTIFYFIRST 11
+#define TRANSACT2_FINDNOTIFYNEXT  12
+#define TRANSACT2_MKDIR           13
+
+#endif /* _SMBNO_H_ */
diff -u --recursive --new-file v1.3.6/linux/include/linux/stats206.h linux/include/linux/stats206.h
--- v1.3.6/linux/include/linux/stats206.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/stats206.h	Sat Jul  1 19:06:00 1995
@@ -0,0 +1,34 @@
+/* stats206.h. Define constants to gather statistics on the cm206 behavior.
+   Copyright (c) 1995 David van Leeuwen.
+   
+   This is published under the Gnu Public Licence, read the header in 
+   the file cm206.c.
+*/
+
+/* This is an ugly way to guarantee that the names of the statistics
+ * are the same in the code and in the diagnostics program.  */
+
+#ifdef __KERNEL__
+#define x(a) st_ ## a
+#define y enum
+#else
+#define x(a) #a
+#define y char * stats_name[] = 
+#endif
+
+y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error),
+     x(crc_error), x(sync_error), x(lost_intr), x(echo),
+     x(write_timeout), x(receive_timeout), x(read_timeout),
+     x(dsb_timeout), x(stop_0xff), x(back_read_timeout),
+     x(sector_transferred), x(read_restarted), x(read_background),
+     x(bh), x(open), x(ioctl_multisession)
+#ifdef __KERNEL__
+     , x(last_entry)
+#endif
+ };
+
+#ifdef __KERNEL__
+#define NR_STATS st_last_entry
+#else
+#define NR_STATS sizeof(stats_name)/sizeof(char*)
+#endif
diff -u --recursive --new-file v1.3.6/linux/include/linux/timer.h linux/include/linux/timer.h
--- v1.3.6/linux/include/linux/timer.h	Sun Jan 22 21:30:17 1995
+++ linux/include/linux/timer.h	Sat Jul  1 19:06:00 1995
@@ -31,6 +31,10 @@
  *
  * MCD_TIMER		Mitsumi CD-ROM Timer
  *
+ * GSCD_TIMER		Goldstar CD-ROM Timer
+ *
+ * OPTCD_TIMER		Optics Storage CD-ROM Timer
+ *
  */
 
 #define BLANK_TIMER	0
@@ -48,6 +52,8 @@
 #define MCD_TIMER	23
 
 #define HD_TIMER2	24
+#define GSCD_TIMER	25
+#define OPTCD_TIMER	26
 
 struct timer_struct {
 	unsigned long expires;
diff -u --recursive --new-file v1.3.6/linux/include/net/arp.h linux/include/net/arp.h
--- v1.3.6/linux/include/net/arp.h	Tue Jun  6 11:22:17 1995
+++ linux/include/net/arp.h	Thu Jul  6 13:22:05 1995
@@ -3,18 +3,19 @@
 #define _ARP_H
 
 extern void	arp_init(void);
-extern void	arp_destroy(unsigned long paddr, int force);
+extern void	arp_destroy(u32 paddr, int force);
 extern void	arp_device_down(struct device *dev);
 extern int	arp_rcv(struct sk_buff *skb, struct device *dev,
 			struct packet_type *pt);
-extern int	arp_find(unsigned char *haddr, unsigned long paddr,
-		struct device *dev, unsigned long saddr, struct sk_buff *skb);
+extern int	arp_query(unsigned char *haddr, u32 paddr, unsigned short type);
+extern int	arp_find(unsigned char *haddr, u32 paddr,
+		struct device *dev, u32 saddr, struct sk_buff *skb);
 extern int	arp_get_info(char *buffer, char **start, off_t origin, int length);
 extern int	arp_ioctl(unsigned int cmd, void *arg);
-extern void     arp_send(int type, int ptype, unsigned long dest_ip, 
-			 struct device *dev, unsigned long src_ip, 
+extern void     arp_send(int type, int ptype, u32 dest_ip, 
+			 struct device *dev, u32 src_ip, 
 			 unsigned char *dest_hw, unsigned char *src_hw);
-extern int	arp_find_cache(unsigned char *dp, unsigned long daddr, struct device *dev);
+extern int	arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev);
 
 extern unsigned long	arp_cache_stamp;
 #endif	/* _ARP_H */
diff -u --recursive --new-file v1.3.6/linux/include/net/atalk.h linux/include/net/atalk.h
--- v1.3.6/linux/include/net/atalk.h	Tue Jun  6 11:22:17 1995
+++ linux/include/net/atalk.h	Thu Jan  1 02:00:00 1970
@@ -1,144 +0,0 @@
-/*
- *	Appletalk networking structures
- *
- *	The following are directly referenced from the University Of Michigan
- *	netatalk for compatibility reasons.
- */
-
-#ifndef __LINUX_ATALK_H__
-#define __LINUX_ATALK_H__
-
-#define ATPORT_FIRST	1
-#define ATPORT_RESERVED	128
-#define ATPORT_LAST	255
-#define ATADDR_ANYNET	(__u16)0
-#define ATADDR_ANYNODE	(__u8)0
-#define ATADDR_ANYPORT  (__u8)0
-#define ATADDR_BCAST	(__u8)255
-#define DDP_MAXSZ	587
-
-struct at_addr 
-{
-	__u16	s_net;
-	__u8	s_node;
-};
-
-struct sockaddr_at 
-{
-	short		sat_family;
-	__u8		sat_port;
-	struct at_addr	sat_addr;
-	char		sat_zero[ 8 ];
-};
-
-struct netrange 
-{
-	__u8	nr_phase;
-	__u16	nr_firstnet;
-	__u16	nr_lastnet;
-};
-
-struct atalk_route
-{
-	struct device *dev;
-	struct at_addr target;
-	struct at_addr gateway;
-	int flags;
-	struct atalk_route *next;
-};
-
-struct atalk_iface
-{
-	struct device *dev;
-	struct at_addr address;		/* Our address */
-	int status;			/* What are we doing ?? */
-#define ATIF_PROBE	1		/* Probing for an address */
-#define ATIF_PROBE_FAIL	2		/* Probe collided */
-	struct netrange nets;		/* Associated direct netrange */
-	struct atalk_iface *next;
-};
-	
-struct atalk_sock
-{
-	unsigned short dest_net;
-	unsigned short src_net;
-	unsigned char dest_node;
-	unsigned char src_node;
-	unsigned char dest_port;
-	unsigned char src_port;
-};
-
-#define DDP_MAXHOPS	15	/* 4 bits of hop counter */
-
-#ifdef __KERNEL__
-
-struct ddpehdr
-{
-	/* FIXME for bigendians */
-	/*__u16	deh_pad:2,deh_hops:4,deh_len:10;*/
-	__u16	deh_len:10,deh_hops:4,deh_pad:2;
-	__u16	deh_sum;
-	__u16	deh_dnet;
-	__u16	deh_snet;
-	__u8	deh_dnode;
-	__u8	deh_snode;
-	__u8	deh_dport;
-	__u8	deh_sport;
-	/* And netatalk apps expect to stick the type in themselves */
-};
-
-/*
- *	Unused (and currently unsupported)
- */
- 
-struct ddpshdr
-{
-	/* FIXME for bigendians */
-	__u8	dsh_sport;
-	__u8	dsh_dport;
-	__u16	dsh_len:10, dsh_pad:6;
-	/* And netatalk apps expect to stick the type in themselves */
-};
-
-/* Appletalk AARP headers */
-
-struct elapaarp
-{
-	__u16	hw_type;
-#define AARP_HW_TYPE_ETHERNET		1
-#define AARP_HW_TYPE_TOKENRING		2
-	__u16	pa_type;
-	__u8	hw_len;
-	__u8	pa_len;
-#define AARP_PA_ALEN			4
-	__u16	function;
-#define AARP_REQUEST			1
-#define AARP_REPLY			2
-#define AARP_PROBE			3
-	__u8	hw_src[ETH_ALEN]	__attribute__ ((packed));
-	__u8	pa_src_zero		__attribute__ ((packed));
-	__u16	pa_src_net		__attribute__ ((packed));
-	__u8	pa_src_node		__attribute__ ((packed));
-	__u8	hw_dst[ETH_ALEN]	__attribute__ ((packed));
-	__u8	pa_dst_zero		__attribute__ ((packed));
-	__u16	pa_dst_net		__attribute__ ((packed));
-	__u8	pa_dst_node		__attribute__ ((packed));	
-};
-
-typedef struct sock	atalk_socket;
-
-#define AARP_EXPIRY_TIME	(5*60*HZ)	/* Not specified - how long till we drop a resolved entry */
-#define AARP_HASH_SIZE		16		/* Size of hash table */
-#define AARP_TICK_TIME		(HZ/5)		/* Fast retransmission timer when resolving */
-#define AARP_RETRANSMIT_LIMIT	10		/* Send 10 requests then give up (2 seconds) */
-#define AARP_RESOLVE_TIME	(10*HZ)		/* Some value bigger than total retransmit time + a bit for last reply to appear and to stop continual requests */
-
-extern struct datalink_proto *ddp_dl, *aarp_dl;
-extern void aarp_proto_init(void);
-/* Inter module exports */
-extern struct atalk_iface *atalk_find_dev(struct device *dev);
-extern struct at_addr *atalk_find_dev_addr(struct device *dev);
-extern int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, void *hwaddr);
-extern void aarp_send_probe(struct device *dev, struct at_addr *addr);
-#endif
-#endif
diff -u --recursive --new-file v1.3.6/linux/include/net/ax25.h linux/include/net/ax25.h
--- v1.3.6/linux/include/net/ax25.h	Fri Jun 30 16:22:30 1995
+++ linux/include/net/ax25.h	Wed Jul  5 13:06:27 1995
@@ -7,6 +7,13 @@
 #ifndef _AX25_H
 #define _AX25_H 
 #include <linux/ax25.h>
+
+#define	AX25_BPQ_HEADER_LEN	16
+#define	AX25_KISS_HEADER_LEN	1
+
+#define	AX25_MAX_HEADER_LEN	56
+#define	AX25_HEADER_LEN		17
+#define	AX25_ADDR_LEN		7
  
 #define AX25_P_IP	0xCC
 #define AX25_P_ARP	0xCD
@@ -126,11 +133,10 @@
 	struct sock		*sk;		/* Backlink to socket */
 } ax25_cb;
 
-/* ax25.c */
+/* af_ax25.c */
 extern char *ax2asc(ax25_address *);
 extern int  ax25cmp(ax25_address *, ax25_address *);
-extern int  ax25_send_frame(struct sk_buff *, ax25_address *, ax25_address *, struct device *);
-extern int  ax25_rcv(struct sk_buff *,struct device *,struct packet_type *);
+extern int  ax25_send_frame(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *, struct device *);
 extern void ax25_destroy_socket(ax25_cb *);
 extern struct device *ax25rtr_get_dev(ax25_address *);
 extern int  ax25_encapsulate(struct sk_buff *, struct device *, unsigned short,
@@ -140,6 +146,7 @@
 extern ax25_uid_assoc *ax25_uid_list;
 extern int  ax25_uid_policy;
 extern ax25_address *ax25_findbyuid(uid_t);
+extern void ax25_queue_xmit(struct sk_buff *, struct device *, int);
 
 #include "ax25call.h"
 
@@ -184,5 +191,8 @@
 
 /* ax25_timer */
 extern void ax25_set_timer(ax25_cb *);
+
+/* slip.c */
+extern int  sl_get_ax25_mode(struct device *);
 
 #endif
diff -u --recursive --new-file v1.3.6/linux/include/net/netrom.h linux/include/net/netrom.h
--- v1.3.6/linux/include/net/netrom.h	Fri Jun 30 16:22:30 1995
+++ linux/include/net/netrom.h	Wed Jul  5 13:06:27 1995
@@ -7,6 +7,9 @@
 #ifndef _NETROM_H
 #define _NETROM_H 
 #include <linux/netrom.h>
+
+#define	NR_NETWORK_LEN		15
+#define	NR_TRANSPORT_LEN	5
  
 #define	NR_PROTO_IP		0x0C
 
@@ -72,6 +75,7 @@
 struct nr_neigh {
 	struct nr_neigh *next;
 	ax25_address    callsign;
+	ax25_digi       *digipeat;
 	struct device   *dev;
 	unsigned char   quality;
 	unsigned char   locked;
@@ -79,7 +83,7 @@
 	unsigned short  number;
 };
 
-/* netrom.c */
+/* af_netrom.c */
 extern struct nr_parms_struct nr_default;
 extern int  nr_rx_frame(struct sk_buff *, struct device *);
 extern void nr_destroy_socket(struct sock *);
@@ -110,7 +114,7 @@
 extern struct device *nr_dev_get(ax25_address *);
 extern int  nr_rt_ioctl(unsigned int, void *);
 extern void nr_link_failed(ax25_address *, struct device *);
-extern int  nr_route_frame(struct sk_buff *, struct device *);
+extern int  nr_route_frame(struct sk_buff *, ax25_cb *);
 extern int  nr_nodes_get_info(char *, char **, off_t, int);
 extern int  nr_neigh_get_info(char *, char **, off_t, int);
 
diff -u --recursive --new-file v1.3.6/linux/include/net/route.h linux/include/net/route.h
--- v1.3.6/linux/include/net/route.h	Fri Jun 30 16:22:30 1995
+++ linux/include/net/route.h	Fri Jun 30 16:46:17 1995
@@ -32,9 +32,9 @@
 	unsigned long		rt_dst;
 	unsigned long		rt_mask;
 	unsigned long		rt_gateway;
-	unsigned char		rt_flags;
-	unsigned char		rt_metric;
-	short			rt_refcnt;
+	unsigned short		rt_flags;
+	unsigned short		rt_metric;
+	unsigned int		rt_refcnt;
 	unsigned long		rt_use;
 	unsigned short		rt_mss;
 	unsigned short		rt_irtt;
diff -u --recursive --new-file v1.3.6/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.6/linux/include/net/sock.h	Fri Jun 30 16:22:30 1995
+++ linux/include/net/sock.h	Wed Jul  5 13:06:27 1995
@@ -48,7 +48,7 @@
 #include "ipx.h"
 #endif
 #ifdef CONFIG_ATALK
-#include "atalk.h"
+#include <linux/atalk.h>
 #endif
 
 #include <linux/igmp.h>
diff -u --recursive --new-file v1.3.6/linux/include/net/tcp.h linux/include/net/tcp.h
--- v1.3.6/linux/include/net/tcp.h	Tue Jun 27 14:11:47 1995
+++ linux/include/net/tcp.h	Thu Jul  6 14:04:43 1995
@@ -20,10 +20,10 @@
 
 #include <linux/tcp.h>
 
-#define MAX_SYN_SIZE	44 + MAX_HEADER
-#define MAX_FIN_SIZE	40 + MAX_HEADER
-#define MAX_ACK_SIZE	40 + MAX_HEADER
-#define MAX_RESET_SIZE	40 + MAX_HEADER
+#define MAX_SYN_SIZE	44 + MAX_HEADER + 15
+#define MAX_FIN_SIZE	40 + MAX_HEADER + 15
+#define MAX_ACK_SIZE	40 + MAX_HEADER + 15
+#define MAX_RESET_SIZE	40 + MAX_HEADER + 15
 #define MAX_WINDOW	16384
 #define MIN_WINDOW	2048
 #define MAX_ACK_BACKLOG	2
diff -u --recursive --new-file v1.3.6/linux/init/main.c linux/init/main.c
--- v1.3.6/linux/init/main.c	Tue Jun 27 14:11:47 1995
+++ linux/init/main.c	Sat Jul  1 19:06:00 1995
@@ -54,8 +54,6 @@
 extern void eth_setup(char *str, int *ints);
 extern void xd_setup(char *str, int *ints);
 extern void floppy_setup(char *str, int *ints);
-extern void mcd_setup(char *str, int *ints);
-extern void aztcd_setup(char *str, int *ints);
 extern void st_setup(char *str, int *ints);
 extern void st0x_setup(char *str, int *ints);
 extern void tmc8xx_setup(char *str, int *ints);
@@ -68,15 +66,36 @@
 extern void buslogic_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
-#ifdef CONFIG_SBPCD
-extern void sbpcd_setup(char *str, int *ints);
-#endif CONFIG_SBPCD
 #ifdef CONFIG_CDU31A
 extern void cdu31a_setup(char *str, int *ints);
 #endif CONFIG_CDU31A
+#ifdef CONFIG_MCD
+extern void mcd_setup(char *str, int *ints);
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+extern void mcdx_setup(char *str, int *ints);
+#endif CONFIG_MCDX
+#ifdef CONFIG_SBPCD
+extern void sbpcd_setup(char *str, int *ints);
+#endif CONFIG_SBPCD
+#ifdef CONFIG_AZTCD
+extern void aztcd_setup(char *str, int *ints);
+#endif CONFIG_AZTCD
 #ifdef CONFIG_CDU535
 extern void sonycd535_setup(char *str, int *ints);
 #endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+extern void gscd_setup(char *str, int *ints);
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+extern void cm206_setup(char *str, int *ints);
+#endif CONFIG_CM206
+#ifdef CONFIG_OPTCD
+extern void optcd_setup(char *str, int *ints);
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+extern void sjcd_setup(char *str, int *ints);
+#endif CONFIG_SJCD
 void ramdisk_setup(char *str, int *ints);
 
 #ifdef CONFIG_SYSVIPC
@@ -187,24 +206,39 @@
 #ifdef CONFIG_BLK_DEV_FD
 	{ "floppy=", floppy_setup },
 #endif
+#ifdef CONFIG_CDU31A
+	{ "cdu31a=", cdu31a_setup },
+#endif CONFIG_CDU31A
 #ifdef CONFIG_MCD
 	{ "mcd=", mcd_setup },
-#endif
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+	{ "mcdx=", mcdx_setup },
+#endif CONFIG_MCDX
+#ifdef CONFIG_SBPCD
+	{ "sbpcd=", sbpcd_setup },
+#endif CONFIG_SBPCD
 #ifdef CONFIG_AZTCD
 	{ "aztcd=", aztcd_setup },
-#endif
+#endif CONFIG_AZTCD
 #ifdef CONFIG_CDU535
 	{ "sonycd535=", sonycd535_setup },
 #endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+	{ "gscd=", gscd_setup },
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+	{ "cm206=", cm206_setup },
+#endif CONFIG_CM206
+#ifdef CONFIG_OPTCD
+	{ "optcd=", optcd_setup },
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+	{ "sjcd=", sjcd_setup },
+#endif CONFIG_SJCD
 #ifdef CONFIG_SOUND
 	{ "sound=", sound_setup },
 #endif
-#ifdef CONFIG_SBPCD
-	{ "sbpcd=", sbpcd_setup },
-#endif CONFIG_SBPCD
-#ifdef CONFIG_CDU31A
-	{ "cdu31a=", cdu31a_setup },
-#endif CONFIG_CDU31A
 	{ 0, 0 }
 };
 
diff -u --recursive --new-file v1.3.6/linux/kernel/Makefile linux/kernel/Makefile
--- v1.3.6/linux/kernel/Makefile	Mon Jan 23 10:38:30 1995
+++ linux/kernel/Makefile	Tue Jul  4 07:59:54 1995
@@ -26,7 +26,7 @@
 
 include ../versions.mk
 
-kernel.o: $(SYMTAB_OBJS) $(OBJS)
+kernel.o: $(SYMTAB_OBJS:.o=.ver) $(SYMTAB_OBJS) $(OBJS)
 	$(LD) -r -o kernel.o $(SYMTAB_OBJS) $(OBJS)
 	sync
 
diff -u --recursive --new-file v1.3.6/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.6/linux/kernel/ksyms.c	Fri Jun 30 16:22:30 1995
+++ linux/kernel/ksyms.c	Tue Jul  4 07:59:54 1995
@@ -57,6 +57,10 @@
 #include <linux/msdos_fs.h>
 #endif
 
+#if defined(CONFIG_PROC_FS)
+#include <linux/proc_fs.h>
+#endif
+
 #include <asm/irq.h>
 extern char *get_options(char *str, int *ints);
 extern void set_device_ro(int dev,int flag);
@@ -80,6 +84,11 @@
 				off_t offset, int length, 
 				int inode, int func) = 0; /* Dirty hack */
 
+#if defined(CONFIG_PROC_FS)
+extern struct proc_dir_entry scsi_dir[]; 
+extern struct proc_dir_entry scsi_hba_dir[];
+#endif
+
 extern int sys_tz;
 extern int request_dma(unsigned int dmanr, char * deviceID);
 extern void free_dma(unsigned int dmanr);
@@ -222,6 +231,7 @@
 	X(disable_irq),
 	X(bh_active),
 	X(bh_mask),
+	X(bh_base),
 	X(add_timer),
 	X(del_timer),
 	X(tq_timer),
@@ -375,6 +385,23 @@
  	X(mem_map),
     	X(print_msg),
 	X(print_status),
+	X(gendisk_head), /* Needed for sd.c */
+	X(resetup_one_dev), /* Needed for sd.c */
+#else
+	/*
+	 * With no scsi configured, we still need to export a few
+	 * symbols so that scsi can be loaded later via insmod.
+	 */
+ 	X(intr_count),
+ 	X(mem_map),
+	X(gendisk_head),
+	X(resetup_one_dev),
+
+#if defined(CONFIG_PROC_FS)
+	X(scsi_dir),
+	X(scsi_hba_dir),
+#endif
+	X(dispatch_scsi_info_ptr),
 #endif
 	/* Added to make file system as module */
 	X(set_writetime),
diff -u --recursive --new-file v1.3.6/linux/kernel/sys.c linux/kernel/sys.c
--- v1.3.6/linux/kernel/sys.c	Fri Jun 30 16:22:30 1995
+++ linux/kernel/sys.c	Thu Jul  6 13:24:46 1995
@@ -195,6 +195,7 @@
 asmlinkage int sys_setregid(gid_t rgid, gid_t egid)
 {
 	int old_rgid = current->gid;
+	int old_egid = current->egid;
 
 	if (rgid != (gid_t) -1) {
 		if ((old_rgid == rgid) ||
@@ -219,6 +220,8 @@
 	    (egid != (gid_t) -1 && egid != old_rgid))
 		current->sgid = current->egid;
 	current->fsgid = current->egid;
+	if (current->egid != old_egid)
+		current->dumpable = 0;
 	return 0;
 }
 
@@ -227,12 +230,16 @@
  */
 asmlinkage int sys_setgid(gid_t gid)
 {
+	int old_egid = current->egid;
+
 	if (suser())
 		current->gid = current->egid = current->sgid = current->fsgid = gid;
 	else if ((gid == current->gid) || (gid == current->sgid))
 		current->egid = current->fsgid = gid;
 	else
 		return -EPERM;
+	if (current->egid != old_egid)
+		current->dumpable = 0;
 	return 0;
 }
 
@@ -284,6 +291,7 @@
 asmlinkage int sys_setreuid(uid_t ruid, uid_t euid)
 {
 	int old_ruid = current->uid;
+	int old_euid = current->euid;
 
 	if (ruid != (uid_t) -1) {
 		if ((old_ruid == ruid) || 
@@ -308,6 +316,8 @@
 	    (euid != (uid_t) -1 && euid != old_ruid))
 		current->suid = current->euid;
 	current->fsuid = current->euid;
+	if (current->euid != old_euid)
+		current->dumpable = 0;
 	return 0;
 }
 
@@ -324,12 +334,16 @@
  */
 asmlinkage int sys_setuid(uid_t uid)
 {
+	int old_euid = current->euid;
+
 	if (suser())
 		current->uid = current->euid = current->suid = current->fsuid = uid;
 	else if ((uid == current->uid) || (uid == current->suid))
 		current->fsuid = current->euid = uid;
 	else
 		return -EPERM;
+	if (current->euid != old_euid)
+		current->dumpable = 0;
 	return(0);
 }
 
@@ -346,6 +360,8 @@
 	if (uid == current->uid || uid == current->euid ||
 	    uid == current->suid || uid == current->fsuid || suser())
 		current->fsuid = uid;
+	if (current->fsuid != old_fsuid)
+		current->dumpable = 0;
 	return old_fsuid;
 }
 
@@ -359,6 +375,8 @@
 	if (gid == current->gid || gid == current->egid ||
 	    gid == current->sgid || gid == current->fsgid || suser())
 		current->fsgid = gid;
+	if (current->fsgid != old_fsgid)
+		current->dumpable = 0;
 	return old_fsgid;
 }
 
diff -u --recursive --new-file v1.3.6/linux/net/802/p8022.c linux/net/802/p8022.c
--- v1.3.6/linux/net/802/p8022.c	Fri Jun 30 16:22:30 1995
+++ linux/net/802/p8022.c	Wed Jul  5 13:06:27 1995
@@ -6,6 +6,13 @@
 
 static struct datalink_proto *p8022_list = NULL;
 
+/*
+ *	We don't handle the loopback SAP stuff, the extended
+ *	802.2 command set, multicast SAP identifiers and non UI
+ *	frames. We have the absolute minimum needed for IPX,
+ *	IP and Appletalk phase 2.
+ */
+ 
 static struct datalink_proto *
 find_8022_client(unsigned char type)
 {
diff -u --recursive --new-file v1.3.6/linux/net/802/tr.c linux/net/802/tr.c
--- v1.3.6/linux/net/802/tr.c	Fri Jun 30 16:22:30 1995
+++ linux/net/802/tr.c	Wed Jul  5 13:06:27 1995
@@ -92,6 +92,8 @@
 	struct trh_hdr *trh=(struct trh_hdr *)skb->data;
 	struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
 	
+	skb_pull(skb,dev->hard_header_len);
+	
 	if(trh->saddr[0] & TR_RII)
 		tr_add_rif_info(trh);
 
diff -u --recursive --new-file v1.3.6/linux/net/Changes linux/net/Changes
--- v1.3.6/linux/net/Changes	Fri Jun 30 16:22:30 1995
+++ linux/net/Changes	Wed Jul  5 13:06:27 1995
@@ -100,25 +100,33 @@
 
 ------->>>>>  NET3 030  <<<<<----------
 
+o	Long word align ethernet IP headers (64byte align for pentium) [TESTED]
+		(less helpful than I'd have liked)
+o	Fixed variable length header support to really work	[TESTED]
+o	Mend appletalk/ipx partially			[IN]
+o	Start playing with input checksum & copy	[IN]
+o	Fixed PPP and other oddments			[IN]
+o	Mended IPIP					[Might work ;)]
+
+------->>>>>    1.3.7   <<<<<----------
+
 o	Finish merging the bridge code
 o	Device locking
-o	Faster ip_csum
 o	SIOCSLEEPRT patch
 o	Options support in ip_build_xmit		[PENDING]
 o	Fast checksum/copy on outgoing TCP
-o	Long word align ethernet IP headers (64byte align for pentium) [TESTING - EMAIL FOR INFO]
 o	Explode/implode headers for alpha,mips etc.
 o	Fast dev_grab_next() transmit reload function
 	and dev_push_failed() ??
-o	Faster ip_forward
-o	Faster loopback frame forwarding.
+o	Faster ip_forward				[PENDING]
 o	Forwarding queue control (+ fairness algorithms ??)
 o	IP forward flow control.
 o	Infinite PPP/SLIP devices.
-o	PI2 card doesn't do AX.25 VC yet
+o	PI2 card doesn't do AX.25 VC yet		[PENDING]
 o	AX.25 set protocol type
 o	Clean up RAW AX.25 sockets.
 o	Finish 802.2 Class I code to be compliant to the oddities of 802.2
+o	Full variable length AX.25 support		[JSN doing]
 
 0.2
 ---
diff -u --recursive --new-file v1.3.6/linux/net/appletalk/aarp.c linux/net/appletalk/aarp.c
--- v1.3.6/linux/net/appletalk/aarp.c	Fri Jun 30 16:22:30 1995
+++ linux/net/appletalk/aarp.c	Wed Jul  5 14:13:04 1995
@@ -46,7 +46,7 @@
 #include <net/sock.h>
 #include <net/datalink.h>
 #include <net/psnap.h>
-#include <net/atalk.h>
+#include <linux/atalk.h>
 
 #ifdef CONFIG_ATALK
 /*
@@ -103,7 +103,7 @@
 	struct device *dev=a->dev;
 	int len=dev->hard_header_len+sizeof(struct elapaarp)+aarp_dl->header_length;
 	struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC);
-	struct elapaarp *eah=(struct elapaarp *)(skb->data+dev->hard_header_len+aarp_dl->header_length);
+	struct elapaarp *eah;
 	struct at_addr *sat=atalk_find_dev_addr(dev);
 	
 	if(skb==NULL || sat==NULL)
@@ -112,10 +112,11 @@
 	/*
 	 *	Set up the buffer.
 	 */		
-	 
+
+	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
+	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
 	skb->arp	=	1;
 	skb->free	=	1;
-	skb_put(skb,len);
 	skb->dev	=	a->dev;
 	
 	/*
@@ -164,7 +165,7 @@
 {
 	int len=dev->hard_header_len+sizeof(struct elapaarp)+aarp_dl->header_length;
 	struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC);
-	struct elapaarp *eah=(struct elapaarp *)(skb->data+dev->hard_header_len+aarp_dl->header_length);
+	struct elapaarp *eah;
 	
 	if(skb==NULL)
 		return;
@@ -172,7 +173,9 @@
 	/*
 	 *	Set up the buffer.
 	 */		
-	 
+
+	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
+	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));	 
 	skb->arp	=	1;
 	skb->free	=	1;
 	skb_put(skb,len);
@@ -225,7 +228,7 @@
 {
 	int len=dev->hard_header_len+sizeof(struct elapaarp)+aarp_dl->header_length;
 	struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC);
-	struct elapaarp *eah=(struct elapaarp *)(skb->data+dev->hard_header_len+aarp_dl->header_length);
+	struct elapaarp *eah;
 	static char aarp_eth_multicast[ETH_ALEN]={ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
 	
 	if(skb==NULL)
@@ -234,7 +237,10 @@
 	/*
 	 *	Set up the buffer.
 	 */		
-	 
+
+	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
+	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
+	
 	skb->arp	=	1;
 	skb->free	=	1;
 	skb_put(skb,len);
@@ -575,8 +581,6 @@
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-	
-	skb_pull(skb,dev->hard_header_len);
 	
 	/*
 	 *	Frame size ok ?
diff -u --recursive --new-file v1.3.6/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.6/linux/net/appletalk/ddp.c	Fri Jun 30 16:22:30 1995
+++ linux/net/appletalk/ddp.c	Wed Jul  5 13:06:27 1995
@@ -46,7 +46,7 @@
 #include <net/p8022.h>
 #include <net/psnap.h>
 #include <net/sock.h>
-#include <net/atalk.h>
+#include <linux/atalk.h>
 
 #ifdef CONFIG_ATALK
 
@@ -1334,10 +1334,6 @@
 	struct sockaddr_at tosat;
         int origlen;
 	
-	/* First strip the MAC header */
-	
-	skb_pull(skb,dev->hard_header_len);
-	
 	/* Size check */
 	if(skb->len<sizeof(*ddp))
 	{
@@ -1549,16 +1545,13 @@
 	skb->arp=1;
 	skb_reserve(skb,ddp_dl->header_length);
 	skb_reserve(skb,dev->hard_header_len);
-	skb_put(skb,size);
 
 	skb->dev=dev;
 	
 	if(sk->debug)
 		printk("SK %p: Begin build.\n", sk);
 	
-	skb->h.raw=skb->data;	
-	
-	ddp=(struct ddpehdr *)skb->h.raw;
+	ddp=(struct ddpehdr *)skb_put(skb,sizeof(struct ddpehdr));
 	ddp->deh_pad=0;
 	ddp->deh_hops=0;
 	ddp->deh_len=len+sizeof(*ddp);
@@ -1579,7 +1572,7 @@
 	if(sk->debug)
 		printk("SK %p: Copy user data (%d bytes).\n", sk, len);
 		
-	memcpy_fromfs((char *)(ddp+1),ubuf,len);
+	memcpy_fromfs(skb_put(skb,len),ubuf,len);
 
 	if(sk->no_check==1)
 		ddp->deh_sum=0;
@@ -1616,6 +1609,7 @@
 		sk->wmem_alloc-=skb->truesize;
 		ddp_dl->datalink_header(ddp_dl, skb, dev->dev_addr);
 		skb->sk = NULL;
+		skb->mac.raw=skb->data;
 		skb->h.raw = skb->data + ddp_dl->header_length + dev->hard_header_len;
 		skb_pull(skb,dev->hard_header_len);
 		skb_pull(skb,ddp_dl->header_length);
diff -u --recursive --new-file v1.3.6/linux/net/ax25/README.AX25 linux/net/ax25/README.AX25
--- v1.3.6/linux/net/ax25/README.AX25	Tue Jun  6 11:22:15 1995
+++ linux/net/ax25/README.AX25	Wed Jul  5 13:06:27 1995
@@ -1,20 +1,50 @@
-This is a working version of the new state machine code for AX25 under
-Linux. It is closely based on the SDL diagrams published in the ARRL 7th
-Computer Networking Conference papers, and they should be referred to when
-reading the code, notably the stuff in ax25_in.c. The next stage is to
-separate the ax25 control block from the socket and then add NET/ROM and
-connected mode IP. I would also like to add the extended AX25 designed by a
-Dutch station which allows for window sizes up to 127.
-
-This code will work the same as the old code, although the display in
-/proc/net/ax25 is a little different, but should be understandable. Please
-give this code a work out and report any bugs to me either at
-jsn@cs.nott.ac.uk or at GB7DAD.GBR.EU.
-
-This code has taught me a lot about the internals of the networking side of
-Linux especially skbuff handling and I now feel happy about implementing the
-higher level protocols.
+This is version 029 of the new AX.25 and NET/ROM code for Linux. It
+incorporates many enhancements since the last release, notably the rewriting
+of the connected mode IP code and the IP over NET/ROM code. The opportunity
+has been taken to add the G8BPQ NET/ROM extensions and to add BPQ Ethernet
+support. The latter has been much eased by the use of the new variable
+length header code by Alan Cox.
 
-73's
+To use the BPQ Ethernet option, first up the ethernet interface in the usual
+manner, the IP address of the interface is not that important but it will
+be required for the ARP table. Next create an ARP entry in the ARP table of
+type ax25 for the interface binding it to an AX.25 callsign, this callsign
+will be the callsign of that interface. By default BPQ Ethernet uses a
+multi-cast address, this implementation does not, instead the standard
+ethernet broadcast address is used. Therefore the NET.CFG file for the
+ODI driver should look similar to this:
 
-Jonathan
+------------------------------ cut here ------------------------------------
+
+LINK SUPPORT
+
+	MAX STACKS 1
+	MAX BOARDS 1
+
+LINK DRIVER E2000			; or other MLID to suit your card
+
+	INT 10				;
+	PORT 300			; to suit your card
+
+	FRAME ETHERNET_II
+
+	PROTOCOL BPQ 8FF ETHERNET_II	; required for BPQ - can change PID
+
+BPQPARMS				; optional - only needed if you want
+					; to override the default target addr
+
+	ETH_ADDR  FF:FF:FF:FF:FF:FF	; Target address
+
+----------------------------- cut here -------------------------------------
+
+The above configuration assumes that only BPQ Ethernet is being used.
+
+It is not possible to run IP over AX.25 on the BPQ Ethernet port. To simply
+route IP frames to (say) eth0 would create standard ethernet IP frames and
+completely bypass the AX.25 code. However it is possible to use IP over
+NET/ROM across a BPQ Ethernet link, the performance of such a system is
+very acceptable indeed.
+
+Jonathan Naylor G4KLX
+
+g4klx@amsat.org
diff -u --recursive --new-file v1.3.6/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.6/linux/net/ax25/af_ax25.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ax25/af_ax25.c	Wed Jul  5 13:06:28 1995
@@ -56,7 +56,9 @@
  *			Jonathan(G4KLX)		and removed all the old Berkeley, added IP mode registration.
  *			Darryl(G7LED)		stuff. Cross-port digipeating. Minor fixes and enhancements.
  *			Alan(GW4PTS)		Missed suser() on axassociate checks
- *	AX.25 030	Alan(GW4PTS)		Switched to new buffers. Not tested netrom/new buffers yet.
+ *	AX.25 030	Alan(GW4PTS)		Added variable length headers.
+ *			Jonathan(G4KLX)		Added BPQ Ethernet interface.
+ *			Steven(GW7RRM)
  *
  *	To do:
  *		Support use as digipeater, including an on/off ioctl
@@ -534,15 +536,14 @@
 	return ax25;
 }
 
-int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest, struct device *dev)
+int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest,
+	ax25_digi *digi, struct device *dev)
 {
 	ax25_cb *ax25;
 
 	if (skb == NULL)
 		return 0;
 
-	skb->h.raw = skb->data + 15;
-
 	/*
 	 * Look for an existing connection.
 	 */
@@ -564,6 +565,14 @@
 	memcpy(&ax25->source_addr, src,  sizeof(ax25_address));
 	memcpy(&ax25->dest_addr,   dest, sizeof(ax25_address));
 
+	if (digi != NULL) {
+		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+			kfree_s(ax25, sizeof(ax25));
+			return 0;
+		}
+		memcpy(ax25->digipeat, digi, sizeof(ax25_digi));
+	}
+
 	ax25_establish_data_link(ax25);
 	ax25_insert_socket(ax25);
 
@@ -585,11 +594,23 @@
 struct device *ax25rtr_get_dev(ax25_address *addr)
 {
 	struct device *dev;
+	ax25_address dev_addr;
 	
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) { /* Active kiss ax25 mode */ 
-			if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
-				return dev;
+		if (dev->flags & IFF_UP) {
+			switch (dev->type) {
+				case ARPHRD_AX25: /* Active kiss ax25 mode */ 
+					if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
+						return dev;
+					break;
+				case ARPHRD_ETHER:
+					if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, ARPHRD_AX25))
+						if (ax25cmp(addr, &dev_addr) == 0)
+							return dev;
+					break;
+				default:
+					break;
+			}
 		}
 	}
 
@@ -632,14 +653,14 @@
 		return err;
 
 	opt = get_fs_long((unsigned long *)optval);
-	
+
 	switch (optname) {
 		case AX25_WINDOW:
 			if (opt < 1 || opt > 7)
 				return -EINVAL;
 			sk->ax25->window = opt;
 			return 0;
-			
+
 		case AX25_T1:
 			if (opt < 1)
 				return -EINVAL;
@@ -651,23 +672,23 @@
 				return -EINVAL;
 			sk->ax25->t2 = opt * PR_SLOWHZ;
 			return 0;
-			
+
 		case AX25_N2:
 			if (opt < 1 || opt > 31)
 				return -EINVAL;
 			sk->ax25->n2 = opt;
 			return 0;
-			
+
 		case AX25_T3:
 			if (opt < 1)
 				return -EINVAL;
 			sk->ax25->t3 = opt * PR_SLOWHZ;
 			return 0;
-	
+
 		case AX25_BACKOFF:
 			sk->ax25->backoff = opt ? 1 : 0;
 			return 0;
-	
+
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -687,7 +708,7 @@
 	
 	if (level != SOL_AX25)
 		return -EOPNOTSUPP;
-	
+
 	switch (optname) {
 		case AX25_WINDOW:
 			val = sk->ax25->window;
@@ -696,23 +717,23 @@
 		case AX25_T1:
 			val = sk->ax25->t1 / PR_SLOWHZ;
 			break;
-			
+
 		case AX25_T2:
 			val = sk->ax25->t2 / PR_SLOWHZ;
 			break;
-			
+
 		case AX25_N2:
 			val = sk->ax25->n2;
 			break;
-						
+
 		case AX25_T3:
 			val = sk->ax25->t3 / PR_SLOWHZ;
 			break;
-	
+
 		case AX25_BACKOFF:
 			val = sk->ax25->backoff;
 			break;
-	
+
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -934,7 +955,7 @@
 				break;
 
 			case AX25_STATE_1:
-				ax25_send_control(sk->ax25, DISC | PF, C_RESPONSE);
+				ax25_send_control(sk->ax25, DISC | PF, C_COMMAND);
 				sk->ax25->state = AX25_STATE_0;
 				sk->dead        = 1;
 				sk->state_change(sk);
@@ -1238,9 +1259,8 @@
 	return 0;
 }
  
-int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype)
 {
-	unsigned char *data;
 	struct sock *make;
 	struct sock *sk;
 	int type = 0;
@@ -1249,30 +1269,19 @@
 	ax25_address src, dest;
 	struct sock *raw;
 	int mine = 0;
-	
-	data=skb->data;
-	
+
 	/*
 	 *	Process the AX.25/LAPB frame.
 	 */
 
-	skb->sk = NULL;		/* Initially we don't know who its for */
-	
-	if ((*data & 0x0F) != 0) {
-		kfree_skb(skb, FREE_READ);	/* Not a KISS data frame */
-		return 0;
-	}
-
-	data++;
-	
 	/*
 	 *	Parse the address header.
 	 */
-	if ((data = ax25_parse_addr(data, skb->len + dev->hard_header_len - 1, &src, &dest, &dp, &type)) == NULL) {
+	if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type) == NULL) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-	
+
 	/*
 	 *	Send the frame to the AX.25 auto-router
 	 */
@@ -1282,7 +1291,7 @@
 	 *	Ours perhaps ?
 	 */
 	if (dp.lastrepeat + 1 < dp.ndigi) {		/* Not yet digipeated completely */
-		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], (ax25_address *)dev->dev_addr) == 0) {
+		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) {
 			/* We are the digipeater. Mark ourselves as repeated
 			   and throw the packet back out of the same device */
 			dp.lastrepeat++;
@@ -1297,9 +1306,9 @@
 				dev = dev_scan;
 			}
 #endif
-			build_ax25_addr(skb->data + 1, &src, &dest, &dp, type);
+			build_ax25_addr(skb->data, &src, &dest, &dp, type);
 			skb->arp = 1;
-			dev_queue_xmit(skb, dev, SOPRI_NORMAL);			
+			ax25_queue_xmit(skb, dev, SOPRI_NORMAL);			
 		} else {
 			kfree_skb(skb, FREE_READ);
 		}
@@ -1307,9 +1316,13 @@
 		return 0;
 	}
 
-	 
+	/*
+	 *	Pull of the AX.25 headers leaving the CTRL/PID bytes
+	 */
+	skb_pull(skb, size_ax25_addr(&dp));
+
 	/* For our port addreses ? */
-	if (ax25cmp(&dest, (ax25_address *)dev->dev_addr) == 0)
+	if (ax25cmp(&dest, dev_addr) == 0)
 		mine = 1;
 
 #ifdef CONFIG_NETROM
@@ -1318,28 +1331,28 @@
 		mine = 1;
 #endif	
 	
-	if ((*data & ~0x10) == LAPB_UI) {	/* UI frame - bypass LAPB processing */
-		data++;
-		skb->h.raw = data + 1;		/* skip pid */
+	if ((*skb->data & ~0x10) == LAPB_UI) {	/* UI frame - bypass LAPB processing */
+		skb->h.raw = skb->data + 2;		/* skip control and pid */
 
 		if ((raw = ax25_addr_match(&dest)) != NULL)
-			ax25_send_to_raw(raw, skb, (int)*data);
+			ax25_send_to_raw(raw, skb, skb->data[1]);
 
-		if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) 
-		{
+		if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
 
 		/* Now we are pointing at the pid byte */
-		switch (*data++) {
+		switch (skb->data[1]) {
 #ifdef CONFIG_INET		
 			case AX25_P_IP:
+				skb_pull(skb,2);		/* drop PID/CTRL */
 				ax25_ip_mode_set(&src, dev, 'D');
 				ip_rcv(skb, dev, ptype);	/* Note ptype here is the wrong one, fix me later */
 				break;
 
 			case AX25_P_ARP:
+				skb_pull(skb,2);
 				arp_rcv(skb, dev, ptype);	/* Note ptype here is wrong... */
 				break;
 #endif				
@@ -1350,9 +1363,9 @@
 						kfree_skb(skb, FREE_READ);
 					} else {
 						/*
-						 *	Remove kiss/headers
+						 *	Remove the control and PID.
 						 */
-						skb_pull(skb,3+size_ax25_addr(&dp));
+						skb_pull(skb, 2);
 						skb_queue_tail(&sk->receive_queue, skb);
 						skb->sk = sk;
 						sk->rmem_alloc += skb->truesize;
@@ -1373,29 +1386,24 @@
 	}
 	
 	/* LAPB */
-	
-	/*
-	 *	Pull of the AX.25 headers leaving the CTRL/PID bytes
-	 */
-	skb_pull(skb, 1+size_ax25_addr(&dp));
-	
 	if ((ax25 = ax25_find_cb(&dest, &src, dev)) != NULL) {
-		skb->h.raw = data;
-		/* Process the frame. If it is queued up internally it returns one otherwise we 
-		   free it immediately. This routine itself wakes the user context layers so we
-		   do no further work */
+		/*
+		 *	Process the frame. If it is queued up internally it returns one otherwise we 
+		 *	free it immediately. This routine itself wakes the user context layers so we
+		 *	do no further work
+		 */
 		if (ax25_process_rx_frame(ax25, skb, type) == 0)
 			kfree_skb(skb, FREE_READ);
 
 		return 0;
 	}
 
-	if ((data[0] & 0xEF) != SABM) {
+	if ((*skb->data & 0xEF) != SABM) {
 		/*
 		 *	Never reply to a DM. Also ignore any connects for
 		 *	addresses that are not our interfaces and not a socket.
 		 */
-		if ((data[0] & 0xEF) != DM && mine)
+		if ((*skb->data & 0xEF) != DM && mine)
 			ax25_return_dm(dev, &src, &dest, &dp);
 
 		kfree_skb(skb, FREE_READ);
@@ -1413,25 +1421,6 @@
 		
 		ax25 = make->ax25;
 
-		/*
-		 *	Sort out any digipeated paths.
-		 */
-		if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-			kfree_skb(skb, FREE_READ);
-			ax25_destroy_socket(ax25);
-			return 0;
-		}
-
-		if (dp.ndigi == 0) {
-			if (ax25->digipeat != NULL) {
-				kfree_s(ax25->digipeat, sizeof(ax25_digi));
-				ax25->digipeat = NULL;
-			}
-		} else {
-			/* Reverse the source SABM's path */
-			ax25_digi_invert(&dp, ax25->digipeat);
-		}
-
 		skb_queue_head(&sk->receive_queue, skb);
 
 		skb->sk     = make;
@@ -1446,12 +1435,6 @@
 			return 0;
 		}
 
-		if (dp.ndigi != 0) {
-			ax25_return_dm(dev, &src, &dest, &dp);
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
 		if ((ax25 = ax25_create_cb()) == NULL) {
 			ax25_return_dm(dev, &src, &dest, &dp);
 			kfree_skb(skb, FREE_READ);
@@ -1469,6 +1452,25 @@
 	memcpy(&ax25->source_addr, &dest, sizeof(ax25_address));
 	memcpy(&ax25->dest_addr,   &src,  sizeof(ax25_address));
 
+	/*
+	 *	Sort out any digipeated paths.
+	 */
+	if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+		kfree_skb(skb, FREE_READ);
+		ax25_destroy_socket(ax25);
+		return 0;
+	}
+
+	if (dp.ndigi == 0) {
+		if (ax25->digipeat != NULL) {
+			kfree_s(ax25->digipeat, sizeof(ax25_digi));
+			ax25->digipeat = NULL;
+		}
+	} else {
+		/* Reverse the source SABM's path */
+		ax25_digi_invert(&dp, ax25->digipeat);
+	}
+
 	ax25->device = dev;
 	
 	ax25_send_control(ax25, UA | PF, C_RESPONSE);
@@ -1490,6 +1492,46 @@
 	return 0;
 }
 
+/*
+ *	Receive an AX.25 frame via a SLIP interface.
+ */
+static int kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+	skb->sk = NULL;		/* Initially we don't know who its for */
+
+	if ((*skb->data & 0x0F) != 0) {
+		kfree_skb(skb, FREE_READ);	/* Not a KISS data frame */
+		return 0;
+	}
+
+	skb_pull(skb, AX25_KISS_HEADER_LEN);	/* Remove the KISS byte */
+
+	return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
+}
+
+/*
+ *	Receive an AX.25 frame via an Ethernet interface.
+ */
+static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+	ax25_address port_call;
+	int len;
+
+	skb->sk = NULL;		/* Initially we don't know who its for */
+
+	if (!arp_query((unsigned char *)&port_call, dev->pa_addr, ARPHRD_AX25)) {
+		kfree_skb(skb, FREE_READ);	/* We have no port callsign */
+		return 0;
+	}
+
+	len = skb->data[14] + skb->data[15] * 256 - 5;
+
+	skb_pull(skb, AX25_BPQ_HEADER_LEN);	/* Remove the BPQ Header */
+	skb_trim(skb, len);			/* Set the length of the data */
+
+	return ax25_rcv(skb, dev, &port_call, ptype);
+}
+
 static int ax25_sendto(struct socket *sock, void *ubuf, int len, int noblock,
 	unsigned flags, struct sockaddr *usip, int addr_len)
 {
@@ -1575,8 +1617,8 @@
 	if (sk->debug)
 		printk("AX.25: sendto: building packet.\n");
 
-	size = 2 + len + 1 + size_ax25_addr(dp);	
-	/* 2 bytes for PID and (U)I frame byte: 15+ for KISS data & calls */	
+	/* Assume the worst case */
+	size = len + 2 + size_ax25_addr(dp) + AX25_BPQ_HEADER_LEN;
 
 	if ((skb = sock_alloc_send_skb(sk, size, 0, &err)) == NULL)
 		return err;
@@ -1584,46 +1626,60 @@
 	skb->sk   = sk;
 	skb->free = 1;
 	skb->arp  = 1;
-	skb_reserve(skb, 3+size_ax25_addr(dp));
-	asmptr = skb_push(skb,3+size_ax25_addr(dp));
-	if (sk->debug) {
-		printk("Building AX.25 Header (dp=%p).\n", dp);
-		if (dp != 0)
-			printk("Num digipeaters=%d\n", dp->ndigi);
-	}
 
-	/* Build an AX.25 header */
-	*asmptr++ = 0;	/* KISS data */
-	asmptr   += (lv = build_ax25_addr(asmptr, &sk->ax25->source_addr, &sax.sax25_call, dp, C_COMMAND));
-	if (sk->debug)
-		printk("Built header (%d bytes)\n",lv);
-	skb->h.raw = asmptr;
-	
-	if (sk->debug)
-		printk("base=%p pos=%p\n", skb->data, asmptr);
-	*asmptr++ = LAPB_UI;		/* Datagram - will get replaced for I frames */
-	*asmptr++ = sk->protocol;	/* AX.25 TEXT by default */
-		
+	skb_reserve(skb, size - len);
+
 	if (sk->debug)
 		printk("AX.25: Appending user data\n");
 
 	/* User data follows immediately after the AX.25 data */
-	memcpy_fromfs(skb_put(skb,len), ubuf, len);
+	memcpy_fromfs(skb_put(skb, len), ubuf, len);
+
+	/* Add the PID, usually AX25_TEXT */
+	asmptr  = skb_push(skb, 1);
+	*asmptr = sk->protocol;
+
 	if (sk->debug)
 		printk("AX.25: Transmitting buffer\n");
+
 	if (sk->type == SOCK_SEQPACKET) {
 		/* Connected mode sockets go via the LAPB machine */
 		if (sk->state != TCP_ESTABLISHED) {
 			kfree_skb(skb, FREE_WRITE);
 			return -ENOTCONN;
 		}
+
 		ax25_output(sk->ax25, skb);	/* Shove it onto the queue and kick */
+
 		return len;
 	} else {
+		asmptr = skb_push(skb, 1 + size_ax25_addr(dp));
+
+		if (sk->debug) {
+			printk("Building AX.25 Header (dp=%p).\n", dp);
+			if (dp != 0)
+				printk("Num digipeaters=%d\n", dp->ndigi);
+		}
+
+		/* Build an AX.25 header */
+		asmptr += (lv = build_ax25_addr(asmptr, &sk->ax25->source_addr, &sax.sax25_call, dp, C_COMMAND));
+
+		if (sk->debug)
+			printk("Built header (%d bytes)\n",lv);
+
+		skb->h.raw = asmptr;
+	
+		if (sk->debug)
+			printk("base=%p pos=%p\n", skb->data, asmptr);
+
+		*asmptr = LAPB_UI;
+
 		/* Datagram frames go straight out of the door as UI */
-		dev_queue_xmit(skb, sk->ax25->device, SOPRI_NORMAL);
+		ax25_queue_xmit(skb, sk->ax25->device, SOPRI_NORMAL);
+
 		return len;
 	}
+		
 }
 
 static int ax25_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags)
@@ -1645,7 +1701,7 @@
 	int copied = 0;
 	struct sk_buff *skb;
 	int er;
-	int bias=0;
+	int bias = 0;
 
 	if (sk->err) {
 		er      = -sk->err;
@@ -1657,18 +1713,17 @@
 		*addr_len = sizeof(*sax);
 
 	/* This works for seqpacket too. The receiver has ordered the queue for us! We do one quick check first though */
-	if (sk->type == SOCK_SEQPACKET)
-	{
-		if(sk->state != TCP_ESTABLISHED)
+	if (sk->type == SOCK_SEQPACKET) {
+		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
-		bias=2;
+		bias = 2;
 	}
 
 	/* Now we can treat all alike */
 	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
 		return er;
 
-	copied= (size < skb->len-bias) ? size : skb->len-bias;
+	copied = (size < skb->len - bias) ? size : skb->len - bias;
 	skb_copy_datagram(skb, bias, ubuf, copied);
 	
 	if (sax) {
@@ -1685,12 +1740,12 @@
 		addrptr += sizeof(*sax);
 
 		while (ct < digi.ndigi) {
-			memcpy(addrptr, &digi. calls[ct], 7);
-			addrptr += 7;
+			memcpy(addrptr, &digi. calls[ct], AX25_ADDR_LEN);
+			addrptr += AX25_ADDR_LEN;
 			ct++;
 		}
 		if (addr_len)
-			*addr_len = sizeof(*sax) + 7 * digi.ndigi;
+			*addr_len = sizeof(*sax) + AX25_ADDR_LEN * digi.ndigi;
 	}
 
 	skb_free_datagram(skb);
@@ -1819,7 +1874,7 @@
   
 	cli();
 
-	len += sprintf(buffer, "dest_addr src_addr  dev st vs vr va    t1     t2     t3     n2  rtt wnd Snd-Q Rcv-Q\n");
+	len += sprintf(buffer, "dest_addr src_addr  dev  st vs vr va    t1     t2     t3     n2  rtt wnd Snd-Q Rcv-Q\n");
 
 	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
 		if ((dev = ax25->device) == NULL)
@@ -1829,7 +1884,7 @@
 
 		len += sprintf(buffer + len, "%-9s ",
 			ax2asc(&ax25->dest_addr));
-		len += sprintf(buffer + len, "%-9s %-3s %2d %2d %2d %2d %3d/%03d %2d/%02d %3d/%03d %2d/%02d %3d %3d",
+		len += sprintf(buffer + len, "%-9s %-4s %2d %2d %2d %2d %3d/%03d %2d/%02d %3d/%03d %2d/%02d %3d %3d",
 			ax2asc(&ax25->source_addr), devname,
 			ax25->state,
 			ax25->vs, ax25->vr, ax25->va,
@@ -1904,7 +1959,16 @@
 {
 	0,	/* MUTTER ntohs(ETH_P_AX25),*/
 	0,		/* copy */
-	ax25_rcv,
+	kiss_rcv,
+	NULL,
+	NULL,
+};
+
+static struct packet_type bpq_packet_type = 
+{
+	0,	/* MUTTER ntohs(ETH_P_BPQ),*/
+	0,		/* copy */
+	bpq_rcv,
 	NULL,
 	NULL,
 };
@@ -1919,8 +1983,45 @@
 	sock_register(ax25_proto_ops.family, &ax25_proto_ops);
 	ax25_packet_type.type = htons(ETH_P_AX25);
 	dev_add_pack(&ax25_packet_type);	
+	bpq_packet_type.type  = htons(ETH_P_BPQ);
+	dev_add_pack(&bpq_packet_type);
 	register_netdevice_notifier(&ax25_dev_notifier);
-	printk("GW4PTS/G4KLX AX.25 for Linux. Version 0.29 ALPHA for Linux NET3.029 (Linux 1.3.0)\n");
+	printk("GW4PTS/G4KLX AX.25 for Linux. Version 0.30 ALPHA for Linux NET3.030 (Linux 1.3.0)\n");
+}
+
+/*
+ *	A small shim to dev_queue_xmit to handle the difference between
+ *	KISS AX.25 and BPQ AX.25.
+ */
+void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+{
+	static char bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+	unsigned char *ptr;
+	int size;
+
+	if (dev->type == ARPHRD_ETHER) {
+		if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {
+			printk("ax25_queue_xmit: not enough space to add BPQ Ether header\n");
+			skb->free = 1;
+			kfree_skb(skb, FREE_WRITE);
+			return;
+		}
+
+		size = skb->len;
+	
+		ptr = skb_push(skb, 2);
+
+		*ptr++ = (size + 5) % 256;
+		*ptr++ = (size + 5) / 256;
+
+		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
+	} else {
+		ptr = skb_push(skb, 1);
+		
+		*ptr++ = 0;			/* KISS */
+	}
+
+	dev_queue_xmit(skb, dev, pri);
 }
 
 /*******************************************************************************************************************\
@@ -1940,15 +2041,17 @@
 		void *saddr, unsigned len)
 {
   	/* header is an AX.25 UI frame from us to them */
- 	unsigned char *buff=skb_push(skb,17);
+ 	unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
+
   	*buff++ = 0;	/* KISS DATA */
   	
 	if (daddr != NULL)
 		memcpy(buff, daddr, dev->addr_len);	/* Address specified */
+
   	buff[6] &= ~LAPB_C;
   	buff[6] &= ~LAPB_E;
   	buff[6] |= SSID_SPARE;
-  	buff += 7;
+  	buff += AX25_ADDR_LEN;
 
   	if (saddr != NULL)
   		memcpy(buff, saddr, dev->addr_len);
@@ -1958,7 +2061,8 @@
   	buff[6] &= ~LAPB_C;
   	buff[6] |= LAPB_E;
   	buff[6] |= SSID_SPARE;
-  	buff   += 7;
+  	buff   += AX25_ADDR_LEN;
+
   	*buff++ = LAPB_UI;	/* UI */
 
   	/* Append a suitable AX.25 PID */
@@ -1970,26 +2074,39 @@
   		case ETH_P_ARP:
   			*buff++ = AX25_P_ARP;
   			break;
-
   		default:
   			*buff++ = 0;
   			break;
  	}
 	
-	if (daddr != NULL)  
-	  	return 17;
+	if (daddr != NULL)
+	  	return AX25_HEADER_LEN;
 
-	return -17;	/* Unfinished header */
+	return -AX25_HEADER_LEN;	/* Unfinished header */
 }
 
 int ax25_rebuild_header(unsigned char *bp, struct device *dev, unsigned long dest, struct sk_buff *skb)
 {
+	int mode;
+
   	if (arp_find(bp + 1, dest, dev, dev->pa_addr, skb))
   		return 1;
 
+	if (bp[16] == AX25_P_IP) {
+		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
+
+		if (mode == 'V' || mode == 'v' || (mode == ' ' && sl_get_ax25_mode(dev))) {
+			skb_device_unlock(skb);
+			skb_pull(skb, AX25_HEADER_LEN - 1);	/* Keep PID */
+			ax25_send_frame(skb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
+			return 1;
+		}
+	}
+
   	bp[7]  &= ~LAPB_C;
   	bp[7]  &= ~LAPB_E;
   	bp[7]  |= SSID_SPARE;
+
   	bp[14] &= ~LAPB_C;
   	bp[14] |= LAPB_E;
   	bp[14] |= SSID_SPARE;
diff -u --recursive --new-file v1.3.6/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v1.3.6/linux/net/ax25/ax25_in.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ax25/ax25_in.c	Wed Jul  5 13:06:27 1995
@@ -57,22 +57,24 @@
  *	This is where all valid I frames are sent to, to be dispatched to
  *	whichever protocol requires them.
  */
-static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb, unsigned char *iframe)
+static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
 {
 	int queued = 0;
 
-	switch (iframe[1]) {
+	skb->h.raw = skb->data;
+
+	switch (skb->data[1]) {
 #ifdef CONFIG_NETROM
 		case AX25_P_NETROM:
-			/* We can't handle digipeated NET/ROM frames */
-			if (ax25->digipeat == NULL)
-				queued = nr_route_frame(skb, ax25->device);
+			skb_pull(skb, 2);
+			queued = nr_route_frame(skb, ax25);
 			break;
 #endif
 #ifdef CONFIG_INET
 		case AX25_P_IP:
 			ax25_ip_mode_set(&ax25->dest_addr, ax25->device, 'V');
-			skb->h.raw = skb->data;
+			skb->h.raw += 2;
+			skb_push(skb, skb->dev->hard_header_len);
 			ip_rcv(skb, skb->dev, NULL);	/* Wrong ptype */
 			queued = 1;
 			break;
@@ -99,9 +101,9 @@
  *	The handling of the timer(s) is in file ax25_timer.c.
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type)
+static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int type)
 {
-	int pf = frame[0] & PF;
+	int pf = skb->data[0] & PF;
 
 	switch (frametype) {
 		case SABM:
@@ -157,9 +159,9 @@
  *	The handling of the timer(s) is in file ax25_timer.c
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type)
+static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int type)
 {
-	int pf = frame[0] & PF;
+	int pf = skb->data[0] & PF;
 
 	switch (frametype) {
 		case SABM:
@@ -216,11 +218,11 @@
  *	The handling of the timer(s) is in file ax25_timer.c
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type)
+static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int type)
 {
-	unsigned short nr = (frame[0] >> 5) & 7;
-	unsigned short ns = (frame[0] >> 1) & 7;
-	int pf = frame[0] & PF;
+	unsigned short nr = (skb->data[0] >> 5) & 7;
+	unsigned short ns = (skb->data[0] >> 1) & 7;
+	int pf = skb->data[0] & PF;
 	int queued = 0;
 
 	switch (frametype) {
@@ -320,7 +322,7 @@
 				break;
 			}
 			if (ns == ax25->vr) {
-				queued = ax25_rx_iframe(ax25, skb, frame);
+				queued = ax25_rx_iframe(ax25, skb);
 				if (ax25->condition & OWN_RX_BUSY_CONDITION) {
 					if (pf) ax25_enquiry_response(ax25);
 					break;
@@ -364,11 +366,11 @@
  *	The handling of the timer(s) is in file ax25_timer.c
  *	Handling of state 0 and connection release is in ax25.c.
  */
-static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type)
+static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int type)
 {
-	unsigned short nr = (frame[0] >> 5) & 7;
-	unsigned short ns = (frame[0] >> 1) & 7;
-	int pf = frame[0] & PF;
+	unsigned short nr = (skb->data[0] >> 5) & 7;
+	unsigned short ns = (skb->data[0] >> 1) & 7;
+	int pf = skb->data[0] & PF;
 	int queued = 0;
 
 	switch (frametype) {
@@ -511,7 +513,7 @@
 				break;
 			}
 			if (ns == ax25->vr) {
-				queued = ax25_rx_iframe(ax25, skb, frame);
+				queued = ax25_rx_iframe(ax25, skb);
 				if (ax25->condition & OWN_RX_BUSY_CONDITION) {
 					if (pf) ax25_enquiry_response(ax25);
 					break;
@@ -556,7 +558,6 @@
 int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type)
 {
 	int queued = 0, frametype;
-	unsigned char *frame;
 
 	if (ax25->state != AX25_STATE_1 && ax25->state != AX25_STATE_2 &&
 	    ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) {
@@ -566,22 +567,20 @@
 
 	del_timer(&ax25->timer);
 
-	frame = skb->h.raw;
-
-	frametype = ax25_decode(frame);
+	frametype = ax25_decode(skb->data);
 
 	switch (ax25->state) {
 		case AX25_STATE_1:
-			queued = ax25_state1_machine(ax25, skb, frame, frametype, type);
+			queued = ax25_state1_machine(ax25, skb, frametype, type);
 			break;
 		case AX25_STATE_2:
-			queued = ax25_state2_machine(ax25, skb, frame, frametype, type);
+			queued = ax25_state2_machine(ax25, skb, frametype, type);
 			break;
 		case AX25_STATE_3:
-			queued = ax25_state3_machine(ax25, skb, frame, frametype, type);
+			queued = ax25_state3_machine(ax25, skb, frametype, type);
 			break;
 		case AX25_STATE_4:
-			queued = ax25_state4_machine(ax25, skb, frame, frametype, type);
+			queued = ax25_state4_machine(ax25, skb, frametype, type);
 			break;
 	}
 
diff -u --recursive --new-file v1.3.6/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v1.3.6/linux/net/ax25/ax25_out.c	Tue Jun  6 12:16:43 1995
+++ linux/net/ax25/ax25_out.c	Wed Jul  5 13:06:27 1995
@@ -68,7 +68,7 @@
 	if (skb == NULL)
 		return;
 	
-	frame = skb->h.raw;	/* KISS + header */
+	frame = skb_push(skb, 1);	/* KISS + header */
 
 	*frame = I;
 	*frame |= poll_bit;
@@ -146,7 +146,7 @@
 
 void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
 {
-	unsigned char *ptr = skb->data;
+	unsigned char *ptr;
 
 	if (ax25->device == NULL) {
 		if (ax25->sk != NULL) {
@@ -159,12 +159,19 @@
 		return;
 	}
 
-	*ptr++ = 0;	/* KISS data */
-	ptr   += build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type);
+	if (skb_headroom(skb) < size_ax25_addr(ax25->digipeat)) {
+		printk("ax25_transmit_buffer: not enough room for digi-peaters\n");
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+		return;
+	}
+
+	ptr = skb_push(skb, size_ax25_addr(ax25->digipeat));
+	build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type);
 
 	skb->arp = 1;
 
-	dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
+	ax25_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
 }
 
 /*
diff -u --recursive --new-file v1.3.6/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v1.3.6/linux/net/ax25/ax25_route.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ax25/ax25_route.c	Wed Jul  5 13:06:27 1995
@@ -145,10 +145,10 @@
   
 	cli();
 
-	len += sprintf(buffer, "callsign  dev count      time mode\n");
+	len += sprintf(buffer, "callsign  dev  count time      mode\n");
 
 	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
-		len += sprintf(buffer + len, "%-9s %-3s %5d %9d",
+		len += sprintf(buffer + len, "%-9s %-4s %5d %9d",
 			ax2asc(&ax25_rt->callsign),
 			ax25_rt->dev ? ax25_rt->dev->name : "???",
 			ax25_rt->n,
diff -u --recursive --new-file v1.3.6/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v1.3.6/linux/net/ax25/ax25_subr.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ax25/ax25_subr.c	Wed Jul  5 13:06:27 1995
@@ -143,25 +143,22 @@
 	struct sk_buff *skb;
 	unsigned char  *dptr;
 	struct device *dev;
-	int asize;
 	
 	if ((dev = ax25->device) == NULL)
 		return;	/* Route died */
 
-	asize= 1+size_ax25_addr(ax25->digipeat);
-	
-	if ((skb = alloc_skb(16 + asize, GFP_ATOMIC)) == NULL)
+	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat) + 1, GFP_ATOMIC)) == NULL)
 		return;
-	skb_reserve(skb, asize);
-	
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat));
+
 	if (ax25->sk != NULL) {
 		skb->sk = ax25->sk;
         	ax25->sk->wmem_alloc += skb->truesize;
 	}
 
 	/* Assume a response - address structure for DTE */
-	dptr = skb_put(skb,1);
-	
+	dptr = skb_put(skb, 1);
 	
 	if ((frametype & U) == S)		/* S frames carry NR */
 		frametype |= (ax25->vr << 5);
@@ -169,7 +166,7 @@
 	*dptr = frametype;
 
 	skb->free = 1;
-	skb_push(skb,asize);
+
 	ax25_transmit_buffer(ax25, skb, type);
 }
 
@@ -183,35 +180,33 @@
 	struct sk_buff *skb;
 	char *dptr;
 	ax25_digi retdigi;
-	int len = 2 + size_ax25_addr(digi);
 
-	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+	if (dev == NULL)
+		return;
+
+	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(digi) + 1, GFP_ATOMIC)) == NULL)
 		return;	/* Next SABM will get DM'd */
 
-	skb_reserve(skb,len-1);
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(digi));
 
 	ax25_digi_invert(digi, &retdigi);
 
-	dptr = skb_put(skb,1);
+	dptr = skb_put(skb, 1);
 	skb->sk = NULL;
 
 	*dptr = DM | PF;
 
-	if (dev == NULL)
-		return;
-
 	/*
-	 *	Do the address ourselves.
+	 *	Do the address ourselves
 	 */
 
-	dptr    = skb_push(skb, len-1);
-	*dptr++ = 0;
-	dptr   += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE);
+	dptr  = skb_push(skb, size_ax25_addr(digi));
+	dptr += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE);
 
 	skb->arp  = 1;
 	skb->free = 1;
 
-	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+	ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
 }
 
 /*
@@ -233,7 +228,7 @@
 }
 
 /*
- *	Calculate the r Round Trip Time
+ *	Calculate the Round Trip Time
  */
 void ax25_calculate_rtt(ax25_cb *ax25)
 {
@@ -273,10 +268,10 @@
 	}
 		
 	/* Copy to, from */
-	if (dest != NULL) memcpy(dest, buf + 0, 7);
-	if (src != NULL)  memcpy(src,  buf + 7, 7);
-	buf += 14;
-	len -= 14;
+	if (dest != NULL) memcpy(dest, buf + 0, AX25_ADDR_LEN);
+	if (src != NULL)  memcpy(src,  buf + 7, AX25_ADDR_LEN);
+	buf += 2 * AX25_ADDR_LEN;
+	len -= 2 * AX25_ADDR_LEN;
 	digi->lastrepeat = -1;
 	digi->ndigi      = 0;
 	
@@ -286,7 +281,7 @@
 		if (len < 7) return NULL;	/* Short packet */
 
 		if (digi != NULL) {
-			memcpy(&digi->calls[d], buf, 7);
+			memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
 			digi->ndigi = d + 1;
 			if (buf[6] & AX25_REPEATED) {
 				digi->repeated[d] = 1;
@@ -296,8 +291,8 @@
 			}
 		}
 
-		buf += 7;
-		len -= 7;
+		buf += AX25_ADDR_LEN;
+		len -= AX25_ADDR_LEN;
 		d++;
 	}
 
@@ -313,7 +308,7 @@
 	int len = 0;
 	int ct  = 0;
 
-	memcpy(buf, dest, 7);
+	memcpy(buf, dest, AX25_ADDR_LEN);
 	
 	if (flag != C_COMMAND && flag != C_RESPONSE)
 		printk("build_ax25_addr: Bogus flag %d\n!", flag);
@@ -322,9 +317,9 @@
 
 	if (flag == C_COMMAND) buf[6] |= LAPB_C;
 
-	buf += 7;
-	len += 7;
-	memcpy(buf, src, 7);
+	buf += AX25_ADDR_LEN;
+	len += AX25_ADDR_LEN;
+	memcpy(buf, src, AX25_ADDR_LEN);
 	buf[6] &= ~(LAPB_E | LAPB_C);
 	buf[6] |= SSID_SPARE;
 
@@ -334,14 +329,14 @@
 	 */
 	if (d == NULL || d->ndigi == 0) {
 		buf[6] |= LAPB_E;
-		return 14;
+		return 2 * AX25_ADDR_LEN;
 	}	
 	
-	buf += 7;
-	len += 7;
+	buf += AX25_ADDR_LEN;
+	len += AX25_ADDR_LEN;
 	
 	while (ct < d->ndigi) {
-		memcpy(buf, &d->calls[ct], 7);
+		memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
 		if (d->repeated[ct])
 			buf[6] |= AX25_REPEATED;
 		else
@@ -349,8 +344,8 @@
 		buf[6] &= ~LAPB_E;
 		buf[6] |= SSID_SPARE;
 
-		buf += 7;
-		len += 7;
+		buf += AX25_ADDR_LEN;
+		len += AX25_ADDR_LEN;
 		ct++;
 	}
 
@@ -362,9 +357,9 @@
 int size_ax25_addr(ax25_digi *dp)
 {
 	if (dp == NULL)
-		return 14;
+		return 2 * AX25_ADDR_LEN;
 
-	return 14 + (7 * dp->ndigi);
+	return AX25_ADDR_LEN * (2 + dp->ndigi);
 }
 	
 /* 
diff -u --recursive --new-file v1.3.6/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.6/linux/net/core/dev.c	Fri Jun 30 16:22:31 1995
+++ linux/net/core/dev.c	Wed Jul  5 13:06:27 1995
@@ -93,7 +93,7 @@
 static struct sk_buff_head backlog = 
 {
 	(struct sk_buff *)&backlog, (struct sk_buff *)&backlog
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 	,SK_HEAD_SKB
 #endif
 };
@@ -320,7 +320,7 @@
 
 	if(pri>=0 && !skb_device_locked(skb))
 		skb_device_lock(skb);	/* Shove a lock on the frame */
-#ifdef CONFIG_SKB_CHECK 
+#if CONFIG_SKB_CHECK 
 	IS_SKB(skb);
 #endif    
 	skb->dev = dev;
@@ -444,7 +444,7 @@
 	/*
 	 *	Add it to the "backlog" queue. 
 	 */
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 	IS_SKB(skb);
 #endif	
 	skb_queue_tail(&backlog,skb);
@@ -647,7 +647,7 @@
 		*	skb->h.raw point to the MAC and encapsulated data
 		*/
 
-		skb->h.raw = skb->data+skb->dev->hard_header_len;
+		skb->h.raw = skb->data;
 
 	       /*
 		* 	Fetch the packet protocol ID. 
diff -u --recursive --new-file v1.3.6/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v1.3.6/linux/net/core/skbuff.c	Fri Jun 30 16:22:31 1995
+++ linux/net/core/skbuff.c	Wed Jul  5 13:06:27 1995
@@ -179,7 +179,7 @@
 #endif
 
 
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 void skb_queue_head_init(struct sk_buff_head *list)
 {
 	list->prev = (struct sk_buff *)list;
@@ -440,7 +440,7 @@
 			__builtin_return_address(0));
 		return;
   	}
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 	IS_SKB(skb);
 #endif
 	if (skb->lock)
@@ -544,6 +544,7 @@
 	skb->localroute=0;
 	skb->stamp.tv_sec=0;	/* No idea about time */
 	skb->localroute = 0;
+	skb->ip_summed = 0;
 	save_flags(flags);
 	cli();
 	net_skbcount++;
diff -u --recursive --new-file v1.3.6/linux/net/core/sock.c linux/net/core/sock.c
--- v1.3.6/linux/net/core/sock.c	Fri Jun 30 16:22:31 1995
+++ linux/net/core/sock.c	Wed Jul  5 13:06:27 1995
@@ -382,7 +382,7 @@
 void sock_wfree(struct sock *sk, struct sk_buff *skb)
 {
 	int s=skb->truesize;
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 	IS_SKB(skb);
 #endif
 	kfree_skbmem(skb);
@@ -403,7 +403,7 @@
 void sock_rfree(struct sock *sk, struct sk_buff *skb)
 {
 	int s=skb->truesize;
-#ifdef CONFIG_SKB_CHECK
+#if CONFIG_SKB_CHECK
 	IS_SKB(skb);
 #endif	
 	kfree_skbmem(skb);
diff -u --recursive --new-file v1.3.6/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v1.3.6/linux/net/ethernet/eth.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ethernet/eth.c	Wed Jul  5 13:06:27 1995
@@ -24,7 +24,8 @@
  *				  and changes for new arp and skbuff.
  *		Alan Cox	: Redid header building to reflect new format.
  *		Alan Cox	: ARP only when compiled with CONFIG_INET
- *		Greg Page	: 802.2 and SNAP stuff
+ *		Greg Page	: 802.2 and SNAP stuff.
+ *		Alan Cox	: MAC layer pointers/new format.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -48,6 +49,7 @@
 #include <linux/config.h>
 #include <net/arp.h>
 #include <net/sock.h>
+#include <asm/checksum.h>
 
 void eth_setup(char *str, int *ints)
 {
@@ -166,9 +168,13 @@
  
 unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
 {
-	struct ethhdr *eth = (struct ethhdr *) skb->data;
+	struct ethhdr *eth;
 	unsigned char *rawp;
 	
+	skb->mac.raw=skb->data;
+	skb_pull(skb,14);	
+	eth= skb->mac.ethernet;
+	
 	if(*eth->h_dest&1)
 	{
 		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
@@ -186,11 +192,20 @@
 	if (ntohs(eth->h_proto) >= 1536)
 		return eth->h_proto;
 		
-	rawp = (unsigned char *)(eth + 1);
+	rawp = skb->data;
 	
+	/*
+	 *	This is a magic hack to spot IPX packets. Older Novell breaks
+	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *	layer. We look for FFFF which isnt a used 802.2 SSAP/DSAP. This
+	 *	won't work for fault tolerant netware but does for the rest.
+	 */
 	if (*(unsigned short *)rawp == 0xFFFF)
 		return htons(ETH_P_802_3);
 		
+	/*
+	 *	Real 802.2 LLC
+	 */
 	return htons(ETH_P_802_2);
 }
 
@@ -214,3 +229,20 @@
 	}
 }
 
+/*
+ *	Copy from an ethernet device memory space to an sk_buff while checksumming if IP
+ */
+ 
+void eth_copy_and_sum(struct sk_buff *dest,unsigned char *src, int length, int base)
+{
+	struct ethhdr *eth=(struct ethhdr *)dest->data;
+	memcpy(dest->data,src,34);	/* ethernet is always >= 60 */
+	length-=34;
+	if(eth->h_proto!=htons(ETH_P_IP))
+	{
+		memcpy(dest->data+34,src+34,length);
+		return;
+	}
+	dest->csum=csum_partial_copy(src+34,dest->data+34,length,base);
+	dest->ip_summed=1;
+}
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.6/linux/net/ipv4/af_inet.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ipv4/af_inet.c	Thu Jul  6 13:24:59 1995
@@ -1280,6 +1280,7 @@
 {
 	struct sock *sk=(struct sock *)sock->data;
 	int err;
+	int tmp;
 
 	switch(cmd) 
 	{
@@ -1288,7 +1289,11 @@
 			err=verify_area(VERIFY_READ,(int *)arg,sizeof(long));
 			if(err)
 				return err;
-			sk->proc = get_fs_long((int *) arg);
+			tmp = get_fs_long((int *) arg);
+			/* see inet_fcntl */
+			if (current->pid != tmp && current->pgrp != -tmp && !suser())
+				return -EPERM;
+			sk->proc = tmp;
 			return(0);
 		case FIOGETOWN:
 		case SIOCGPGRP:
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.6/linux/net/ipv4/arp.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ipv4/arp.c	Thu Jul  6 13:22:05 1995
@@ -86,8 +86,8 @@
 	struct arp_table		*next;			/* Linked entry list 		*/
 	unsigned long			last_used;		/* For expiry 			*/
 	unsigned int			flags;			/* Control status 		*/
-	unsigned long			ip;			/* ip address of entry 		*/
-	unsigned long			mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
+	u32				ip;			/* ip address of entry 		*/
+	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
 	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
 	unsigned char			hlen;			/* Length of hardware address 	*/
 	unsigned short			htype;			/* Type of hardware in use	*/
@@ -143,7 +143,7 @@
 
 /* Forward declarations. */
 static void arp_check_expire (unsigned long);  
-static struct arp_table *arp_lookup(unsigned long paddr, enum proxy proxy);
+static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy);
 
 
 static struct timer_list arp_timer =
@@ -307,8 +307,8 @@
  *	message.
  */
 
-void arp_send(int type, int ptype, unsigned long dest_ip, 
-	      struct device *dev, unsigned long src_ip, 
+void arp_send(int type, int ptype, u32 dest_ip, 
+	      struct device *dev, u32 src_ip, 
 	      unsigned char *dest_hw, unsigned char *src_hw)
 {
 	struct sk_buff *skb;
@@ -406,7 +406,7 @@
 
 	if (--entry->retries > 0)
 	{
-		unsigned long ip = entry->ip;
+		u32 ip = entry->ip;
 		struct device *dev = entry->dev;
 
 		/* Set new timer. */
@@ -493,14 +493,6 @@
 			else
 				dev_queue_xmit(skb,skb->dev,skb->sk->priority);
 		}
-		else
-		{
-			/* This routine is only ever called when 'entry' is
-			   complete. Thus this can't fail. */
-			printk("arp_send_q: The impossible occurred. Please notify Alan.\n");
-			printk("arp_send_q: active entity %s\n",in_ntoa(entry->ip));
-			printk("arp_send_q: failed to find %s\n",in_ntoa(skb->raddr));
-		}
 	}
 	restore_flags(flags);
 }
@@ -510,7 +502,7 @@
  *	Delete an ARP mapping entry in the cache.
  */
 
-void arp_destroy(unsigned long ip_addr, int force)
+void arp_destroy(u32 ip_addr, int force)
 {
         int checked_proxies = 0;
 	struct arp_table *entry;
@@ -571,15 +563,10 @@
 	int addr_hint,hlen,htype;
 	unsigned long hash;
 	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
-	long sip,tip;
 	unsigned char *sha,*tha;
+	u32 sip,tip;
 	
 /*
- *	ARP carries the MAC addresses wrapped in the packet. We can't sanity
- *	check this as proxy arp has them different.
- */
-	skb_pull(skb,dev->hard_header_len);
-/*
  *	The hardware length of the packet should match the hardware length
  *	of the device.  Similarly, the hardware types should match.  The
  *	device should be ARP-able.  Also, if pln is not 4, then the lookup
@@ -839,16 +826,49 @@
 
 
 /*
+ *	Find an arp mapping in the cache. If not found, return false.
+ */
+
+int arp_query(unsigned char *haddr, u32 paddr, unsigned short type)
+{
+	struct arp_table *entry;
+	unsigned long hash = HASH(paddr);
+
+	/*
+	 *	Find an entry
+	 */
+	cli();
+
+	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+		if (entry->ip == paddr && entry->htype == type)
+			break;
+
+	if (entry != NULL) {
+		/*
+		 *	Update the record
+		 */
+		
+		entry->last_used = jiffies;
+		memcpy(haddr, entry->ha, entry->hlen);
+		sti();
+		return 1;
+	}
+
+	sti();
+	return 0;
+}
+
+/*
  *	Find an arp mapping in the cache. If not found, post a request.
  */
 
-int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
-	   unsigned long saddr, struct sk_buff *skb)
+int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
+	   u32 saddr, struct sk_buff *skb)
 {
 	struct arp_table *entry;
 	unsigned long hash;
 #ifdef CONFIG_IP_MULTICAST
-	unsigned long taddr;
+	u32 taddr;
 #endif	
 
 	switch (ip_chk_addr(paddr))
@@ -1063,7 +1083,7 @@
  *      for proxy entries, otherwise the netmask will be used
  */
 
-static struct arp_table *arp_lookup(unsigned long paddr, enum proxy proxy)
+static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy)
 {
 	struct arp_table *entry;
 	unsigned long hash = HASH(paddr);
@@ -1082,14 +1102,14 @@
 }
 
 
-int arp_find_cache(unsigned char *dp, unsigned long daddr, struct device *dev)
+int arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev)
 {	
 	/* 
 	 *	We need the broadcast/multicast awareness here and the find routine split up.
 	 */
 	struct arp_table *entry;
 #ifdef CONFIG_IP_MULTICAST
-	unsigned long taddr;
+	u32 taddr;
 #endif	
 
 	switch (ip_chk_addr(daddr))
@@ -1143,8 +1163,8 @@
 	struct arp_table *entry;
 	struct sockaddr_in *si;
 	int htype, hlen;
-	unsigned long ip;
 	struct rtable *rt;
+	u32 ip;
 
 	memcpy_fromfs(&r, req, sizeof(r));
 
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v1.3.6/linux/net/ipv4/icmp.c	Fri Jun 30 16:22:31 1995
+++ linux/net/ipv4/icmp.c	Thu Jul  6 14:04:42 1995
@@ -32,6 +32,7 @@
  *		Stefan Becker   :       ICMP redirects in icmp_send().
  *		Peter Belding	:	Tightened up ICMP redirect handling
  *		Alan Cox	:	Tightened even more.
+ *		Arnt Gulbrandsen:	Misplaced #endif with net redirect and break
  *
  * 
  *
@@ -213,7 +214,7 @@
 	len = dev->hard_header_len + sizeof(struct iphdr) + sizeof(struct icmphdr) +
 		sizeof(struct iphdr) + 32;	/* amount of header to return */
 	   
-	skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);
+	skb = (struct sk_buff *) alloc_skb(len+15, GFP_ATOMIC);
 	if (skb == NULL) 
 	{
 		icmp_statistics.IcmpOutErrors++;
@@ -380,8 +381,8 @@
 #ifdef not_a_good_idea
 			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
 				ip, 0, icmph->un.gateway, dev,0, 0, 0);
-			break;
 #endif
+			break;
 		case ICMP_REDIR_HOST:
 			/*
 			 *	Add better route to host.
@@ -434,7 +435,7 @@
 	icmp_statistics.IcmpOutEchoReps++;
 	icmp_statistics.IcmpOutMsgs++;
 	
-	size = dev->hard_header_len + 64 + len;
+	size = dev->hard_header_len + 64 + len + 15;
 	skb2 = alloc_skb(size, GFP_ATOMIC);
 
 	if (skb2 == NULL) 
@@ -508,7 +509,7 @@
                 /* correct answers are possible for everything >= 12 */
 	}
 
-	size = dev->hard_header_len + 84;
+	size = dev->hard_header_len + 84 + 15;
 
 	if (! (skb2 = alloc_skb(size, GFP_ATOMIC))) 
 	{
@@ -599,7 +600,7 @@
 	icmp_statistics.IcmpOutMsgs++;
 	icmp_statistics.IcmpOutAddrMaskReps++;
 	
-	size = dev->hard_header_len + 64 + len;
+	size = dev->hard_header_len + 64 + len + 15;
 	skb2 = alloc_skb(size, GFP_ATOMIC);
 	if (skb2 == NULL) 
 	{
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/ip.c linux/net/ipv4/ip.c
--- v1.3.6/linux/net/ipv4/ip.c	Fri Jun 30 16:22:32 1995
+++ linux/net/ipv4/ip.c	Thu Jul  6 14:04:42 1995
@@ -185,7 +185,7 @@
 		 *	Build a hardware header. Source address is our mac, destination unknown
 		 *  	(rebuild header will sort this out)
 		 */
-		skb_reserve(skb,dev->hard_header_len);
+		skb_reserve(skb,(dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
 		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
 		if (mac < 0)
 		{
@@ -307,15 +307,15 @@
 	 */
 	 
 	iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
-	
+
 	iph->version  = 4;
+	iph->ihl      = 5;
 	iph->tos      = tos;
 	iph->frag_off = 0;
 	iph->ttl      = ttl;
 	iph->daddr    = daddr;
 	iph->saddr    = saddr;
 	iph->protocol = type;
-	iph->ihl      = 5;
 	skb->ip_hdr   = iph;
 
 	return(20 + tmp);	/* IP header plus MAC header size */
@@ -502,7 +502,7 @@
 	 *	Allocate memory for the IP header (plus 8 octets for ICMP).
 	 */
 
-	ihlen = (iph->ihl * sizeof(unsigned long));
+	ihlen = iph->ihl * 4;
 	qp->iph = (struct iphdr *) kmalloc(64 + 8, GFP_ATOMIC);
 	if (qp->iph == NULL)
 	{
@@ -628,7 +628,7 @@
 	/* Done with all fragments. Fixup the new IP header. */
 	iph = skb->h.iph;
 	iph->frag_off = 0;
-	iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count);
+	iph->tot_len = htons((iph->ihl * 4) + count);
 	skb->ip_hdr = iph;
 
 	ip_statistics.IpReasmOKs++;
@@ -700,7 +700,7 @@
 	 *	Determine the position of this fragment.
 	 */
 
-	ihl = (iph->ihl * sizeof(unsigned long));
+	ihl = iph->ihl * 4;
 	end = offset + ntohs(iph->tot_len) - ihl;
 
 	/*
@@ -857,7 +857,7 @@
 	 *	Setup starting values.
 	 */
 
-	hlen = (iph->ihl * sizeof(unsigned long));
+	hlen = iph->ihl * 4;
 	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
 	hlen += dev->hard_header_len;		/* Total header size */
 	mtu = (dev->mtu - hlen);		/* Size of data space */
@@ -901,7 +901,7 @@
 	 */
 
 	if (is_frag & 2)
-		offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
+		offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
 	else
 		offset = 0;
 
@@ -927,7 +927,7 @@
 		 *	Allocate buffer.
 		 */
 
-		if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL)
+		if ((skb2 = alloc_skb(len + hlen+15,GFP_ATOMIC)) == NULL)
 		{
 			NETDEBUG(printk("IP: frag: no memory for new fragment!\n"));
 			ip_statistics.IpFragFails++;
@@ -1160,7 +1160,7 @@
 		 *	data so the problem goes away then.
 		 */
 
-		skb2 = alloc_skb(dev2->hard_header_len + skb->len, GFP_ATOMIC);
+		skb2 = alloc_skb(dev2->hard_header_len + skb->len + 15, GFP_ATOMIC);
 		
 		/*
 		 *	This is rare and since IP is tolerant of network failures
@@ -1250,13 +1250,6 @@
 	int err;
 #endif	
 
-	/*
-	 *	IP is layered, throw away the
-	 *	MAC addresses.
-	 */
-	 
-	skb_pull(skb,dev->hard_header_len);
-	
 	ip_statistics.IpInReceives++;
 
 	/*
@@ -1332,7 +1325,9 @@
 		int opt_space=4*(iph->ihl-5);
 		int opt_size;
 		unsigned char *opt_ptr=skb->h.raw+sizeof(struct iphdr);
-		
+	
+		skb->ip_summed=0;		/* Our free checksum is bogus for this case */
+			
 		while(opt_space>0)
 		{
 			if(*opt_ptr==IPOPT_NOOP)
@@ -1385,7 +1380,7 @@
 					if(opt_ptr[0]!=IPOPT_RR)
 					{
 						int t;
-						target_addr=*(long *)(&opt_ptr[opt_ptr[2]]);	/* Get hop */
+						target_addr=*(u32 *)(&opt_ptr[opt_ptr[2]]);	/* Get hop */
 						t=ip_chk_addr(target_addr);
 						if(t==IS_MULTICAST||t==IS_BROADCAST)
 						{
@@ -1395,7 +1390,7 @@
 							return -EINVAL;						
 						}
 					}
-					*(long *)(&opt_ptr[opt_ptr[2]])=skb->dev->pa_addr;	/* Record hop */
+					*(u32 *)(&opt_ptr[opt_ptr[2]])=skb->dev->pa_addr;	/* Record hop */
 					break;
 				case IPOPT_TIMESTAMP:
 				/*
@@ -1420,13 +1415,13 @@
 	 
 	if(iph->frag_off)
 	{
-		if (iph->frag_off & 0x0020)
+		if (iph->frag_off & htons(IP_MF))
 			is_frag|=1;
 		/*
 		 *	Last fragment ?
 		 */
 	
-		if (ntohs(iph->frag_off) & 0x1fff)
+		if (iph->frag_off & htons(IP_OFFSET))
 			is_frag|=2;
 	}
 	
@@ -2005,8 +2000,8 @@
 	if(err)
 		return err;
 
-	val = get_fs_long((unsigned long *)optval);
-	ucval=get_fs_byte((unsigned char *)optval);
+	val = get_user((int *) optval);
+	ucval=get_user((unsigned char *) optval);
 
 	if(level!=SOL_IP)
 		return -EOPNOTSUPP;
@@ -2277,7 +2272,7 @@
   			err=verify_area(VERIFY_WRITE, optval, len);
 		  	if(err)
   				return err;
-  			put_fs_long(len,(unsigned long *) optlen);
+  			put_user(len,(int *) optlen);
 			memcpy_tofs((void *)optval,sk->ip_mc_name, len);
 			return 0;
 #endif
@@ -2287,12 +2282,12 @@
 	err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
 	if(err)
 		return err;
-	put_fs_long(sizeof(int),(unsigned long *) optlen);
+	put_user(sizeof(int),(int *) optlen);
 
 	err=verify_area(VERIFY_WRITE, optval, sizeof(int));
 	if(err)
 		return err;
-	put_fs_long(val,(unsigned long *)optval);
+	put_user(val,(int *) optval);
 
 	return(0);
 }
@@ -2475,10 +2470,10 @@
 		char *data;
 
 		/*
-		 *	Get the memory we require.
+		 *	Get the memory we require with some space left for alignment.
 		 */
 
-		skb = sock_alloc_send_skb(sk, fraglen, 0, &error);
+		skb = sock_alloc_send_skb(sk, fraglen+15, 0, &error);
 		if (skb == NULL)
 			return(error);
 
@@ -2494,7 +2489,7 @@
 		skb->arp = 0;
 		skb->saddr = saddr;
 		skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr;
-		skb_reserve(skb,dev->hard_header_len);
+		skb_reserve(skb,(dev->hard_header_len+15)&~15);
 		data = skb_put(skb, fraglen-dev->hard_header_len);
 
 		/*
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v1.3.6/linux/net/ipv4/packet.c	Fri Jun 30 16:22:32 1995
+++ linux/net/ipv4/packet.c	Wed Jul  5 13:06:28 1995
@@ -28,6 +28,7 @@
  *					dubious gcc output. Can you read
  *					compiler: it said _VOLATILE_
  *	Richard Kooijman	:	Timestamp fixes.
+ *		Alan Cox	:	New buffers. Use sk->mac.raw
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -80,6 +81,12 @@
 	 */
 
 	sk = (struct sock *) pt->data;	
+	
+	/*
+	 *	Yank back the headers
+	 */
+	 
+	skb_push(skb,skb->data-skb->mac.raw);
 
 	/*
 	 *	The SOCK_PACKET socket receives _all_ frames.
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v1.3.6/linux/net/ipv4/rarp.c	Fri Jun 30 16:22:32 1995
+++ linux/net/ipv4/rarp.c	Wed Jul  5 13:06:28 1995
@@ -151,8 +151,6 @@
 	long sip,tip;
 	unsigned char *sha,*tha;            /* s for "source", t for "target" */
 	
-	skb_pull(skb,dev->hard_header_len);
-  
 /*
  *	If this test doesn't pass, it's not IP, or we should ignore it anyway
  */
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.6/linux/net/ipv4/tcp.c	Fri Jun 30 16:22:32 1995
+++ linux/net/ipv4/tcp.c	Thu Jul  6 14:04:43 1995
@@ -1030,22 +1030,20 @@
 			sk->inuse = 1;
 			amount = tcp_readable(sk);
 			release_sock(sk);
-			err=verify_area(VERIFY_WRITE,(void *)arg,
-						   sizeof(unsigned long));
+			err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(int));
 			if(err)
 				return err;
-			put_fs_long(amount,(unsigned long *)arg);
+			put_user(amount, (int *)arg);
 			return(0);
 		}
 		case SIOCATMARK:
 		{
 			int answ = sk->urg_data && sk->urg_seq == sk->copied_seq;
 
-			err = verify_area(VERIFY_WRITE,(void *) arg,
-						  sizeof(unsigned long));
+			err = verify_area(VERIFY_WRITE,(void *) arg, sizeof(int));
 			if (err)
 				return err;
-			put_fs_long(answ,(int *) arg);
+			put_user(answ,(int *) arg);
 			return(0);
 		}
 		case TIOCOUTQ:
@@ -1054,11 +1052,10 @@
 
 			if (sk->state == TCP_LISTEN) return(-EINVAL);
 			amount = sk->prot->wspace(sk);
-			err=verify_area(VERIFY_WRITE,(void *)arg,
-						   sizeof(unsigned long));
+			err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(int));
 			if(err)
 				return err;
-			put_fs_long(amount,(unsigned long *)arg);
+			put_user(amount, (int *)arg);
 			return(0);
 		}
 		default:
@@ -1075,10 +1072,9 @@
  */
  
 unsigned short tcp_check(struct tcphdr *th, int len,
-	  unsigned long saddr, unsigned long daddr)
+	  unsigned long saddr, unsigned long daddr, unsigned long base)
 {     
-	return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,
-		csum_partial((char *)th,len,0));
+	return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
 }
 
 
@@ -1087,7 +1083,8 @@
 		unsigned long daddr, int len, struct sock *sk)
 {
 	th->check = 0;
-	th->check = tcp_check(th, len, saddr, daddr);
+	th->check = tcp_check(th, len, saddr, daddr,
+		csum_partial((char *)th,len,0));
 	return;
 }
 
@@ -1602,7 +1599,7 @@
 			 *	NB: following must be mtu, because mss can be increased.
 			 *	mss is always <= mtu 
 			 */
-			skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header, 0, GFP_KERNEL);
+			skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header + 15, 0, GFP_KERNEL);
 			sk->inuse = 1;
 			send_tmp = skb;
 		} 
@@ -1612,7 +1609,7 @@
 			 *	We will release the socket in case we sleep here. 
 			 */
 			release_sock(sk);
-			skb = prot->wmalloc(sk, copy + prot->max_header , 0, GFP_KERNEL);
+			skb = prot->wmalloc(sk, copy + prot->max_header + 15 , 0, GFP_KERNEL);
   			sk->inuse = 1;
 		}
 
@@ -4476,7 +4473,6 @@
 	int syn_ok=0;
 	
 	tcp_statistics.TcpInSegs++;
-  
 	if(skb->pkt_type!=PACKET_HOST)
 	{
 	  	kfree_skb(skb,FREE_READ);
@@ -4519,7 +4515,13 @@
 		 *	Pull up the IP header.
 		 */
 		skb_pull(skb, skb->h.raw-skb->data);
-		if (tcp_check(th, len, saddr, daddr )) 
+		/*
+		 *	Try to use the device checksum if provided.
+		 */
+		if (
+			(skb->ip_summed && tcp_check(th, len, saddr, daddr, skb->csum ))||
+		    	(!skb->ip_summed && tcp_check(th, len, saddr, daddr, csum_partial((char *)th, len, 0)))
+		    )
 		{
 			skb->sk = NULL;
 			kfree_skb(skb,FREE_READ);
@@ -4924,7 +4926,7 @@
 		 
 	    	buff = sk->prot->wmalloc(sk, win_size + th->doff * 4 + 
 				     (iph->ihl << 2) +
-				     skb->dev->hard_header_len, 
+				     skb->dev->hard_header_len + 15, 
 				     1, GFP_ATOMIC);
 	    	if ( buff == NULL )
 	    		return;
@@ -5119,7 +5121,7 @@
   	if(err)
   		return err;
   	
-  	val = get_fs_long((unsigned long *)optval);
+  	val = get_user((int *)optval);
 
 	switch(optname)
 	{
@@ -5162,12 +5164,12 @@
 	err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
 	if(err)
   		return err;
-  	put_fs_long(sizeof(int),(unsigned long *) optlen);
+  	put_user(sizeof(int),(int *) optlen);
 
   	err=verify_area(VERIFY_WRITE, optval, sizeof(int));
   	if(err)
   		return err;
-  	put_fs_long(val,(unsigned long *)optval);
+  	put_user(val,(int *)optval);
 
   	return(0);
 }	
diff -u --recursive --new-file v1.3.6/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v1.3.6/linux/net/ipv4/udp.c	Fri Jun 30 16:22:33 1995
+++ linux/net/ipv4/udp.c	Thu Jul  6 13:22:05 1995
@@ -165,10 +165,9 @@
 }
 
 
-static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr)
+static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr, unsigned long base)
 {
-	return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP,
-				 csum_partial((char*)uh, len, 0)));
+	return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base));
 }
 
 struct udpfakehdr 
@@ -204,7 +203,7 @@
  		src = ufh->from;
 		dst = to+sizeof(struct udphdr);
 	}
-	ufh->wcheck = csum_partial_copyffs(src, dst, len, ufh->wcheck);
+	ufh->wcheck = csum_partial_copy_fromuser(src, dst, len, ufh->wcheck);
 	if (offset == 0) 
 	{
  		ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr),
@@ -243,7 +242,7 @@
  		src = ufh->from;
 		dst = to+sizeof(struct udphdr);
 	}
-	memcpy_fromfs(src,dst,len);
+	memcpy_fromfs(dst,src,len);
 	if (offset == 0) 
 		memcpy(to, ufh, sizeof(struct udphdr));
 }
@@ -542,7 +541,11 @@
 		return(0);
 	}
 
-	if (uh->check && udp_check(uh, len, saddr, daddr)) 
+	if (uh->check && (
+		( skb->ip_summed && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
+		( !skb->ip_summed && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
+		         )
+	   )
 	{
 		/* <mea@utu.fi> wants to know, who sent it, to
 		   go and stomp on the garbage sender... */
diff -u --recursive --new-file v1.3.6/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.6/linux/net/ipx/af_ipx.c	Fri Jun 30 16:22:34 1995
+++ linux/net/ipx/af_ipx.c	Wed Jul  5 13:06:28 1995
@@ -1635,12 +1635,6 @@
 	ipx_packet *ipx;
 	
 	
-	/*
-	 *	Throw away the MAC layer
-	 */
-	 
-	skb_pull(skb,dev->hard_header_len);
-	
 	ipx=(ipx_packet *)skb->h.raw;
 	
 	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) {
diff -u --recursive --new-file v1.3.6/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.6/linux/net/netrom/af_netrom.c	Fri Jun 30 16:22:34 1995
+++ linux/net/netrom/af_netrom.c	Wed Jul  5 13:06:28 1995
@@ -870,12 +870,12 @@
 	unsigned short frametype, window, timeout;
 
 	skb->sk = NULL;		/* Initially we don't know who its for */
-	
+
 	/*
 	 *	skb->data points to the netrom frame start
 	 */
-	 
-	src  = (ax25_address *)(skb->data);
+	
+	src  = (ax25_address *)(skb->data + 0);
 	dest = (ax25_address *)(skb->data + 7);
 
 	circuit_index = skb->data[15];
@@ -887,7 +887,8 @@
 	 * Check for an incoming IP over NET/ROM frame.
 	 */
 	 if ((frametype & 0x0F) == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
-	 	skb->h.raw = skb->data + 20;
+		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+	 	skb->h.raw = skb->data;
 
 		return nr_rx_ip(skb, dev);
 	 }
@@ -898,12 +899,15 @@
 	 * a Connect Request base it on their circuit ID.
 	 */
 	if (((frametype & 0x0F) != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id, SOCK_SEQPACKET)) != NULL) ||
-	    ((frametype & 0x0F) == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id, SOCK_SEQPACKET)) != NULL)) 
-	{
-		if((frametype & 0x0F) == NR_CONNACK && skb->len == 39)	/* ??? size check --FIXME-- */
+	    ((frametype & 0x0F) == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id, SOCK_SEQPACKET)) != NULL)) {
+		skb_pull(skb, NR_NETWORK_LEN);
+		skb->h.raw = skb->data + NR_TRANSPORT_LEN;
+
+		if ((frametype & 0x0F) == NR_CONNACK && skb->len == 7)
 			sk->nr->bpqext = 1;
 		else
 			sk->nr->bpqext = 0;
+
 		return nr_process_rx_frame(sk, skb);
 	}
 
@@ -917,7 +921,7 @@
 		return 0;
 	}
 
-	user   = (ax25_address *)(skb->data + 11);
+	user   = (ax25_address *)(skb->data + 21);
 	window = skb->data[20];
 
 	skb->sk             = make;
@@ -942,7 +946,7 @@
 
 	/* L4 timeout negotiation */
 	if (skb->len == 37) {
-		timeout = skb->data[53] * 256 + skb->data[52];
+		timeout = skb->data[36] * 256 + skb->data[35];
 		if (timeout * PR_SLOWHZ < make->nr->rtt * 2)
 			make->nr->rtt = (timeout * PR_SLOWHZ) / 2;
 		make->nr->bpqext = 1;
@@ -1018,7 +1022,7 @@
 	if (sk->debug)
 		printk("NET/ROM: sendto: building packet.\n");
 
-	size = len + 37;
+	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 2 + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 
 	if ((skb = sock_alloc_send_skb(sk, size, 0, &err)) == NULL)
 		return err;
@@ -1026,35 +1030,18 @@
 	skb->sk   = sk;
 	skb->free = 1;
 	skb->arp  = 1;
-	skb_reserve(skb,37);
+
+	skb_reserve(skb, size - len);
 	
 	/*
-	 *	Push down the NetROM header
+	 *	Push down the NET/ROM header
 	 */
-	 
-	asmptr = skb_push(skb,20);
+
+	asmptr = skb_push(skb, NR_TRANSPORT_LEN);
 
 	if (sk->debug)
 		printk("Building NET/ROM Header.\n");
 
-	/* Build a NET/ROM Network header */
-
-	*asmptr++ = AX25_P_NETROM;
-
-	memcpy(asmptr, &sk->nr->source_addr, sizeof(ax25_address));
-	asmptr[6] &= ~LAPB_C;
-	asmptr[6] &= ~LAPB_E;
-	asmptr[6] |= SSID_SPARE;
-	asmptr += 7;
-
-	memcpy(asmptr, &sax.sax25_call, sizeof(ax25_address));
-	asmptr[6] &= ~LAPB_C;
-	asmptr[6] |= LAPB_E;
-	asmptr[6] |= SSID_SPARE;
-	asmptr += 7;
-	
-	*asmptr++ = nr_default.ttl;
-
 	/* Build a NET/ROM Transport header */
 
 	*asmptr++ = sk->nr->your_index;
@@ -1067,11 +1054,12 @@
 		printk("Built header.\n");
 
 	/*
-	 *	Put the data on the end.
+	 *	Put the data on the end
 	 */
-	 
-	skb->h.raw = skb_put(skb,len);
-	asmptr=skb->h.raw;
+
+	skb->h.raw = skb_put(skb, len);
+
+	asmptr = skb->h.raw;
 	
 	if (sk->debug)
 		printk("NET/ROM: Appending user data\n");
@@ -1128,10 +1116,9 @@
 	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
 		return er;
 
-	/* Allow for the 20 byte netrom header */
-	copied = (size < skb->len-20) ? size : skb->len-20;
+	copied = (size < skb->len - NR_TRANSPORT_LEN) ? size : skb->len - NR_TRANSPORT_LEN;
 
-	skb_copy_datagram(skb, 20, ubuf, copied);
+	skb_copy_datagram(skb, 0, ubuf, copied);
 	
 	if (sax != NULL) {
 		struct sockaddr_ax25 addr;
@@ -1198,7 +1185,7 @@
 			struct sk_buff *skb;
 			/* These two are safe on a single CPU system as only user tasks fiddle here */
 			if ((skb = skb_peek(&sk->receive_queue)) != NULL)
-				amount = skb->len-20;
+				amount = skb->len - 20;
 			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
 				return err;
 			put_fs_long(amount, (unsigned long *)arg);
@@ -1351,7 +1338,7 @@
 {
 	sock_register(nr_proto_ops.family, &nr_proto_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk("G4KLX NET/ROM for Linux. Version 0.3 ALPHA for AX25 029 Linux 1.3.0\n");
+	printk("G4KLX NET/ROM for Linux. Version 0.3 ALPHA for AX25 030 Linux 1.3.0\n");
 
 	nr_default.quality    = NR_DEFAULT_QUAL;
 	nr_default.obs_count  = NR_DEFAULT_OBS;
diff -u --recursive --new-file v1.3.6/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v1.3.6/linux/net/netrom/nr_dev.c	Fri Jun 30 16:22:34 1995
+++ linux/net/netrom/nr_dev.c	Wed Jul  5 13:06:28 1995
@@ -46,59 +46,48 @@
 #include <net/netrom.h>
 
 /*
- * Only allow IP over NET/ROM frames through if the netrom device is up.
+ *	Only allow IP over NET/ROM frames through if the netrom device is up.
  */
 
 int nr_rx_ip(struct sk_buff *skb, struct device *dev)
 {
 	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
 
-	if (!dev->start) {
+	if (!dev->start) 
+	{
 		stats->rx_errors++;
 		return 0;
 	}
 
 	stats->rx_packets++;
-	skb->protocol=htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
+
 	/* Spoof incoming device */
-	skb->dev=dev;
+	skb->dev = dev;
 
-	ip_rcv(skb, dev, NULL);
+	skb->h.raw = skb->data;
+	ip_rcv(skb, skb->dev, NULL);
 
 	return 1;
 }
 
-/*
- * We can't handle ARP so put some identification characters into the ARP
- * packet so that the transmit routine can identify it, and throw it away.
- */
-
 static int nr_header(struct sk_buff *skb, struct device *dev, unsigned short type,
 	void *daddr, void *saddr, unsigned len)
 {
-	unsigned char *buff=skb_push(skb,37);
-	if (type == ETH_P_ARP) {
-		*buff++ = 0xFF;		/* Mark it */
-		*buff++ = 0xFE;
-		return 37;
-	}
+	unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
-	buff += 16;
-	
-	*buff++ = AX25_P_NETROM;
-	
 	memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len);
 	buff[6] &= ~LAPB_C;
 	buff[6] &= ~LAPB_E;
 	buff[6] |= SSID_SPARE;
-	buff += dev->addr_len;
+	buff    += AX25_ADDR_LEN;
 
 	if (daddr != NULL)
 		memcpy(buff, daddr, dev->addr_len);
 	buff[6] &= ~LAPB_C;
 	buff[6] |= LAPB_E;
 	buff[6] |= SSID_SPARE;
-	buff += dev->addr_len;
+	buff    += AX25_ADDR_LEN;
 
 	*buff++ = nr_default.ttl;
 
@@ -117,20 +106,35 @@
 static int nr_rebuild_header(void *buff, struct device *dev,
 	unsigned long raddr, struct sk_buff *skb)
 {
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
 	unsigned char *bp = (unsigned char *)buff;
 
-	if (arp_find(bp + 24, raddr, dev, dev->pa_addr, skb))
+	skb_device_unlock(skb);
+
+	if (!arp_query(bp + 7, raddr, ARPHRD_NETROM)) {
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
 		return 1;
+	}
 
-	bp[23] &= ~LAPB_C;
-	bp[23] &= ~LAPB_E;
-	bp[23] |= SSID_SPARE;
+	bp[6] &= ~LAPB_C;
+	bp[6] &= ~LAPB_E;
+	bp[6] |= SSID_SPARE;
+	bp    += AX25_ADDR_LEN;
 	
-	bp[30] &= ~LAPB_C;
-	bp[30] |= LAPB_E;
-	bp[30] |= SSID_SPARE;
+	bp[6] &= ~LAPB_C;
+	bp[6] |= LAPB_E;
+	bp[6] |= SSID_SPARE;
+
+	if (!nr_route_frame(skb, NULL)) {
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+		stats->tx_errors++;
+	}
 
-	return 0;
+	stats->tx_packets++;
+
+	return 1;
 }
 
 static int nr_set_mac_address(struct device *dev, void *addr)
@@ -159,7 +163,6 @@
 static int nr_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
-	struct sk_buff *skbn;
 
 	if (skb == NULL || dev == NULL)
 		return 0;
@@ -181,25 +184,9 @@
 
 	sti();
 
-	if (skb->data[0] != 0xFF && skb->data[1] != 0xFE) {
-		if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-			dev->tbusy = 0;
-			stats->tx_errors++;
-			return 1;
-		}
-
-		if (!nr_route_frame(skbn, NULL)) {
-			skbn->free = 1;
-			kfree_skb(skbn, FREE_WRITE);
-			dev->tbusy = 0;
-			stats->tx_errors++;
-			return 1;
-		}
-	}
-
 	dev_kfree_skb(skb, FREE_WRITE);
 
-	stats->tx_packets++;
+	stats->tx_errors++;
 
 	dev->tbusy = 0;
 
@@ -224,8 +211,8 @@
 	dev->stop		= nr_close;
 
 	dev->hard_header	= nr_header;
-	dev->hard_header_len	= 37;
-	dev->addr_len		= 7;
+	dev->hard_header_len	= AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 2 + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
+	dev->addr_len		= AX25_ADDR_LEN;
 	dev->type		= ARPHRD_NETROM;
 	dev->rebuild_header	= nr_rebuild_header;
 	dev->set_mac_address    = nr_set_mac_address;
diff -u --recursive --new-file v1.3.6/linux/net/netrom/nr_in.c linux/net/netrom/nr_in.c
--- v1.3.6/linux/net/netrom/nr_in.c	Fri Jun 30 16:22:34 1995
+++ linux/net/netrom/nr_in.c	Wed Jul  5 13:06:28 1995
@@ -58,9 +58,9 @@
 
 		case NR_CONNACK:
 			nr_calculate_rtt(sk);
-			sk->window         = skb->data[37];
-			sk->nr->your_index = skb->data[34];
-			sk->nr->your_id    = skb->data[35];
+			sk->window         = skb->data[5];
+			sk->nr->your_index = skb->data[2];
+			sk->nr->your_id    = skb->data[3];
 			sk->nr->t1timer    = 0;
 			sk->nr->t2timer    = 0;
 			sk->nr->t4timer    = 0;
@@ -135,8 +135,8 @@
 	unsigned short nr, ns;
 	int queued = 0;
 
-	nr = skb->data[18];
-	ns = skb->data[17];
+	nr = skb->data[3];
+	ns = skb->data[2];
 
 	switch (frametype) {
 
@@ -241,7 +241,7 @@
 			do {
 				save_vr = sk->nr->vr;
 				while ((skbn = skb_dequeue(&sk->nr->reseq_queue)) != NULL) {
-					ns = skbn->data[17];
+					ns = skbn->data[2];
 					if (ns == sk->nr->vr) {
 						if (sock_queue_rcv_skb(sk, skbn) == 0) {
 							sk->nr->vr = (sk->nr->vr + 1) % NR_MODULUS;
@@ -293,7 +293,7 @@
 
 	del_timer(&sk->timer);
 
-	frametype = skb->data[19];
+	frametype = skb->data[4];
 
 	switch (sk->nr->state)
 	{
diff -u --recursive --new-file v1.3.6/linux/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- v1.3.6/linux/net/netrom/nr_out.c	Fri Jun 30 16:22:34 1995
+++ linux/net/netrom/nr_out.c	Wed Jul  5 13:06:28 1995
@@ -56,15 +56,11 @@
  */
 static void nr_send_iframe(struct sock *sk, struct sk_buff *skb)
 {
-	unsigned char *dptr;
-
 	if (skb == NULL)
 		return;
 
-	dptr = skb->data + 17;
-	
-	*dptr++ = sk->nr->vs;
-	*dptr++ = sk->nr->vr;
+	skb->data[2] = sk->nr->vs;
+	skb->data[3] = sk->nr->vr;
 
 	nr_transmit_buffer(sk, skb);	
 }
@@ -151,24 +147,21 @@
 	unsigned char *dptr;
 
 	/*
-	 *	Add the protocol byte
+	 *	Add the protocol byte and network header.
 	 */
-	 
-	dptr = skb_push(skb,1);
+	dptr = skb_push(skb, NR_NETWORK_LEN);
 
-	*dptr++ = AX25_P_NETROM;
-	
 	memcpy(dptr, &sk->nr->source_addr, sizeof(ax25_address));
 	dptr[6] &= ~LAPB_C;
 	dptr[6] &= ~LAPB_E;
 	dptr[6] |= SSID_SPARE;
-	dptr += 7;
+	dptr += AX25_ADDR_LEN;
 
 	memcpy(dptr, &sk->nr->dest_addr,   sizeof(ax25_address));
 	dptr[6] &= ~LAPB_C;
 	dptr[6] |= LAPB_E;
 	dptr[6] |= SSID_SPARE;
-	dptr += 7;
+	dptr += AX25_ADDR_LEN;
 
 	*dptr++ = nr_default.ttl;
 
diff -u --recursive --new-file v1.3.6/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v1.3.6/linux/net/netrom/nr_route.c	Fri Jun 30 16:22:34 1995
+++ linux/net/netrom/nr_route.c	Wed Jul  5 13:06:28 1995
@@ -39,6 +39,7 @@
 #include <net/ax25.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <net/arp.h>
 #include <linux/if_arp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -61,7 +62,7 @@
  *	neighbour if it is new.
  */
 static int nr_add_node(ax25_address *nr, char *mnemonic, ax25_address *ax25,
-	struct device *dev, int quality, int obs_count)
+	ax25_digi *ax25_digi, struct device *dev, int quality, int obs_count)
 {
 	struct nr_node  *nr_node;
 	struct nr_neigh *nr_neigh;
@@ -86,11 +87,20 @@
 
 		memcpy(&nr_neigh->callsign, ax25, sizeof(ax25_address));
 
+		nr_neigh->digipeat= NULL;
 		nr_neigh->dev     = dev;
 		nr_neigh->quality = nr_default.quality;
 		nr_neigh->locked  = 0;
 		nr_neigh->count   = 0;
 		nr_neigh->number  = nr_neigh_no++;
+
+		if (ax25_digi != NULL) {
+			if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
+				kfree_s(nr_neigh, sizeof(*nr_neigh));
+				return -ENOMEM;
+			}
+			memcpy(nr_neigh->digipeat, ax25_digi, sizeof(*ax25_digi));
+		}
 			
 		save_flags(flags);
 		cli();
@@ -250,6 +260,8 @@
 	if ((s = nr_neigh_list) == nr_neigh) {
 		nr_neigh_list = nr_neigh->next;
 		restore_flags(flags);
+		if (nr_neigh->digipeat != NULL)
+			kfree_s(nr_neigh->digipeat, sizeof(ax25_digi));
 		kfree_s(nr_neigh, sizeof(struct nr_neigh));
 		return;
 	}
@@ -258,6 +270,8 @@
 		if (s->next == nr_neigh) {
 			s->next = nr_neigh->next;
 			restore_flags(flags);
+			if (nr_neigh->digipeat != NULL)
+				kfree_s(nr_neigh->digipeat, sizeof(ax25_digi));
 			kfree_s(nr_neigh, sizeof(struct nr_neigh));
 			return;
 		}
@@ -340,6 +354,7 @@
 
 	memcpy(&nr_neigh->callsign, callsign, sizeof(ax25_address));
 
+	nr_neigh->digipeat= NULL;
 	nr_neigh->dev     = dev;
 	nr_neigh->quality = quality;
 	nr_neigh->locked  = 1;
@@ -492,16 +507,22 @@
 
 /*
  *	Check that the device given is a valid AX.25 interface that is "up".
+ *	Or a valid ethernet interface with an AX.25 callsign binding.
  */
 static struct device *nr_ax25_dev_get(char *devname)
 {
 	struct device *dev;
+	ax25_address callsign;
 
 	if ((dev = dev_get(devname)) == NULL)
 		return NULL;
 
 	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
 		return dev;
+
+	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
+		if (arp_query((unsigned char *)&callsign, dev->pa_addr, ARPHRD_AX25))
+			return dev;
 	
 	return NULL;
 }
@@ -557,7 +578,7 @@
 					return nr_add_node(&nr_route.callsign,
 						nr_route.mnemonic,
 						&nr_route.neighbour,
-						dev, nr_route.quality,
+						NULL, dev, nr_route.quality,
 						nr_route.obs_count);
 				case NETROM_NEIGH:
 					return nr_add_neigh(&nr_route.callsign,
@@ -611,30 +632,28 @@
 }
 
 /*
- *	Route a frame to an appropriate AX.25 connection. A NULL dev means
- *	that the frame was generated internally.
+ *	Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
+ *	indicates an internally generated frame.
  */
-int nr_route_frame(struct sk_buff *skb, struct device *device)
+int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 {
-	ax25_address *ax25_src, *ax25_dest;
-	ax25_address *nr_src,   *nr_dest;
+	ax25_address *nr_src, *nr_dest;
 	struct nr_neigh *nr_neigh;
 	struct nr_node  *nr_node;
 	struct device *dev;
+	unsigned char *dptr;
 
-	ax25_dest = (ax25_address *)(skb->data + 1);
-	ax25_src  = (ax25_address *)(skb->data + 8);
-	nr_src    = (ax25_address *)(skb->data + 17);
-	nr_dest   = (ax25_address *)(skb->data + 24);
+	nr_src  = (ax25_address *)(skb->data + 0);
+	nr_dest = (ax25_address *)(skb->data + 7);
 
-	if (device != NULL)
-		nr_add_node(nr_src, "", ax25_src, device, 0, nr_default.obs_count);
+	if (ax25 != NULL)
+		nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, ax25->device, 0, nr_default.obs_count);
 
 	if ((dev = nr_dev_get(nr_dest)) != NULL)	/* Its for me */
 		return nr_rx_frame(skb, dev);
 
 	/* Its Time-To-Live has expired */
-	if (--skb->data[31] == 0)
+	if (--skb->data[14] == 0)
 		return 0;
 
 	for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
@@ -654,11 +673,10 @@
 	if ((dev = nr_dev_first()) == NULL)
 		return 0;
 
-/*	if (device != NULL)
-		skb->len += dev->hard_header_len;*/
+	dptr  = skb_push(skb, 1);
+	*dptr = AX25_P_NETROM;
 
-	skb_push(skb,17);
-	ax25_send_frame(skb, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->dev);
+	ax25_send_frame(skb, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
 
 	return 1;
 }
@@ -676,14 +694,14 @@
 	len += sprintf(buffer, "callsign  mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n");
 
 	for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) {
-		len += sprintf(buffer + len, "%-9s %-7s  %d %d ",
+		len += sprintf(buffer + len, "%-9s %-7s  %d %d",
 			ax2asc(&nr_node->callsign),
 			nr_node->mnemonic,
 			nr_node->which + 1,
 			nr_node->count);			
 
 		for (i = 0; i < nr_node->count; i++) {
-			len += sprintf(buffer + len, " %3d   %d %05d",
+			len += sprintf(buffer + len, "  %3d   %d %05d",
 				nr_node->routes[i].quality,
 				nr_node->routes[i].obs_count,
 				nr_node->routes[i].neighbour);
@@ -721,10 +739,10 @@
   
 	cli();
 
-	len += sprintf(buffer, "addr  callsign  dev qual lock count\n");
+	len += sprintf(buffer, "addr  callsign  dev  qual lock count\n");
 
 	for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
-		len += sprintf(buffer + len, "%05d %-9s %-3s  %3d    %d   %3d\n",
+		len += sprintf(buffer + len, "%05d %-9s %-4s  %3d    %d   %3d\n",
 			nr_neigh->number,
 			ax2asc(&nr_neigh->callsign),
 			nr_neigh->dev ? nr_neigh->dev->name : "???",
diff -u --recursive --new-file v1.3.6/linux/net/netrom/nr_subr.c linux/net/netrom/nr_subr.c
--- v1.3.6/linux/net/netrom/nr_subr.c	Fri Jun 30 16:22:34 1995
+++ linux/net/netrom/nr_subr.c	Wed Jul  5 13:06:28 1995
@@ -152,13 +152,20 @@
 	struct sk_buff *skb;
 	unsigned char  *dptr;
 	int len, timeout;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 2 + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 	
 	switch (frametype & 0x0F) {
-		case NR_CONNREQ: len = 54; break;
-		case NR_CONNACK: len = (sk->nr->bpqext) ? 39 : 38; break;
-		case NR_DISCREQ: len = 37; break;
-		case NR_DISCACK: len = 37; break;
-		case NR_INFOACK: len = 37; break;
+		case NR_CONNREQ:
+			len += 17;
+			break;
+		case NR_CONNACK:
+			len += (sk->nr->bpqext) ? 2 : 1;
+			break;
+		case NR_DISCREQ:
+		case NR_DISCACK:
+		case NR_INFOACK:
+			break;
 		default:
 			printk("nr_write_internal: invalid frame type %d\n", frametype);
 			return;
@@ -168,34 +175,34 @@
 		return;
 
 	/*
-	 *	Space for AX.25
+	 *	Space for AX.25 and NET/ROM network header
 	 */
-	skb_reserve(skb,17);
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 2 + NR_NETWORK_LEN);
 	
-	dptr = skb_put(skb,len-17);
+	dptr = skb_put(skb, skb_tailroom(skb));
 
 	switch (frametype & 0x0F) {
 
 		case NR_CONNREQ:
-			timeout = (sk->nr->rtt / PR_SLOWHZ) * 2;
-			*dptr++ = sk->nr->my_index;
-			*dptr++ = sk->nr->my_id;
-			*dptr++ = 0;
-			*dptr++ = 0;
-			*dptr++ = frametype;
-			*dptr++ = sk->window;
+			timeout  = (sk->nr->rtt / PR_SLOWHZ) * 2;
+			*dptr++  = sk->nr->my_index;
+			*dptr++  = sk->nr->my_id;
+			*dptr++  = 0;
+			*dptr++  = 0;
+			*dptr++  = frametype;
+			*dptr++  = sk->window;
 			memcpy(dptr, &sk->nr->user_addr, sizeof(ax25_address));
 			dptr[6] &= ~LAPB_C;
 			dptr[6] &= ~LAPB_E;
 			dptr[6] |= SSID_SPARE;
-			dptr += 7;
+			dptr    += AX25_ADDR_LEN;
 			memcpy(dptr, &sk->nr->source_addr, sizeof(ax25_address));
 			dptr[6] &= ~LAPB_C;
 			dptr[6] &= ~LAPB_E;
 			dptr[6] |= SSID_SPARE;
-			dptr += 7;
-			*dptr++ = timeout % 256;
-			*dptr++ = timeout / 256;
+			dptr    += AX25_ADDR_LEN;
+			*dptr++  = timeout % 256;
+			*dptr++  = timeout / 256;
 			break;
 
 		case NR_CONNACK:
@@ -239,31 +246,33 @@
 {
 	struct sk_buff *skbn;
 	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 2 + NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1;
 
-	if ((skbn = alloc_skb(38, GFP_ATOMIC)) == NULL)
+	if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL)
 		return;
 
-	skb_reserve(skbn,17);
-	dptr = skb_put(skbn,21);
+	skb_reserve(skbn, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 2);
 
-	*dptr++ = AX25_P_NETROM;
-	
-	memcpy(dptr, skb->data + 24, 7);
+	dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+
+	memcpy(dptr, skb->data + 7, AX25_ADDR_LEN);
 	dptr[6] &= ~LAPB_C;
 	dptr[6] &= ~LAPB_E;
 	dptr[6] |= SSID_SPARE;
-	dptr += 7;
+	dptr += AX25_ADDR_LEN;
 	
-	memcpy(dptr, skb->data + 17, 7);
+	memcpy(dptr, skb->data + 0, AX25_ADDR_LEN);
 	dptr[6] &= ~LAPB_C;
 	dptr[6] |= LAPB_E;
 	dptr[6] |= SSID_SPARE;
-	dptr += 7;
+	dptr += AX25_ADDR_LEN;
 
 	*dptr++ = nr_default.ttl;
 
-	*dptr++ = skb->data[32];
-	*dptr++ = skb->data[33];
+	*dptr++ = skb->data[15];
+	*dptr++ = skb->data[16];
 	*dptr++ = 0;
 	*dptr++ = 0;
 	*dptr++ = NR_CONNACK + NR_CHOKE_FLAG;
