*** original/Makefile	Mon May  9 00:52:33 1994
--- linux/Makefile	Mon May  9 14:15:44 1994
***************
*** 87,93 ****
  FILESYSTEMS	=fs/filesystems.a
  DRIVERS		=drivers/block/block.a \
  		 drivers/char/char.a \
- 		 drivers/net/net.a \
  		 ibcs/ibcs.o
  LIBS		=lib/lib.a
  SUBDIRS		=kernel drivers mm fs net ipc ibcs lib
--- 87,92 ----
*** original/config.in	Mon May  9 00:51:03 1994
--- linux/config.in	Tue May 10 19:40:19 1994
***************
*** 8,14 ****
  bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
  bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
  bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
- bool 'TCP/IP networking' CONFIG_INET y
  bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
  bool 'System V IPC' CONFIG_SYSVIPC y
  bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
--- 8,13 ----
***************
*** 49,89 ****
  bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
  fi
  
! comment 'Network device support'
  
! bool 'Network device support?' CONFIG_ETHERCARDS y
! if [ "$CONFIG_ETHERCARDS" = "n" ]; then
! 
! comment 'Skipping ethercard configuration options...'
! 
! else
! bool 'SLIP (serial line) support' CONFIG_SLIP n
! if [ "$CONFIG_SLIP" = "y" ]; then
!   bool ' CSLIP compressed headers' SL_COMPRESSED y
! #  bool ' SLIP debugging on' SL_DUMP y
! fi
! #bool 'PPP (point-to-point) support' CONFIG_PPP n
! bool 'PLIP (parallel port) support' CONFIG_PLIP n
! bool 'NE2000/NE1000 support' CONFIG_NE2000 n
! bool 'WD80*3 support' CONFIG_WD80x3 y
! bool 'SMC Ultra support' CONFIG_ULTRA n
! bool '3c501 support' CONFIG_EL1 n
! bool '3c503 support' CONFIG_EL2 n
! #bool '3c505 support' CONFIG_ELPLUS n
! #bool '3c507 support' CONFIG_EL16 n
! bool '3c509/3c579 support' CONFIG_EL3 n
! bool 'HP PCLAN support' CONFIG_HPLAN n
! bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
! bool 'AT1700 support' CONFIG_AT1700 n
! #bool 'Zenith Z-Note support' CONFIG_ZNET n
! #bool 'EtherExpress support' CONFIG_EEXPRESS n
! bool 'DEPCA support' CONFIG_DEPCA n
! #bool 'NI52EE support' CONFIG_NI52 n
! #bool 'NI65EE support' CONFIG_NI65 n
! #bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
! #bool 'Cabletron E21xx support (not recommended)' CONFIG_E21 n
! bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
! bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
  fi
  
  comment 'CD-ROM drivers'
--- 48,86 ----
  bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
  fi
  
! comment 'Networking section.'
  
! bool 'Do you want *any* kind of networking?' CONFIG_NET y
! if [ "$CONFIG_NET" = "y" ]; then
! bool 'UNIX domain sockets?' CONFIG_UNIX y
! bool 'INET (TCP/IP) domain sockets?' CONFIG_INET y
! #bool 'AX25 (Packet Radio) domain sockets?' CONFIG_AX25 y
! 
! comment 'Network device drivers.'
! 
! bool 'SLIP (serial line) support' CONFIG_SLIP y
! bool 'PPP (point-to-point) support' CONFIG_PPP y
! bool 'PLIP (parallel port) support' CONFIG_PLIP y
! #bool 'KISS (AX.25 Keep It Simple, Stupid) support' CONFIG_KISS y
! bool 'SMC Ultra support' CONFIG_ULTRA y
! bool 'WD80x3 support' CONFIG_WD80x3 y
! bool 'NEx000 support' CONFIG_NE2000 y
! bool '3Com 3C501 support' CONFIG_EL1 y
! bool '3Com 3C503 support' CONFIG_EL2 y
! bool '3Com 3C507 support' CONFIG_EL16 y
! bool '3Com 3C509/3C579 support' CONFIG_EL3 y
! bool 'HP PCLAN support' CONFIG_HPLAN y
! bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_AT1500 y
! bool 'AT1700 support' CONFIG_AT1700 y
! bool 'Zenith Z-Note support' CONFIG_ZNOTE y
! bool 'EtherExpress support' CONFIG_EEXPRESS y
! bool 'DEPCA support' CONFIG_DEPCA y
! #bool 'NI52** support' CONFIG_NI52 y
! #bool 'NI65** support' CONFIG_NI65 y
! #bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 y
! #bool 'Cabletron E21xx support (not recommended)' CONFIG_E21 y
! bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 y
! bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP y
  fi
  
  comment 'CD-ROM drivers'
*** original/drivers/Makefile	Wed Dec  1 13:44:16 1993
--- linux/drivers/Makefile	Mon May  9 13:12:52 1994
***************
*** 16,22 ****
  .c.o:
  	$(CC) $(CFLAGS) -c $<
  
! SUBDIRS	= block char net
  
  ifdef CONFIG_MATH_EMULATION
  SUBDIRS := $(SUBDIRS) FPU-emu
--- 16,22 ----
  .c.o:
  	$(CC) $(CFLAGS) -c $<
  
! SUBDIRS	= block char
  
  ifdef CONFIG_MATH_EMULATION
  SUBDIRS := $(SUBDIRS) FPU-emu
*** original/fs/nfs/sock.c	Tue Mar  1 07:03:41 1994
--- linux/fs/nfs/sock.c	Mon May  9 13:12:52 1994
***************
*** 14,20 ****
  #include <linux/fcntl.h>
  #include <asm/segment.h>
  #include <linux/in.h>
- #include <linux/net.h>
  
  
  extern struct socket *socki_lookup(struct inode *inode);
--- 14,19 ----
*** original/fs/proc/net.c	Mon May  9 00:51:09 1994
--- linux/fs/proc/net.c	Mon May  9 13:12:52 1994
***************
*** 15,23 ****
   *            quite a bit, modularized the code.
   * fvk 4/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
   *	      Renamed "route_get_info()" to "rt_get_info()" for consistency.
!  * Alan Cox (gw4pts@gw4pts.ampr.org) 4/94
!  *	      Dusted off the code and added IPX. Fixed the 4K limit.
   *
   *  proc net directory handling functions
   */
  #include <linux/autoconf.h>
--- 15,29 ----
   *            quite a bit, modularized the code.
   * fvk 4/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
   *	      Renamed "route_get_info()" to "rt_get_info()" for consistency.
!  * fvk 6/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
!  *	      Removed the "dev" entry and all support for it.
!  * fvk 7/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
!  *	      Renamed "rt_get_info" back to "route_get_info" for the new
!  *	      top-level router module.
!  * fpb 9/'93  dc6iq@insu1.etec.uni-karlsruhe.de (Fred Baumgarten)
!  *            rewrite to use multiple pages.
   *
+  *
   *  proc net directory handling functions
   */
  #include <linux/autoconf.h>
***************
*** 38,56 ****
  
  /* the get_*_info() functions are in the net code, and are configured
     in via the standard mechanism... */
! extern int unix_get_info(char *, char **, off_t, int);
  #ifdef CONFIG_INET
! extern int tcp_get_info(char *, char **, off_t, int);
! extern int udp_get_info(char *, char **, off_t, int);
! extern int raw_get_info(char *, char **, off_t, int);
! extern int arp_get_info(char *, char **, off_t, int);
! extern int dev_get_info(char *, char **, off_t, int);
! extern int rt_get_info(char *, char **, off_t, int);
  #endif /* CONFIG_INET */
- #ifdef CONFIG_IPX
- extern int ipx_get_info(char *, char **, off_t, int);
- extern int ipx_rt_get_info(char *, char **, off_t, int);
- #endif /* CONFIG_IPX */
  
  static struct file_operations proc_net_operations = {
  	NULL,			/* lseek - default */
--- 44,61 ----
  
  /* the get_*_info() functions are in the net code, and are configured
     in via the standard mechanism... */
! 
! #ifdef CONFIG_UNIX
! extern struct pagelist *unix_get_info(void);
! #endif /* CONFIG_UNIX */
! 
  #ifdef CONFIG_INET
! extern struct pagelist *tcp_get_info(void);
! extern struct pagelist *udp_get_info(void);
! extern struct pagelist *raw_get_info(void);
! extern struct pagelist *arp_get_info(void);
! extern struct pagelist *route_get_info(void);
  #endif /* CONFIG_INET */
  
  static struct file_operations proc_net_operations = {
  	NULL,			/* lseek - default */
***************
*** 89,107 ****
  static struct proc_dir_entry net_dir[] = {
  	{ 1,2,".." },
  	{ 8,1,"." },
! 	{ 128,4,"unix" }
  #ifdef CONFIG_INET
! 	,{ 129,3,"arp" },
  	{ 130,5,"route" },
! 	{ 131,3,"dev" },
! 	{ 132,3,"raw" },
! 	{ 133,3,"tcp" },
! 	{ 134,3,"udp" }
  #endif	/* CONFIG_INET */
- #ifdef CONFIG_IPX
- 	,{ 135,9,"ipx_route" },
- 	{ 136,3,"ipx" }
- #endif /* CONFIG_IPX */
  };
  
  #define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])))
--- 94,109 ----
  static struct proc_dir_entry net_dir[] = {
  	{ 1,2,".." },
  	{ 8,1,"." },
! #ifdef CONFIG_UNIX
! 	{ 128,4,"unix" },
! #endif
  #ifdef CONFIG_INET
! 	{ 129,3,"arp" },
  	{ 130,5,"route" },
! 	{ 131,3,"raw" },
! 	{ 132,3,"tcp" },
! 	{ 133,3,"udp" }
  #endif	/* CONFIG_INET */
  };
  
  #define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])))
***************
*** 163,248 ****
  }
  
  
- #define PROC_BLOCK_SIZE	(3*1024)		/* 4K page size but our output routines use some slack for overruns */
- 
  static int proc_readnet(struct inode * inode, struct file * file,
  			char * buf, int count)
  {
! 	char * page;
! 	int length;
  	unsigned int ino;
! 	int bytes=count;
! 	int thistime;
! 	int copied=0;
! 	char *start;
  
  	if (count < 0)
  		return -EINVAL;
- 	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
- 		return -ENOMEM;
  	ino = inode->i_ino;
! 
! 	while(bytes>0)
! 	{
! 		thistime=bytes;
! 		if(bytes>PROC_BLOCK_SIZE)
! 			thistime=PROC_BLOCK_SIZE;
! 
! 		switch (ino) 
! 		{
! 			case 128:
! 				length = unix_get_info(page,&start,file->f_pos,thistime);
! 				break;
  #ifdef CONFIG_INET
! 			case 129:
! 				length = arp_get_info(page,&start,file->f_pos,thistime);
! 				break;
! 			case 130:
! 				length = rt_get_info(page,&start,file->f_pos,thistime);
! 				break;
! 			case 131:
! 				length = dev_get_info(page,&start,file->f_pos,thistime);
! 				break;
! 			case 132:
! 				length = raw_get_info(page,&start,file->f_pos,thistime);
! 				break;
! 			case 133:
! 				length = tcp_get_info(page,&start,file->f_pos,thistime);
! 				break;
! 			case 134:
! 				length = udp_get_info(page,&start,file->f_pos,thistime);
! 				break;
  #endif /* CONFIG_INET */
! #ifdef CONFIG_IPX
! 			case 135:
! 				length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
! 				break;
! 			case 136:
! 				length = ipx_get_info(page,&start,file->f_pos,thistime);
! 				break;
! #endif /* CONFIG_IPX */
! 			default:
! 				free_page((unsigned long) page);
! 				return -EBADF;
! 		}
! 		
! 		/*
!  		 *	We have been given a non page aligned block of
! 		 *	the data we asked for + a bit. We have been given
!  		 *	the start pointer and we know the length.. 
! 		 */
! 
! 		/*
!  		 *	Copy the bytes
! 		 */
! 		memcpy_tofs(buf+copied, start, length);
! 		file->f_pos+=length;	/* Move down the file */
! 		bytes-=length;
! 		copied+=length;
! 		if(length<thistime)
! 			break;	/* End of file */
  	}
! 	free_page((unsigned long) page);
! 	return copied;
! 
  }
--- 165,230 ----
  }
  
  
  static int proc_readnet(struct inode * inode, struct file * file,
  			char * buf, int count)
  {
! 	int cpos;
! 	int end;
  	unsigned int ino;
! 	struct pagelist *fp, *pp;
  
  	if (count < 0)
  		return -EINVAL;
  	ino = inode->i_ino;
! 	switch (ino) {
! #ifdef CONFIG_UNIX
! 		case 128:
! 			fp = unix_get_info();
! 			break;
! #endif /* CONFIG_UNIX */
  #ifdef CONFIG_INET
! 		case 129:
! 			fp = arp_get_info();
! 			break;
! 		case 130:
! 			fp = route_get_info();
! 			break;
! 		case 131:
! 			fp = raw_get_info();
! 			break;
! 		case 132:
! 			fp = tcp_get_info();
! 			break;
! 		case 133:
! 			fp = udp_get_info();
! 			break;
  #endif /* CONFIG_INET */
! 		default:
! 			return -EBADF;
  	}
! 	if (!fp) {
! 		return -ENOMEM;
! 	}
! 	pp = fp;
! 	cpos = file->f_pos;
! 	while (cpos >= fp->length) {
! 		cpos -= fp->length;
! 		free_page((unsigned long) fp->page);
! 		pp = fp->next;
! 		free_page((unsigned long) fp);
! 		fp = pp;
! 		if (!fp) return 0;
! 	}
! 	if (count + cpos > fp->length)
! 		count = fp->length - cpos;
! 	end = count + file->f_pos;
! 	memcpy_tofs(buf, fp->page + cpos, count);
! 	while (fp) {
! 		free_page((unsigned long) fp->page);
! 		pp = fp->next;
! 		free_page((unsigned long) fp);
! 		fp = pp;
! 	}
! 	file->f_pos = end;
! 	return count;
  }
*** original/include/asm/system.h	Wed Dec  1 13:44:15 1993
--- linux/include/asm/system.h	Mon May  9 13:12:52 1994
***************
*** 18,25 ****
--- 18,30 ----
  	"mov %%ax,%%gs" \
  	: /* no outputs */ :"i" (USER_DS), "i" (USER_CS):"ax")
  
+ #ifdef NOTDEF
  #define sti() __asm__ __volatile__ ("sti": : :"memory")
  #define cli() __asm__ __volatile__ ("cli": : :"memory")
+ #else
+ void sti(void);
+ void cli(void);
+ #endif
  #define nop() __asm__ __volatile__ ("nop")
  
  /*
*** original/include/linux/ddi.h	Wed Dec  1 13:44:15 1993
--- linux/include/linux/ddi.h	Mon May  9 13:12:52 1994
***************
*** 1,10 ****
  /*
!  * ddi.h	Define the structure for linking in I/O drivers into the
!  *		operating system kernel.  This method is currently only
!  *		used by NET layer drivers, but it will be expanded into
!  *		a link methos for ALL kernel-resident device drivers.
   *
!  * Version:	@(#)ddi.h	1.0.2	04/22/93
   *
   * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
   */
--- 1,12 ----
  /*
!  * DDI		The Device Driver Interface (DDI) standard is a protocol
!  *		defined to allow for simple installation and configuration
!  *		of kernel-based device drivers.
!  *		This method is currently only used by NET layer drivers,
!  *		but it will eventually expand into a method for ALL device
!  *		drivers that reside in the kernel.
   *
!  * Version:	@(#)ddi.h	1.1.0	12/17/93
   *
   * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
   */
***************
*** 12,79 ****
  #define _LINUX_DDI_H
  
  
! /* DDI control block flags. */
! #define DDI_FREADY	0x10000000	/* device is initialized	*/
! #define DDI_FPRESENT	0x20000000	/* device hardware is present	*/
! #define DDI_FBLKDEV	0x00000001	/* device has a BLK spec. file	*/
! #define DDI_FCHRDEV	0x00000002	/* device has a CHR spec. file	*/
! 
! /* Various constants. */
! #define DDI_MAXNAME	16		/* length of a DDI ID string	*/
! 
! 
! /* This structure is used to set up a DDI driver. */
! struct ddconf {
!   int		ioaddr;			/* main I/O (port) address	*/
!   int		ioaux;			/* auxiliary I/O (HD, AST)	*/
    int		irq;			/* IRQ channel			*/
    int		dma;			/* DMA channel to use		*/
    unsigned long	memsize;		/* size of onboard memory	*/
    unsigned long	memaddr;		/* base address of memory	*/
! };
! 
! 
! /* The DDI device control block. */
! struct ddi_device {
!   char		*title;			/* title of the driver		*/
!   char		name[DDI_MAXNAME];	/* unit name of the I/O driver	*/
!   short int	unit;			/* unit number of this driver	*/
!   short int	nunits;			/* number of units in driver	*/
!   int		(*init)(struct ddi_device *);	/* initialization func		*/
!   int		(*handler)(int, ...);	/* command handler		*/
!   short	int	major;			/* driver major dev number	*/
!   short	int	minor;			/* driver minor dev number	*/
!   unsigned long	flags;			/* various flags		*/
!   struct ddconf config;			/* driver HW setup		*/
! };
! 
! 
! /* This structure is used to set up networking protocols. */
! struct ddi_proto {
!   char		*name;			/* protocol name		*/
!   void		(*init)(struct ddi_proto *);	/* initialization func	*/
  };
  
  
! /* This structure is used to link a STREAMS interface. */
! struct iflink {
!   char		id[DDI_MAXNAME];	/* DDI ID string		*/
!   char		stream[DDI_MAXNAME];	/* STREAMS interface name	*/
!   int		family;			/* address (protocol) family	*/
!   unsigned int	flags;			/* any flags needed (unused)	*/
! };
! 
! 
! /* DDI control requests. */
  #define DDIOCSDBG	0x9000		/* set DDI debug level		*/
  #define DDIOCGNAME	0x9001		/* get DDI ID name		*/
  #define DDIOCGCONF	0x9002		/* get DDI HW config		*/
  #define DDIOCSCONF	0x9003		/* set DDI HW config		*/
  
  
! /* DDI global functions. */
! extern void			ddi_init(void);
! extern struct ddi_device	*ddi_map(const char *id);
  
  
  #endif	/* _LINUX_DDI_H */
--- 14,51 ----
  #define _LINUX_DDI_H
  
  
! /*
!  * The DDI device control block. 
!  * This structure is used to set up a DDI driver.
!  */
! struct ddi {
!   char		*name;			/* title of the driver		*/
!   short int	nunits;			/* number of units in driver	*/
!   short	int	major;			/* driver major dev number	*/
!   short	int	minor;			/* driver minor dev number	*/
!   int		io;			/* main I/O (port) address	*/
!   int		auxio;			/* auxiliary I/O (HD, AST)	*/
    int		irq;			/* IRQ channel			*/
+   int		auxirq;			/* auxiliary IRQ (SoundBlaster)	*/
    int		dma;			/* DMA channel to use		*/
    unsigned long	memsize;		/* size of onboard memory	*/
    unsigned long	memaddr;		/* base address of memory	*/
!   int		(*init)(struct ddi *);	/* initialization function	*/
  };
  
  
! /* DDI control requests. These should be merged with the SIOC calls. */
  #define DDIOCSDBG	0x9000		/* set DDI debug level		*/
  #define DDIOCGNAME	0x9001		/* get DDI ID name		*/
  #define DDIOCGCONF	0x9002		/* get DDI HW config		*/
  #define DDIOCSCONF	0x9003		/* set DDI HW config		*/
  
  
! #define DDI_IOAUTO	0xFFFF		/* "auto-probe" pseudo address	*/
! #define DDI_IOANY	0x0000		/* "user-conf" pseudo address	*/
! 
! 
! extern	void	ddi_init(void);
  
  
  #endif	/* _LINUX_DDI_H */
*** original/include/linux/fs.h	Mon May  9 00:52:14 1994
--- linux/include/linux/fs.h	Mon May  9 13:12:53 1994
***************
*** 12,18 ****
  #include <linux/types.h>
  #include <linux/dirent.h>
  #include <linux/vfs.h>
- #include <linux/net.h>
  
  /*
   * It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix
--- 12,17 ----
***************
*** 320,325 ****
--- 319,326 ----
  };
  
  #ifdef __KERNEL__
+ 
+ #include <linux/socket.h>
  
  extern int register_filesystem(struct file_system_type *);
  extern int unregister_filesystem(struct file_system_type *);
*** original/include/linux/if.h	Mon May  9 00:51:09 1994
--- linux/include/linux/if.h	Mon May  9 13:12:53 1994
***************
*** 1,16 ****
  /*
!  * INET		An implementation of the TCP/IP protocol suite for the LINUX
!  *		operating system.  INET is implemented using the  BSD Socket
!  *		interface as the means of communication with the user level.
   *
!  *		Global definitions for the INET interface module.
   *
!  * Version:	@(#)if.h	1.0.2	04/18/93
   *
!  * Authors:	Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
!  *		Ross Biro, <bir7@leland.Stanford.Edu>
!  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *
   *		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
--- 1,17 ----
  /*
!  * NET		An implementation of the SOCKET network access protocol.
   *
!  *		Global definitions for the NET interface module.
   *
!  * Version:	@(#)if.h	1.2.1	04/05/94
   *
!  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
!  *		release 4.3-RENO system, Copyright UCB 1982-1988
   *
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
   *		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
***************
*** 21,27 ****
--- 22,34 ----
  
  #include <linux/types.h>		/* for "caddr_t" et al		*/
  #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
+ #include <linux/skbuff.h>		/* for "struct sk_buff"		*/
+ #include <linux/fs.h>			/* for "struct inode/file"	*/
  
+ /* For future expansion when we will have different priorities. */
+ #define MAX_HEADER	14		/* FIXME: to be zapped		*/
+ #define IFF_MAX_PRIO	8		/* number of prio queues	*/
+ 
  /* Standard interface flags. */
  #define	IFF_UP		0x1		/* interface is up		*/
  #define	IFF_BROADCAST	0x2		/* broadcast address valid	*/
***************
*** 32,43 ****
  #define	IFF_RUNNING	0x40		/* resources allocated		*/
  #define	IFF_NOARP	0x80		/* no ARP protocol		*/
  
! /* These are not yet used: */
  #define	IFF_PROMISC	0x100		/* recve all packets		*/
  #define	IFF_ALLMULTI	0x200		/* recve all multicast packets	*/
  
  
  /*
   * The ifaddr structure contains information about one address
   * of an interface.  They are maintained by the different address
   * families, are allocated and attached when an address is set,
--- 39,124 ----
  #define	IFF_RUNNING	0x40		/* resources allocated		*/
  #define	IFF_NOARP	0x80		/* no ARP protocol		*/
  
! /* Driver receive modes. */
  #define	IFF_PROMISC	0x100		/* recve all packets		*/
  #define	IFF_ALLMULTI	0x200		/* recve all multicast packets	*/
  
  
  /*
+  * This structure defines a NET interface.  Its layout currently is
+  * a mess, but it is only a matter of time when the whole thing will
+  * be cleaned up.  Its primary goal is to be compatible with BSD UNIX
+  * as much as possible.
+  */
+ struct ifnet {
+   char			*if_name;	/* name, e.g. ``en'' or ``lo''	*/
+ 
+   unsigned short	if_flags;	/* interface flags		*/
+   unsigned short	if_unit;	/* sub-unit for device driver	*/
+   unsigned short	if_metric;	/* routing metric (not used)	*/
+   unsigned short	if_mtu;		/* interface MTU value		*/
+   unsigned long		if_rxtimer;	/* time of last receive		*/
+   unsigned long		if_txtimer;	/* time of last transmit	*/
+   unsigned char		if_tbusy;	/* "transmitter busy" flag	*/
+ 
+   /* Interface hardware address info. */
+   struct ifaddr		*if_macaddr;	/* hardware (MAC) address	*/
+   unsigned short	if_macalen;	/* hardware address length	*/
+   unsigned short	if_machlen;	/* hardware header length	*/
+ 
+   /* Interface protocol address info. */
+   struct ifaddr		*if_addr;	/* list with addrs for this i/f	*/
+   unsigned short	if_alen;	/* protocol address length	*/
+ 
+   /* Pointer to the interface buffers. */
+   int			if_qlen;
+   struct ifqueue	*if_qhead[IFF_MAX_PRIO];
+ 
+   /* Interface statistics. */
+   struct dev_stats	*if_stats;
+ #define			ifs_ipackets	if_stats->rx_packets
+ #define			ifs_opackets	if_stats->tx_packets
+ #define			ifs_ierrors	if_stats->rx_errors
+ #define			ifs_oerrors	if_stats->tx_errors
+ #define			ifs_collisions	if_stats->tx_collision
+ 
+   /* Pointers to interface service routines. */
+   int			(*open) (struct ifnet *);
+   int			(*close) (struct ifnet *);
+   int			(*ioctl) (struct inode *, struct file *,
+ 				  unsigned int, unsigned long);
+   int			(*recv) (struct ifnet *, struct sk_buff *, int len);
+   void			(*send) (struct ifnet *, struct sk_buff *,
+ 				 struct sockaddr *target, int priority);
+   int			(*xmit) (struct ifnet *, struct sk_buff *);
+   int			(*mac1) (struct ifnet *, struct sk_buff *, int type,
+ 				 struct sockaddr *src, struct sockaddr *dst);
+   void			(*mac2) (struct ifnet *, struct sk_buff *,
+ 				 struct sockaddr *);
+   struct sockaddr	*(*mac_aget) (struct ifnet *);
+   int			(*mac_aset) (struct ifnet *, struct sockaddr *);
+   struct sockaddr	*(*mac_bget) (struct ifnet *);
+   int			(*mac_bset) (struct ifnet *, struct sockaddr *);
+   char			*(*mac_print) (unsigned char *);
+   char			*(*mac_sprint) (struct sockaddr *);
+   int			(*set_multi) (struct ifnet *, int mode, void *args);
+ 
+   /* Next device. */
+   struct ifnet		*if_next;	/* next device in chain		*/
+ };
+ 
+ 
+ /* Define the interface output queue. */
+ struct ifqueue {
+   struct ifnet		*dev;		/* back pointer to device	*/
+   int			len;		/* length of packet		*/
+   struct sk_buff	*skb;		/* pointer to data		*/
+   int			retries;	/* how often still retry sending*/
+   struct ifqueue	*next;		/* pointer to next entry	*/
+ };
+ 
+ 
+ /*
   * The ifaddr structure contains information about one address
   * of an interface.  They are maintained by the different address
   * families, are allocated and attached when an address is set,
***************
*** 46,56 ****
   */
  struct ifaddr {
    struct sockaddr	ifa_addr;	/* address of interface		*/
    union {
  	struct sockaddr	ifu_broadaddr;
  	struct sockaddr	ifu_dstaddr;
    } ifa_ifu;
!   struct iface		*ifa_ifp;	/* back-pointer to interface	*/
    struct ifaddr		*ifa_next;	/* next address for interface	*/
  };
  #define	ifa_broadaddr	ifa_ifu.ifu_broadaddr	/* broadcast address	*/
--- 127,138 ----
   */
  struct ifaddr {
    struct sockaddr	ifa_addr;	/* address of interface		*/
+   struct sockaddr	ifa_mask;	/* network mask of interface	*/
    union {
  	struct sockaddr	ifu_broadaddr;
  	struct sockaddr	ifu_dstaddr;
    } ifa_ifu;
!   struct ifnet		*ifa_ifp;	/* back-pointer to interface	*/
    struct ifaddr		*ifa_next;	/* next address for interface	*/
  };
  #define	ifa_broadaddr	ifa_ifu.ifu_broadaddr	/* broadcast address	*/
***************
*** 63,99 ****
   * remainder may be interface specific.
   */
  struct ifreq {
- #define IFHWADDRLEN	6
  #define	IFNAMSIZ	16
! 	union
! 	{
! 		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
! 		char	ifrn_hwaddr[IFHWADDRLEN];	/* Obsolete */
! 	} ifr_ifrn;
! 	
  	union {
  		struct	sockaddr ifru_addr;
  		struct	sockaddr ifru_dstaddr;
  		struct	sockaddr ifru_broadaddr;
  		struct	sockaddr ifru_netmask;
! 		struct  sockaddr ifru_hwaddr;
  		short	ifru_flags;
  		int	ifru_metric;
  		int	ifru_mtu;
  		caddr_t	ifru_data;
  	} ifr_ifru;
  };
- 
- #define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
- #define old_ifr_hwaddr	ifr_ifrn.ifrn_hwaddr	/* interface hardware   */
- #define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
  #define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
  #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
  #define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
  #define	ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
  #define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
  #define	ifr_metric	ifr_ifru.ifru_metric	/* metric		*/
  #define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
  #define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
  
  /*
--- 145,174 ----
   * remainder may be interface specific.
   */
  struct ifreq {
  #define	IFNAMSIZ	16
! 	char	ifr_name[IFNAMSIZ];		/* if name, e.g. "en0" */
  	union {
  		struct	sockaddr ifru_addr;
  		struct	sockaddr ifru_dstaddr;
  		struct	sockaddr ifru_broadaddr;
  		struct	sockaddr ifru_netmask;
! 		struct	sockaddr ifru_hwaddr;
  		short	ifru_flags;
  		int	ifru_metric;
  		int	ifru_mtu;
+ 		int	ifru_encap;
  		caddr_t	ifru_data;
  	} ifr_ifru;
  };
  #define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
  #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
  #define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
  #define	ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
+ #define	ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* hardware address	*/
  #define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
  #define	ifr_metric	ifr_ifru.ifru_metric	/* metric		*/
  #define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
+ #define	ifr_encap	ifr_ifru.ifru_encap	/* link encap type	*/
  #define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
  
  /*
***************
*** 112,120 ****
  #define	ifc_buf	ifc_ifcu.ifcu_buf		/* buffer address	*/
  #define	ifc_req	ifc_ifcu.ifcu_req		/* array of structures	*/
  
  
  /* BSD UNIX expects to find these here, so here we go: */
  #include <linux/if_arp.h>
  #include <linux/route.h>
  
! #endif /* _NET_IF_H */
--- 187,266 ----
  #define	ifc_buf	ifc_ifcu.ifcu_buf		/* buffer address	*/
  #define	ifc_req	ifc_ifcu.ifcu_req		/* array of structures	*/
  
+ /* Statistics collection data. */
+ struct dev_stats {
+   unsigned long	rx_packets,		/* total packets received	*/
+ 		rx_errors,		/* bad packets received		*/
+ 		rx_dropped,		/* no space in linux buffers	*/
+ 		rx_multicast,		/* multicast packets received	*/
+ 		rx_broadcast,		/* multicast packets sent	*/
+ 		rx_soft,		/* soft receive errors		*/
+ 		rx_length,		/* packet length error		*/
+ 		rx_overrun,		/* receiver ring buff overflow	*/
+ 		rx_crc,			/* recved pkt with crc error	*/
+ 		rx_frame,		/* recv'd frame alignment error */
+ 		rx_fifo,		/* recv'r fifo overrun		*/
+ 		rx_missed,		/* receiver missed packet	*/
+ 
+ 		tx_packets,		/* total packets transmitted	*/
+ 		tx_errors,		/* packet transmit problems	*/
+ 		tx_dropped,		/* no space available in linux	*/
+ 		tx_multicast,		/* broadcast packets received	*/
+ 		tx_broadcast,		/* broadcast packets sent	*/
+ 		tx_soft,		/* soft transmit errors		*/
+ 		tx_aborted,		/* transmit aborted		*/
+ 		tx_overrun,		/* transmitter overruns		*/
+ 		tx_carrier,		/* carrier sense lost (ETHER)	*/
+ 		tx_fifo,		/* FIFO overrun			*/
+ 		tx_heartbeat,		/* hartbeat failure (ETHER)	*/
+ 		tx_collision,		/* collided packets (Ethernet)	*/
+ 		tx_window;		/* ??				*/
+ };
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ struct packet_type {
+   char			name[32];
+   unsigned short	type;
+   unsigned short	copy;
+   void			(*handler) (struct ifnet *, struct sk_buff *,
+ 				    struct packet_type *);
+   void			*data;
+   struct packet_type	*next;
+ };
+ 
  
+ extern struct ifnet	*dev_base;		/* sniff... */
+ 
+ 
+ /* Protocol ID management. */
+ extern int		net_add_packet(struct packet_type *);
+ extern void		net_del_packet(struct packet_type *);
+ 
+ /* Interface management. */
+ extern struct ifnet	*dev_create(char *name);
+ extern int		dev_free(struct ifnet *dev);
+ extern struct ifnet	*dev_get_name(char *name);
+ extern struct ifaddr	*dev_get_addr(struct ifnet *dev, int af);
+ extern struct ifaddr	*dev_add_addr(struct ifnet *dev, int af);
+ extern int		dev_open(struct ifnet *);
+ extern int		dev_close(struct ifnet *);
+ 
+ /* Packet I/O service functions. */
+ extern int		dev_rint(struct ifnet *, struct sk_buff *, int len);
+ extern void		dev_tint(struct ifnet *);
+ extern void		dev_queue(struct ifnet *, struct sk_buff *,
+ 				  struct sockaddr *target, int pri);
+ extern void		dev_delque(struct sk_buff *);
+ 
+ 
+ #else	/* __KERNEL__ */
+ 
  /* BSD UNIX expects to find these here, so here we go: */
  #include <linux/if_arp.h>
  #include <linux/route.h>
  
! #endif	/* __KERNEL__ */
! 
! #endif /* _LINUX_IF_H */
*** original/include/linux/if_arp.h	Mon May  9 00:51:09 1994
--- linux/include/linux/if_arp.h	Mon May  9 13:12:53 1994
***************
*** 1,17 ****
  /*
!  * INET		An implementation of the TCP/IP protocol suite for the LINUX
!  *		operating system.  INET is implemented using the  BSD Socket
!  *		interface as the means of communication with the user level.
   *
   *		Global definitions for the ARP (RFC 826) protocol.
   *
!  * Version:	@(#)if_arp.h	1.0.1	04/16/93
   *
!  * Authors:	Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
!  *		Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source.
!  *		Ross Biro, <bir7@leland.Stanford.Edu>
!  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *
   *		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
--- 1,22 ----
  /*
!  * NET		An implementation of the BSD Socket Interface as the means of
!  *		communication with the user level for the Linux operating
!  *		system.
   *
   *		Global definitions for the ARP (RFC 826) protocol.
   *
!  * Version:	@(#)if_arp.h	1.2.1	04/02/94
   *
!  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
!  *		release 4.3-RENO system,  Copyright UCB 1986-1988
   *
+  *		Some definitions adopted from KA9Q/NOS, Copyrighted by
+  *		Phil Karn, KA9Q
+  *
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
   *		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
***************
*** 21,27 ****
  #define _LINUX_IF_ARP_H
  
  /* ARP protocol HARDWARE identifiers. */
- #define ARPHRD_NETROM	0		/* from KA9Q: NET/ROM pseudo	*/
  #define ARPHRD_ETHER 	1		/* Ethernet 10Mbps		*/
  #define	ARPHRD_EETHER	2		/* Experimental Ethernet	*/
  #define	ARPHRD_AX25	3		/* AX.25 Level 2		*/
--- 26,31 ----
***************
*** 38,43 ****
--- 42,74 ----
  #define	ARPOP_RREPLY	4		/* RARP reply			*/
  
  
+ /*
+  * Address Resolution Protocol.
+  *
+  * See RFC 826 for protocol description.  ARP packets are variable
+  * in size; the arphdr structure defines the fixed-length portion.
+  * Protocol type values are the same as those for 10 Mb/s Ethernet.
+  * It is followed by the variable-sized fields ar_sha, arp_spa,
+  * arp_tha and arp_tpa in that order, according to the lengths
+  * specified.  Field names used correspond to RFC 826.
+  */
+ struct arphdr {
+   unsigned short	ar_hrd;		/* format of hardware address	*/
+   unsigned short	ar_pro;		/* format of protocol address	*/
+   unsigned char		ar_hln;		/* length of hardware address	*/
+   unsigned char		ar_pln;		/* length of protocol address	*/
+   unsigned short	ar_op;		/* ARP opcode (command)		*/
+ 
+   /* The rest is variable in size, according to the sizes above. */
+ #if 0
+   unsigned char		ar_sha[];	/* sender hardware address	*/
+   unsigned char		ar_spa[];	/* sender protocol address	*/
+   unsigned char		ar_tha[];	/* target hardware address	*/
+   unsigned char		ar_tpa[];	/* target protocol address	*/
+ #endif	/* not actually included! */
+ };
+ 
+ 
  /* ARP ioctl request. */
  struct arpreq {
    struct sockaddr	arp_pa;		/* protocol address		*/
***************
*** 46,55 ****
--- 77,136 ----
  };
  
  /* ARP Flag values. */
+ #define	ATF_INUSE	0x01		/* entry in use			*/
  #define ATF_COM		0x02		/* completed entry (ha valid)	*/
  #define	ATF_PERM	0x04		/* permanent entry		*/
  #define	ATF_PUBL	0x08		/* publish entry		*/
  #define	ATF_USETRAILERS	0x10		/* has requested trailers	*/
+ #define ATF_RARP	0x20		/* entry can be used for RARP	*/
+ #define ATF_PROXY	0x40		/* entry can be used for PROXY	*/
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ #include <linux/fs.h>
+ #include <linux/proc_fs.h>
+ #include <linux/timer.h>
+ #include <linux/if.h>
+ 
+ 
+ struct arp_hwtype {
+   int			type;
+   char			*name;
+   int			alen;
+   char			*(*print)	(struct sockaddr *);
+   struct arp_hwtype	*next;
+ };
+ 
+ struct arp_aftype {
+   char			*name;
+   int			af;
+   int			alen;
+   int			proto;
+   char			*(*print)	(struct sockaddr *);
+   int			(*check)	(struct sockaddr *);
+   int			(*compare)	(struct sockaddr *, struct sockaddr *);
+   void			(*mksock)	(struct sockaddr *, unsigned char *);
+   unsigned char		*(*unsock)	(struct sockaddr *);
+   struct arp_aftype	*next;
+ };
+ 
+ 
+ /* Packet I/O functions. */
+ extern void		arp_queue(struct sk_buff *skb, struct sockaddr *sap);
+ extern void		arp_delque(struct sk_buff *skb);
+ 
+ /* ARP table manipulation. */
+ extern int		arp_find(struct sockaddr *sap, struct sockaddr *hap,
+ 				 struct ifnet *dev);
+ 
+ /* Various functions. */
+ extern int		arp_add_af(struct arp_aftype *ptr);
+ extern int		arp_add_hw(struct arp_hwtype *ptr);
+ extern struct pagelist	*arp_get_info(void);
+ extern int		arp_ioctl(struct inode *, struct file *,
+ 				  unsigned int cmd, unsigned long arg);
  
+ #endif	/* __KERNEL__ */
  
  #endif	/* _LINUX_IF_ARP_H */
*** original/include/linux/if_ether.h	Fri Feb 18 10:20:46 1994
--- linux/include/linux/if_ether.h	Mon May  9 13:12:53 1994
***************
*** 1,15 ****
  /*
!  * INET		An implementation of the TCP/IP protocol suite for the LINUX
!  *		operating system.  INET is implemented using the  BSD Socket
!  *		interface as the means of communication with the user level.
   *
   *		Global definitions for the Ethernet IEE 802.3 interface.
   *
!  * Version:	@(#)if_ether.h	1.0.1a	02/08/94
   *
!  * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
!  *		Donald Becker, <becker@super.org>
   *
   *		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
--- 1,14 ----
  /*
!  * NET		An implementation of the SOCKET network access protocol.
   *
   *		Global definitions for the Ethernet IEE 802.3 interface.
   *
!  * Version:	@(#)if_ether.h	1.3.1	03/30/94
   *
!  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
   *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
   *		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
***************
*** 19,46 ****
  #define _LINUX_IF_ETHER_H
  
  
! /* IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
!    and FCS/CRC (frame check sequence). */
! #define ETH_ALEN	6		/* Octets in one ethernet addr	 */
! #define ETH_HLEN	14		/* Total octets in header.	 */
! #define ETH_ZLEN	60		/* Min. octets in frame sans FCS */
! #define ETH_DATA_LEN	1500		/* Max. octets in payload	 */
! #define ETH_FRAME_LEN	1514		/* Max. octets in frame sans FCS */
  
  
  /* These are the defined Ethernet Protocol ID's. */
  #define ETH_P_LOOP	0x0060		/* Ethernet Loopback packet	*/
  #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_ARP	0x0806		/* Address Resolution packet	*/
  #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
! #define ETH_P_X25	0x0805		/* CCITT X.25			*/
! #define ETH_P_IPX	0x8137		/* IPX over DIX			*/
! #define ETH_P_802_3	0x0001		/* Dummy type for 802.3 frames  */
! #define ETH_P_AX25	0x0002		/* Dummy protocol id for AX.25  */
! #define ETH_P_ALL	0x0003		/* Every packet (be careful!!!) */
  
  /* This is an Ethernet frame header. */
  struct ethhdr {
    unsigned char		h_dest[ETH_ALEN];	/* destination eth addr	*/
--- 18,62 ----
  #define _LINUX_IF_ETHER_H
  
  
! #include <linux/if.h>
! 
! 
! #define ETH_NAME	"eth%d"		/* generic ETH interface name	*/
! 
  
+ /*
+  * IEEE 802.3 Ethernet magic constants.
+  * The frame sizes omit the preamble and FCS/CRC (frame check sequence).
+  */
+ #define ETH_ALEN	6		/* Octets in one ethernet addr	  */
+ #define ETH_HLEN	14		/* Total octets in header.	  */
+ #define ETH_ZLEN	60		/* Min. octets in frame sans FCS  */
+ #define ETH_DATA_LEN	1500		/* Max. octets in payload	  */
+ #define ETH_FRAME_LEN	(ETH_DATA_LEN + ETH_HLEN)	/* Max. octets in */
+ 							/* frame sans FCS */
+ #define ETH_MTU		(ETH_DATA_LEN)	/* standard Ethernet MTU	  */
  
  /* These are the defined Ethernet Protocol ID's. */
+ #define ETH_P_802_3	0x0001		/* dummy ID for "all" packets	*/
+ #define ETH_P_AX25	0x0002		/* dummy ID for AX.25 packets	*/
+ #define ETH_P_ALL	0x0003		/* every packet (be careful!!!) */
  #define ETH_P_LOOP	0x0060		/* Ethernet Loopback packet	*/
  #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_RARP      0x8035		/* Reverse Addr Res packet	*/
! #define ETH_P_IPX	0x8137		/* Novell IPX			*/
  
+ /* Define the Ethernet Broadcast Address (48 bits set to "1"). */
+ #define ETH_A_BCAST     "\377\377\377\377\377\377"
+ 
+ /* An Ethernet address. */
+ struct ether_addr {
+   unsigned char		ether_addr_octect[ETH_ALEN];
+ };
+ 
  /* This is an Ethernet frame header. */
  struct ethhdr {
    unsigned char		h_dest[ETH_ALEN];	/* destination eth addr	*/
***************
*** 48,78 ****
    unsigned short	h_proto;		/* packet type ID field	*/
  };
  
! /* Ethernet statistics collection data. */
! struct enet_statistics{
!   int	rx_packets;			/* total packets received	*/
!   int	tx_packets;			/* total packets transmitted	*/
!   int	rx_errors;			/* bad packets received		*/
!   int	tx_errors;			/* packet transmit problems	*/
!   int	rx_dropped;			/* no space in linux buffers	*/
!   int	tx_dropped;			/* no space available in linux	*/
!   int	multicast;			/* multicast packets received	*/
!   int	collisions;
! 
!   /* detailed rx_errors: */
!   int	rx_length_errors;
!   int	rx_over_errors;			/* receiver ring buff overflow	*/
!   int	rx_crc_errors;			/* recved pkt with crc error	*/
!   int	rx_frame_errors;		/* recv'd frame alignment error */
!   int	rx_fifo_errors;			/* recv'r fifo overrun		*/
!   int	rx_missed_errors;		/* receiver missed packet	*/
! 
!   /* detailed tx_errors */
!   int	tx_aborted_errors;
!   int	tx_carrier_errors;
!   int	tx_fifo_errors;
!   int	tx_heartbeat_errors;
!   int	tx_window_errors;
  };
  
  #endif	/* _LINUX_IF_ETHER_H */
--- 64,80 ----
    unsigned short	h_proto;		/* packet type ID field	*/
  };
  
! /* This is the complete Ethernet frame. */
! struct ethframe {
!   struct ethhdr		f_hdr;			/* frame header		*/
!   char			f_data[ETH_DATA_LEN];	/* frame data (variable)*/
  };
+ 
+ #ifdef __KERNEL__
+ 
+ extern int		eth_setup(struct ifnet *dev);
+ extern void		eth_init(void);
+ 
+ #endif	/* __KERNEL__ */
  
  #endif	/* _LINUX_IF_ETHER_H */
*** original/include/linux/if_loop.h	Thu Jan  1 01:00:00 1970
--- linux/include/linux/if_loop.h	Mon May  9 13:12:53 1994
***************
*** 0 ****
--- 1,38 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Global definitions for the Loopback interface.
+  *
+  * Version:	@(#)if_loop.h	1.1.0	03/15/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _LINUX_IF_LOOP_H
+ #define _LINUX_IF_LOOP_H
+ 
+ #include <linux/if.h>
+ 
+ #define LOOP_MTU	3500		/* standard loopback MTU	*/
+ 
+ /* ARP pseudo-hardware identifier, needed by ifconfig(8). */
+ #define ARPHRD_LOOPBACK	255		/* the LOOPBACK device		*/
+ 
+ /* Loopback interface constants. */
+ #define LOOP_ALEN	0		/* #bytes in loop addr		*/
+ #define LOOP_HLEN	(sizeof(short))	/* #bytes in loop header	*/
+ 
+ 
+ /* This is a Loopback frame header. */
+ struct loophdr {
+   unsigned short	h_proto;		/* packet type ID field	*/
+ };
+ 
+ 
+ #endif	/* _LINUX_IF_LOOP_H */
*** original/include/linux/if_ppp.h	Thu Jan  1 01:00:00 1970
--- linux/include/linux/if_ppp.h	Thu May 12 13:31:57 1994
***************
*** 0 ****
--- 1,183 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Global definitions for the PPP driver interface.
+  *
+  * Version:	@(#)if_ppp.h	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		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.
+  */
+ #ifndef _LINUX_IF_PPP_H
+ #define _LINUX_IF_PPP_H
+ 
+ 
+ #include <linux/if.h>
+ 
+ 
+ #define PPP_IFNAME	"ppp%d"		/* PPP interface name		*/
+ 
+ 
+ /* ARP pseudo-hardware identifiers, needed by ifconfig(8). */
+ #define ARPHRD_PPP	253		/* the PPP encap		*/
+ 
+ /* PPP interface constants. */
+ #define PPP_ALEN	0		/* #bytes in PPP addr		*/
+ #define PPP_HLEN	0		/* #bytes in PPP header		*/
+ 
+ /* Magic value for the ppp structure. */
+ #define PPP_MAGIC	0x5002
+ 
+ /* PPP I/O control requests. */
+ #define	PPPIOCGFLAGS	0x5490		/* get configuration flags	*/
+ #define	PPPIOCSFLAGS	0x5491		/* set configuration flags	*/
+ #define	PPPIOCGASYNCMAP	0x5492		/* get async map		*/
+ #define	PPPIOCSASYNCMAP	0x5493		/* set async map		*/
+ #define	PPPIOCGUNIT	0x5494		/* get ppp unit number		*/
+ #define PPPIOCSINPSIG	0x5495		/* set input ready signal	*/
+ #define PPPIOCSDEBUG	0x5497		/* set debug level		*/
+ #define PPPIOCGDEBUG	0x5498		/* get debug level		*/
+ #define PPPIOCGSTAT	0x5499		/* read PPP statistic information */
+ #define PPPIOCGTIME	0x549A		/* read time delta information	*/
+ #define PPPIOCGXASYNCMAP	0x549B	/* get async table		*/
+ #define PPPIOCSXASYNCMAP	0x549C	/* set async table		*/
+ #define PPPIOCSMRU		0x549D	/* set receive unit size for PPP */
+ #define PPPIOCRASYNCMAP		0x549E	/* set receive async map	*/
+ #define PPPIOCSMAXCID		0x549F	/* set the maximum compression slot id */
+ 
+ /* Special characters in the framing protocol. */
+ #define PPP_ALLSTATIONS	0xFF		/* All-Stations broadcast address */
+ #define PPP_UI		0x03		/* Unnumbered Information	*/
+ #define PPP_FLAG	0x7E		/* frame delimiter -- marks frame
+ 					 * boundaries			*/
+ #define PPP_ADDRESS	0xFF		/* first character of frame <--
+ 					 * (may be second character of
+ 					 * frame  <-- compressed)	*/
+ #define PPP_CONTROL	0x03
+ #define PPP_TRANS	0x20		/* async transparency modifier	*/
+ #define PPP_ESC		0x7D		/* escape charecter -- next
+ 					 * character is data, and the
+ 					 * PPP_TRANS bit should be toggled.
+ 					 * PPP_ESC PPP_FLAG is illegal	*/
+ 
+ /* PPP protocol numbers. */
+ #define PROTO_IP       0x0021
+ #define PROTO_VJCOMP   0x002d
+ #define PROTO_VJUNCOMP 0x002f
+ 
+ /* FCS support. */
+ #define PPP_FCS_INIT   0xffff
+ #define PPP_FCS_GOOD   0xf0b8
+ 
+ /* Initial MTU. */
+ #define PPP_MTU	       1500
+ 
+ /* Initial MRU. */
+ #define PPP_MRU		PPP_MTU
+ 
+ /* PPP flags. */
+ #define SC_COMP_PROT	0x00000001	/* protocol comp (output)	*/
+ #define SC_COMP_AC	0x00000002	/* header comp (output)		*/
+ #define	SC_COMP_TCP	0x00000004	/* TCP (VJ) comp (output)	*/
+ #define SC_NO_TCP_CCID	0x00000008	/* disable VJ conn.-id comp	*/
+ #define SC_REJ_COMP_AC	0x00000010	/* reject adrs/ctrl comp. on input */
+ #define SC_REJ_COMP_TCP	0x00000020	/* reject TCP (VJ) comp. on input */
+ #define SC_ENABLE_IP	0x00000100	/* IP packets may be exchanged	*/
+ #define SC_IP_DOWN	0x00000200	/* give ip frames to pppd	*/
+ #define SC_IP_FLUSH	0x00000400	/* "next time" flag for IP_DOWN	*/
+ #define SC_DEBUG	0x00010000	/* enable debug messages	*/
+ #define SC_LOG_INPKT	0x00020000	/* log contents of good pkts recvd */
+ #define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent	*/
+ #define SC_LOG_RAWIN	0x00080000	/* log all chars received	*/
+ #define SC_LOG_FLUSH	0x00100000	/* log all chars flushed	*/
+ 
+ /* Flag bits to determine state of input characters */
+ #define SC_RCV_B7_0	0x01000000	/* have rcvd char with bit 7 = 0 */
+ #define SC_RCV_B7_1	0x02000000	/* have rcvd char with bit 7 = 0 */
+ #define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
+ #define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
+ 
+ #define SC_MASK		0x0fffffff	/* bits that user can change	*/
+  
+ /* Flag for doing transmitter lockout. */
+ #define SC_XMIT_BUSY	0x10000000	/* ppp_write_wakeup is active	*/
+ 
+ 
+ /* Extended asyncmap - allows any character to be escaped. */
+ typedef u_long	ext_accm[8];
+ 
+ /*
+  * This is the format of the data buffer of a LQP packet.
+  * The packet data is sent/received to the peer.
+  */
+ struct ppp_lqp_packet_hdr {
+   unsigned long		LastOutLQRs;	/* Copied from PeerOutLQRs	 */
+   unsigned long		LastOutPackets; /* Copied from PeerOutPackets	 */
+   unsigned long		LastOutOctets;	/* Copied from PeerOutOctets	 */
+   unsigned long		PeerInLQRs;	/* Copied from SavedInLQRs	 */
+   unsigned long		PeerInPackets;	/* Copied from SavedInPackets	 */
+   unsigned long		PeerInDiscards; /* Copied from SavedInDiscards	 */
+   unsigned long		PeerInErrors;	/* Copied from SavedInErrors	 */
+   unsigned long		PeerInOctets;	/* Copeid from SavedInOctets	 */
+   unsigned long		PeerOutLQRs;	/* Copied from OutLQRs, plus 1	 */
+   unsigned long		PeerOutPackets; /* Current ifOutUniPackets, + 1	 */
+   unsigned long		PeerOutOctets;	/* Current ifOutOctets + LQR	 */
+ };
+ 
+ /*
+  * This data is not sent to the remote. It is updated by
+  * the driver when a packet is received.
+  */
+ struct ppp_lqp_packet_trailer {
+   unsigned long		SaveInLQRs;	/* Current InLQRs on receiption	 */
+   unsigned long		SaveInPackets;	/* Current ifInUniPackets	 */
+   unsigned long		SaveInDiscards; /* Current ifInDiscards		 */
+   unsigned long		SaveInErrors;	/* Current ifInErrors		 */
+   unsigned long		SaveInOctets;	/* Current ifInOctects		 */
+ };
+ 
+ /*
+  * PPP LQP packet. The packet is changed by the driver immediately prior
+  * to transmission and updated upon receiption with the current values.
+  * So, it must be known to the driver as well as the pppd software.
+  */
+ struct ppp_lpq_packet {
+   unsigned long			magic;	/* current magic value		 */
+   struct ppp_lqp_packet_hdr	hdr;	/* Header fields for structure	 */
+   struct ppp_lqp_packet_trailer tail;	/* Trailer fields (not sent)	 */
+ };
+ 
+ /* PPP interface statistics. (used by LQP / pppstats). */
+ struct ppp_stats {
+   unsigned long		rbytes;		/* bytes received		 */
+   unsigned long		rcomp;		/* compressed packets received	 */
+   unsigned long		runcomp;	/* uncompressed packets received */
+   unsigned long		rothers;	/* non-ip frames received	 */
+   unsigned long		rerrors;	/* received errors		 */
+   unsigned long		roverrun;	/* "buffer overrun" counter	 */
+   unsigned long		tossed;		/* packets discarded		 */
+   unsigned long		runts;		/* frames too short to process	 */
+   unsigned long		rgiants;	/* frames too large to process	 */
+   unsigned long		sbytes;		/* bytes sent			 */
+   unsigned long		scomp;		/* compressed packets sent	 */
+   unsigned long		suncomp;	/* uncompressed packets sent	 */
+   unsigned long		sothers;	/* non-ip frames sent		 */
+   unsigned long		serrors;	/* transmitter errors		 */
+   unsigned long		sbusy;		/* "transmitter busy" counter	 */
+ };
+ 
+ /* Demand dial fields. */
+ struct ppp_ddinfo {
+   unsigned long		ip_sjiffies;	/* time when last IP frame sent */
+   unsigned long		ip_rjiffies;	/* time when last IP frame recvd*/
+   unsigned long		nip_sjiffies;	/* time when last NON-IP sent	*/
+   unsigned long		nip_rjiffies;	/* time when last NON-IP recvd	*/
+ };
+ 
+ 
+ #endif	/* _LINUX_IF_PPP_H */
*** original/include/linux/if_slip.h	Thu Jan  1 01:00:00 1970
--- linux/include/linux/if_slip.h	Mon May  9 13:12:53 1994
***************
*** 0 ****
--- 1,40 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Global definitions for the SLIP driver interface.
+  *
+  * Version:	@(#)if_slip.h	1.7.0	04/02/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1992 Laurence Culhane
+  *		Copyright 1993,1993 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _LINUX_IF_SLIP_H
+ #define _LINUX_IF_SLIP_H
+ 
+ #include <linux/if.h>
+ 
+ #define SLIP_MTU	296		/* standard SLIP MTU		*/
+ #define SLIP_IFNAME	"sl%d"		/* SLIP interface name		*/
+ 
+ /* ARP pseudo-hardware identifiers, needed by ifconfig(8). */
+ #define ARPHRD_SLIP	254		/* the SLIP encap		*/
+ 
+ /* SLIP driver encapsulation types. */
+ #define SLIP_ENCAP_SLIP	    0		/* standard protocol		*/
+ #define SLIP_ENCAP_VJ	    1		/* VJ compression		*/
+ #define SLIP_ENCAP_SLIP6    2		/* 6-bit ASCII protocol		*/
+ #define SLIP_ENCAP_VJSLIP6  3		/* VJ-compressed 6-bit ASCII	*/
+ 
+ /* SLIP interface constants. */
+ #define SLIP_ALEN	0		/* #bytes in SLIP addr		*/
+ #define SLIP_HLEN	0		/* #bytes in SLIP header	*/
+ 
+ 
+ #endif	/* _LINUX_IF_SLIP_H */
*** original/include/linux/in.h	Fri Mar 18 09:30:26 1994
--- linux/include/linux/in.h	Mon May  9 13:12:53 1994
***************
*** 5,14 ****
   *
   *		Definitions of the Internet Protocol.
   *
!  * Version:	@(#)in.h	1.0.1	04/21/93
   *
   * Authors:	Original taken from the GNU Project <netinet/in.h> file.
!  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
--- 5,14 ----
   *
   *		Definitions of the Internet Protocol.
   *
!  * Version:	@(#)in.h	1.1.1	03/21/94
   *
   * Authors:	Original taken from the GNU Project <netinet/in.h> file.
!  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
***************
*** 19,24 ****
--- 19,27 ----
  #define _LINUX_IN_H
  
  
+ #include <linux/socket.h>	/* for "struct sockaddr"		*/
+ 
+ 
  /* Standard well-defined IP protocols.  */
  enum {
    IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
***************
*** 42,48 ****
  
  
  /* Structure describing an Internet (IP) socket address. */
! #define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
  struct sockaddr_in {
    short int		sin_family;	/* Address family		*/
    unsigned short int	sin_port;	/* Port number			*/
--- 45,51 ----
  
  
  /* Structure describing an Internet (IP) socket address. */
! #define __SOCK_SIZE__	(sizeof(struct sockaddr))
  struct sockaddr_in {
    short int		sin_family;	/* Address family		*/
    unsigned short int	sin_port;	/* Port number			*/
*** original/include/linux/in_systm.h	Mon Feb 14 17:51:45 1994
--- linux/include/linux/in_systm.h	Mon May  9 13:12:53 1994
***************
*** 5,11 ****
   *
   *		Miscellaneous internetwork definitions for kernel.
   *
!  * Version:	@(#)in_systm.h  1.0.0   12/17/93
   *
   * Authors:	Original taken from Berkeley BSD UNIX 4.3-RENO.
   *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
--- 5,11 ----
   *
   *		Miscellaneous internetwork definitions for kernel.
   *
!  * Version:	@(#)in_systm.h	1.0.0	12/17/93
   *
   * Authors:	Original taken from Berkeley BSD UNIX 4.3-RENO.
   *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
***************
*** 25,32 ****
   * them (yet), but it might in the future.  This is mostly
   * there for compatibility with BSD user-level programs.
   */
! typedef u_short	n_short;	/* short as received from the net	*/
  typedef u_long	n_long;		/* long as received from the net	*/
! typedef u_long	n_time;		/* ms since 00:00 GMT, byte rev		*/
  
  #endif /* _LINUX_IN_SYSTM_H */
--- 25,33 ----
   * them (yet), but it might in the future.  This is mostly
   * there for compatibility with BSD user-level programs.
   */
! typedef u_short n_short;	/* short as received from the net	*/
  typedef u_long	n_long;		/* long as received from the net	*/
! typedef	u_long	n_time;		/* ms since 00:00 GMT, byte rev		*/
! 
  
  #endif /* _LINUX_IN_SYSTM_H */
*** original/include/linux/interrupt.h	Mon Mar  7 13:34:14 1994
--- linux/include/linux/interrupt.h	Mon May  9 13:12:54 1994
***************
*** 18,24 ****
  	CONSOLE_BH,
  	SERIAL_BH,
  	TTY_BH,
! 	INET_BH,
  	KEYBOARD_BH
  };
  
--- 18,24 ----
  	CONSOLE_BH,
  	SERIAL_BH,
  	TTY_BH,
! 	NET_BH,
  	KEYBOARD_BH
  };
  
*** original/include/linux/ip.h	Wed Dec  1 13:44:15 1993
--- linux/include/linux/ip.h	Mon May  9 13:12:54 1994
***************
*** 3,14 ****
   *		operating system.  INET is implemented using the  BSD Socket
   *		interface as the means of communication with the user level.
   *
!  *		Definitions for the IP protocol.
   *
!  * Version:	@(#)ip.h	1.0.2	04/28/93
   *
!  * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *
   *		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
--- 3,18 ----
   *		operating system.  INET is implemented using the  BSD Socket
   *		interface as the means of communication with the user level.
   *
!  *		Definitions for IP, as per RFC-791, September 1981
   *
!  * Version:	@(#)ip.h	1.2.0	03/18/94
   *
!  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
!  *		release 4.3-RENO system, Copyright UCB 1988.
   *
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
   *		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
***************
*** 18,51 ****
  #define _LINUX_IP_H
  
  
! #define IPOPT_END	0
! #define IPOPT_NOOP	1
! #define IPOPT_SEC	130
! #define IPOPT_LSRR	131
! #define IPOPT_SSRR	137
! #define IPOPT_RR	7
! #define IPOPT_SID	136
! #define IPOPT_TIMESTAMP	68
! 
! 
! struct timestamp {
!   unsigned char	len;
!   unsigned char ptr;
!   union {
! 	unsigned char	flags:4,
! 			overflow:4;
! 	unsigned char	full_char;
!   } x;
!   unsigned long	data[9];
  };
  
  
! #define MAX_ROUTE	16
  
  struct route {
!   char		route_size;
!   char		pointer;
!   unsigned long route[MAX_ROUTE];
  };
  
  
--- 22,173 ----
  #define _LINUX_IP_H
  
  
! #include <endian.h>
! 
! 
! /* Internet implementation parameters. */
! #define IPVERSION	4
! #define	MAXTTL		255	/* maximum time to live (seconds)	*/
! #define DEFTTL		32	/* default TTL value			*/
! #define	IPFRAGTTL	60	/* time to live for frags, slowhz	*/
! #define	IPTTLDEC	1	/* subtracted when forwarding		*/
! #define	IP_MAXPACKET	65535	/* maximum IP datagram size		*/
! #define	IP_MSS		576	/* default maximum segment size		*/
! 
! 
! struct ip_parms {
!   int	version,		/* IP version in use			*/
! 	maxttl,			/* max. TTL value that is legal		*/
! 	defttl,			/* current "default" TTL value		*/
! 	fragttl,		/* fragment TTL (unused)		*/
! 	ttldec,			/* forwarding TTL decrement value	*/
! 	maxpacket,		/* max. IP packet size			*/
! 	mss;			/* default IP MSS size			*/
  };
  
  
! /* The Internet Protocol datagram header, without the options. */
! struct ip {
! #if __BYTE_ORDER == __LITTLE_ENDIAN 
!   unsigned char		ip_hl:4,	/* header length		*/
! 			ip_v:4;		/* version			*/
! #endif
! #if __BYTE_ORDER == __BIG_ENDIAN 
!   unsigned char		ip_v:4,		/* version			*/
! 			ip_hl:4;	/* header length		*/
! #endif
!   unsigned char		ip_tos;		/* type of service		*/
!   unsigned short	ip_len;		/* total length			*/
!   unsigned short	ip_id;		/* identification		*/
!   unsigned short	ip_off;		/* fragment offset field	*/
!   unsigned char		ip_ttl;		/* time to live			*/
!   unsigned char		ip_p;		/* protocol			*/
!   unsigned short	ip_sum;		/* checksum			*/
!   struct in_addr	ip_src, ip_dst;	/* source and dest address	*/
! };
! 
! /* IP flags. */
! #define IP_CE		0x8000		/* Flag: "Congestion"		*/
! #define IP_DF		0x4000		/* Flag: "Don't Fragment"	*/
! #define IP_MF		0x2000		/* Flag: "More Fragments"	*/
! #define IP_OFFSET	0x1FFF		/* "Fragment Offset" part	*/
! 
! 
! /* IP socket options */
! #define IP_TOS		0x01		/* Type Of Service requested	*/
! #define IP_TTL		0x02		/* Time To Live value reqd	*/
! 
! /* IP Type Of Service (TOS) flags. */
! #define IPTOS_RELIABILITY	0x04	/* TOS: Reliability ("dns")	*/
! #define IPTOS_THROUGHPUT	0x08	/* TOS: Throughput ("ftp")	*/
! #define IPTOS_LOWDELAY		0x10	/* TOS: Low-Delay ("telnet")	*/
! 
! /* IP Precedence (TOS) values. */
! #if 0
! #define	IPTOS_PREC_NETCONTROL		0xE0
! #define	IPTOS_PREC_INTERNETCONTROL	0xC0
! #define	IPTOS_PREC_CRITIC_ECP		0xA0
! #define	IPTOS_PREC_FLASHOVERRIDE	0x80
! #define	IPTOS_PREC_FLASH		0x60
! #define	IPTOS_PREC_IMMEDIATE		0x40
! #define	IPTOS_PREC_PRIORITY		0x20
! #define	IPTOS_PREC_ROUTINE		0x10
! #endif
! 
! /* IP Option Definitions. */
! #define	IPOPT_COPIED(o)		((o) & 0x80)
! #define	IPOPT_CLASS(o)		((o) & 0x60)
! #define	IPOPT_NUMBER(o)		((o) & 0x1F)
! 
! #define	IPOPT_CONTROL		0x00
! #define	IPOPT_RESERVED1		0x20
! #define	IPOPT_DEBMEAS		0x40
! #define	IPOPT_RESERVED2		0x60
! 
! #define	IPOPT_EOL		0	/* end of option list		*/
! #define	IPOPT_NOP		1	/* no operation			*/
! #define	IPOPT_RR		7	/* record packet route		*/
! #define	IPOPT_TS		68	/* timestamp			*/
! #define	IPOPT_SECURITY		130	/* provide s,c,h,tcc		*/
! #define	IPOPT_LSRR		131	/* loose source route		*/
! #define	IPOPT_SATID		136	/* satnet id			*/
! #define	IPOPT_SSRR		137	/* strict source route		*/
! 
! /* Offsets to fields in options other than EOL and NOP. */
! #define	IPOPT_OPTVAL		0	/* option ID			*/
! #define	IPOPT_OLEN		1	/* option length		*/
! #define IPOPT_OFFSET		2	/* offset within option		*/
! #define	IPOPT_MINOFF		4	/* min value of above		*/
! 
! /* Time stamp option structure. */
! struct ip_timestamp {
!   unsigned char	ipt_code;		/* IPOPT_TS			*/
!   unsigned char	ipt_len;		/* size of structure (variable)	*/
!   unsigned char	ipt_ptr;		/* index of current entry	*/
! #if __BYTE_ORDER == __LITTLE_ENDIAN 
!   unsigned char	ipt_flg:4,		/* flags, see below		*/
! 		ipt_oflw:4;		/* overflow counter		*/
! #endif
! #if __BYTE_ORDER == __BIG_ENDIAN 
!   unsigned char	ipt_oflw:4,		/* overflow counter		*/
! 		ipt_flg:4;		/* flags, see below		*/
! #endif
!   union ipt_timestamp {
! 	n_long	ipt_time[1];
! 	struct	ipt_ta {
! 		struct in_addr ipt_addr;
! 		n_long ipt_time;
! 	} ipt_ta[1];
!   } ipt_timestamp;
! };
! 
! /* Time Stamp flag bits. */
! #define	IPOPT_TS_TSONLY		0	/* timestamps only		*/
! #define	IPOPT_TS_TSANDADDR	1	/* timestamps and addresses	*/
! #define	IPOPT_TS_PRESPEC	3	/* specified modules only	*/
! 
! 
! /* IP Security bits (*NOT* byte-swapped) */
! #define	IPOPT_SECUR_UNCLASS	0x0000
! #define	IPOPT_SECUR_CONFID	0xF135
! #define	IPOPT_SECUR_EFTO	0x789A
! #define	IPOPT_SECUR_MMMM	0xBC4d
! #define	IPOPT_SECUR_RESTR	0xAf13
! #define	IPOPT_SECUR_SECRET	0xD788
! #define	IPOPT_SECUR_TOPSECRET	0x6BC5
! 
  
+ #ifdef __KERNEL__
+ 
+ 
+ #define SKBIP(x)	((struct ip *) ((x)->raw))
+ 
+ 
+ #define MAX_ROUTE	16
  struct route {
!   char			route_size;
!   char			pointer;
!   unsigned long		route[MAX_ROUTE];
  };
  
  
***************
*** 53,59 ****
    struct route		record_route;
    struct route		loose_route;
    struct route		strict_route;
!   struct timestamp	tstamp;
    unsigned short	security;
    unsigned short	compartment;
    unsigned short	handling;
--- 175,181 ----
    struct route		record_route;
    struct route		loose_route;
    struct route		strict_route;
!   struct ip_timestamp	tstamp;
    unsigned short	security;
    unsigned short	compartment;
    unsigned short	handling;
***************
*** 62,81 ****
  };
  
  
! struct iphdr {
!   unsigned char		ihl:4,
! 			version:4;
!   unsigned char		tos;
!   unsigned short	tot_len;
!   unsigned short	id;
!   unsigned short	frag_off;
!   unsigned char		ttl;
!   unsigned char		protocol;
!   unsigned short	check;
!   unsigned long		saddr;
!   unsigned long		daddr;
!   /*The options start here. */
! };
  
  
  #endif	/* _LINUX_IP_H */
--- 184,219 ----
  };
  
  
! extern struct packet_type	ip_packet_type;
! extern struct ip_parms		ip_parms;
! 
! 
! extern void		ip_sort_send(struct sock *sk);
! extern int		ip_backoff(int n);
! 
! /* Functions exported by IP for its protocol handlers: */
! extern void		ip_icmpexpire(struct ip *iph);
! extern void		ip_route_check(unsigned long daddr);
! extern int		ip_build_header(struct sk_buff *skb,
! 					unsigned long saddr,
! 					unsigned long daddr,
! 					struct ifnet **dev, int type,
! 					struct options *opt, int tos,
! 					int ttl, int noroute);
! extern unsigned short	ip_compute_csum(unsigned char * buff, int len);
! extern void		ip_queue_xmit(struct sock *sk, struct ifnet *dev,
! 				      struct sk_buff *skb, int free);
! extern void		ip_retransmit(struct sock *sk, int all);
! extern int 		ip_setsockopt(struct sock *sk, int level,
! 				      int optname, char *optval, int optlen);
! extern int 		ip_getsockopt(struct sock *sk, int level,
! 				      int optname, char *optval, int *optlen);
! 
! /* Called by SOCK: */
! extern int		ip_ioctl(struct sock *sk, int cmd,
! 				 unsigned long arg);
! 
! #endif	/* __KERNEL__ */
  
  
  #endif	/* _LINUX_IP_H */
*** original/include/linux/ip_icmp.h	Thu Jan  1 01:00:00 1970
--- linux/include/linux/ip_icmp.h	Mon May  9 13:12:54 1994
***************
*** 0 ****
--- 1,131 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Definitions for ICMP as per RFC 792, September 1981.
+  *
+  * Version:	@(#)ip_icmp.h	1.2.0	03/15/94
+  *
+  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
+  *		release 4.3-RENO system, Copyright UCB 1988.
+  *
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		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.
+  */
+ #ifndef _LINUX_IP_ICMP_H
+ #define	_LINUX_IP_ICMP_H
+ 
+ /*
+  * Lower bounds on packet lengths for various types.
+  * For the error advice packets must first insure that the
+  * packet is large enought to contain the returned IP header.
+  * Only then can we do the check to see if 64 bits of packet
+  * data have been returned, since we need to check the returned
+  * ip header length.
+  */
+ #define	ICMP_MINLEN	8			/* abs minimum		*/
+ #define	ICMP_TSLEN	(8 + 3 * sizeof(n_time))/* timestamp		*/
+ #define	ICMP_MASKLEN	12			/* address mask		*/
+ #define	ICMP_ADVLENMIN	(8 + sizeof(struct ip) + 8)	/* min		*/
+ #define	ICMP_ADVLEN(p)	(8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ 	/* N.B.: must separately check that ip_hl >= 5 */
+ 
+ 
+ /* Definition of type and code field values. */
+ #define	ICMP_ECHOREPLY		0	/* echo reply			*/
+ #define	ICMP_UNREACH		3	/* dest unreachable:		*/
+ #define	  ICMP_UNREACH_NET	0	/*   bad net			*/
+ #define	  ICMP_UNREACH_HOST	1	/*   bad host			*/
+ #define	  ICMP_UNREACH_PROTOCOL	2	/*   bad protocol		*/
+ #define	  ICMP_UNREACH_PORT	3	/*   bad port			*/
+ #define	  ICMP_UNREACH_NEEDFRAG	4	/*   IP_DF caused drop		*/
+ #define	  ICMP_UNREACH_SRCFAIL	5	/*   source route failed	*/
+ #define	ICMP_SOURCEQUENCH	4	/* packet lost, slow down	*/
+ #define	ICMP_REDIRECT		5	/* shorter route:		*/
+ #define	  ICMP_REDIRECT_NET	0	/*   for network		*/
+ #define	  ICMP_REDIRECT_HOST	1	/*   for host			*/
+ #define	  ICMP_REDIRECT_TOSNET	2	/*   for tos and net		*/
+ #define	  ICMP_REDIRECT_TOSHOST	3	/*   for tos and host		*/
+ #define	ICMP_ECHO		8	/* echo service			*/
+ #define	ICMP_TIMXCEED		11	/* time exceeded:		*/
+ #define	  ICMP_TIMXCEED_INTRANS	0	/*   ttl==0 in transit		*/
+ #define	  ICMP_TIMXCEED_REASS	1	/*   ttl==0 in reass		*/
+ #define	ICMP_PARAMPROB		12	/* ip header bad		*/
+ #define	ICMP_TSTAMP		13	/* timestamp request		*/
+ #define	ICMP_TSTAMPREPLY	14	/* timestamp reply		*/
+ #define	ICMP_IREQ		15	/* information request		*/
+ #define	ICMP_IREQREPLY		16	/* information reply		*/
+ #define	ICMP_MASKREQ		17	/* address mask request		*/
+ #define	ICMP_MASKREPLY		18	/* address mask reply		*/
+ 
+ #define	ICMP_MAXTYPE		18
+ 
+ #define	ICMP_INFOTYPE(type) \
+ 	((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ 	(type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ 	(type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ 	(type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+ 
+ 
+ struct icmp {
+   u_char	icmp_type;		/* type of message, see below */
+   u_char	icmp_code;		/* type sub code */
+   u_short	icmp_cksum;		/* ones complement cksum of struct */
+   union {
+ 	u_char ih_pptr;			/* ICMP_PARAMPROB */
+ 	struct in_addr ih_gwaddr;	/* ICMP_REDIRECT */
+ 	struct ih_idseq {
+ 		n_short	icd_id;
+ 		n_short	icd_seq;
+ 	} ih_idseq;
+ 	int ih_void;
+   } icmp_hun;
+   union {
+ 	struct id_ts {
+ 		n_time its_otime;
+ 		n_time its_rtime;
+ 		n_time its_ttime;
+ 	} id_ts;
+ 	struct id_ip  {
+ 		struct ip idi_ip;
+ 		/* options and then 64 bits of data */
+ 	} id_ip;
+ 	u_long	id_mask;
+ 	char	id_data[1];
+   } icmp_dun;
+ };
+ #define	icmp_pptr	icmp_hun.ih_pptr
+ #define	icmp_gwaddr	icmp_hun.ih_gwaddr
+ #define	icmp_id		icmp_hun.ih_idseq.icd_id
+ #define	icmp_seq	icmp_hun.ih_idseq.icd_seq
+ #define	icmp_void	icmp_hun.ih_void
+ 
+ #define	icmp_otime	icmp_dun.id_ts.its_otime
+ #define	icmp_rtime	icmp_dun.id_ts.its_rtime
+ #define	icmp_ttime	icmp_dun.id_ts.its_ttime
+ #define	icmp_ip		icmp_dun.id_ip.idi_ip
+ #define	icmp_mask	icmp_dun.id_mask
+ #define	icmp_data	icmp_dun.id_data
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ 
+ extern void		icmp_send(unsigned long daddr, int type, int code,
+ 				  struct ip *iph);
+ 
+ extern int		icmp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+ 
+ extern void		icmp_proto_init(void);
+ 
+ 
+ #endif	/* __KERNEL__ */
+ 
+ 
+ #endif	/* _LINUX_IP_ICMP_H */
*** original/include/linux/ip_tcp.h	Thu Jan  1 01:00:00 1970
--- linux/include/linux/ip_tcp.h	Mon May  9 13:12:54 1994
***************
*** 0 ****
--- 1,227 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Definitions for TCP, as per RFC-793, September, 1981.
+  *
+  * Version:	@(#)ip_tcp.h	1.2.1	04/23/94
+  *
+  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
+  *		release 4.3-RENO system, Copyright UCB 1988.
+  *
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		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.
+  */
+ #ifndef _LINUX_TCP_H
+ #define _LINUX_TCP_H
+ 
+ 
+ #include <endian.h>
+ 
+ 
+ /* TCP socket options. */
+ #define	TCP_NODELAY	0x01		/* don't delay send to coalesce	*/
+ 					/* packets			*/
+ #define	TCP_MAXSEG	0x02		/* set maximum segment size	*/
+ #define TCP_ACKDELAY	0x03		/* allow ACK DELAYS or not	*/
+ 
+ #define HEADER_SIZE	64		/* maximum header size		*/
+ 
+ /* TCP system-settable options. */
+ #define	TCPOPT_EOL	0
+ #define	TCPOPT_NOP	1
+ #define	TCPOPT_MAXSEG	2
+ 
+ 
+ #if 0
+ 
+ /* TCP header flags. */
+ #define	TH_FIN		0x01		/* FINal packet	(disconnect)	*/
+ #define	TH_SYN		0x02		/* SYNchronize packet (connect)	*/
+ #define	TH_RST		0x04		/* RESet packet (disconnect)	*/
+ #define	TH_PUSH		0x08		/* PUSH (fast data)		*/
+ #define	TH_ACK		0x10		/* ACKnowledge packet		*/
+ #define	TH_URG		0x20		/* URGent data packet		*/
+ 
+ typedef	u_long	tcp_seq;		/* will this ever change???	*/
+ 
+ /* TCP header (BSD style, will be cleaned up later- FvK). */
+ struct tcphdr {
+   u_short	th_sport;		/* source port			*/
+   u_short	th_dport;		/* destination port		*/
+   tcp_seq	th_seq;			/* sequence number		*/
+   tcp_seq	th_ack;			/* acknowledgement number	*/
+ #if __BYTE_ORDER == __LITTLE_ENDIAN 
+   u_char	th_x2:4,		/* (unused)			*/
+ 		th_off:4;		/* data offset			*/
+ #endif
+ #if __BYTE_ORDER == __BIG_ENDIAN 
+   u_char	th_off:4,		/* data offset			*/
+  		th_x2:4;		/* (unused)			*/
+ #endif
+   u_char	th_flags;
+   u_short	th_win;			/* window			*/
+   u_short	th_sum;			/* checksum			*/
+   u_short	th_urp;			/* urgent pointer		*/
+ };
+ 
+ #else
+ 
+ /* TCP header (old style, will be cleaned up later- FvK). */
+ struct tcphdr {
+   unsigned short	source;
+   unsigned short	dest;
+   unsigned long		seq;
+   unsigned long		ack_seq;
+   unsigned short	res1:4,
+ 			doff:4,
+ 			fin:1,
+ 			syn:1,
+ 			rst:1,
+ 			psh:1,
+ 			ack:1,
+ 			urg:1,
+ 			res2:2;
+   unsigned short	window;
+   unsigned short	check;
+   unsigned short	urg_ptr;
+ };
+ 
+ #endif
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ 
+ #define MAX_SYN_SIZE	(44 + sizeof(struct sk_buff) + MAX_HEADER)
+ #define MAX_FIN_SIZE	(40 + sizeof(struct sk_buff) + MAX_HEADER)
+ #define MAX_ACK_SIZE	(40 + sizeof(struct sk_buff) + MAX_HEADER)
+ #define MAX_RESET_SIZE	(40 + sizeof(struct sk_buff) + MAX_HEADER)
+ #define MAX_WINDOW	8192
+ #define MIN_WINDOW	2048
+ #define MAX_ACK_BACKLOG	2
+ #define MIN_WRITE_SPACE	2048
+ #define TCP_WINDOW_DIFF	2048
+ 
+ #define TCP_RETR1	4	/*
+ 				 * This is howmany retries it does before it
+ 				 * tries to figure out if the gateway is
+ 				 * down.
+ 				 */
+ 
+ #define TCP_RETR2	7	/* this should take around 5 min to timeout */
+ 
+ #define TCP_TIMEOUT_LEN	720000	/* should be about 2 hrs		*/
+ #define TCP_TIMEWAIT_LEN 1000	/* how long to wait to sucessfully 
+ 				 * close the socket, about 60 seconds	*/
+ #define TCP_ACK_TIME	15	/* time to delay before sending an ACK	*/
+ #define TCP_DONE_TIME	250	/* maximum time to wait before actually
+ 				 * destroying a socket			*/
+ #define TCP_WRITE_TIME	3000	/* initial time to wait for an ACK,
+ 			         * after last transmit			*/
+ #define TCP_CONNECT_TIME 2000	/* time to retransmit first SYN		*/
+ #define TCP_SYN_RETRIES	5	/* number of times to retry openning a
+ 				 * connection 				*/
+ #define TCP_PROBEWAIT_LEN 100	/* time to wait between probes when
+ 				 * I've got something to write and
+ 				 * there is no window			*/
+ 
+ #define TCP_NO_CHECK	0	/* turn to one if you want the default
+ 				 * to be no checksum			*/
+ 
+ /* This is also needed by the netstat(1) program. */
+ enum {
+   TCP_ESTABLISHED = 1,
+   TCP_SYN_SENT,
+   TCP_SYN_RECV,
+   TCP_FIN_WAIT1,
+   TCP_FIN_WAIT2,
+   TCP_TIME_WAIT,
+   TCP_CLOSE,
+   TCP_CLOSE_WAIT,
+   TCP_LAST_ACK,
+   TCP_CLOSING,
+   TCP_LISTEN
+ };
+ 
+ 
+ /*
+  * The next routines deal with comparing 32 bit unsigned ints
+  * and worry about wraparound. The general strategy is to do a
+  * normal compare so long as neither of the numbers is within
+  * 4K of wrapping.  Otherwise we must check for the wrap.
+  */
+ static inline int
+ before(unsigned long seq1, unsigned long seq2)
+ {
+   /* this inequality is strict. */
+   if (seq1 == seq2) return(0);
+ 
+   if (seq1 < seq2) {
+ 	if ((unsigned long)seq2-(unsigned long)seq1 < 65536UL) {
+ 		return(1);
+ 	} else {
+ 		return(0);
+ 	}
+   }
+ 
+   /*
+    * Now we know seq1 > seq2.  So all we need to do is check
+    * to see if seq1 has wrapped.
+    */
+   if (seq2 < 8192UL && seq1 > (0xffffffffUL - 8192UL)) {
+ 	return(1);
+   }
+   return(0);
+ }
+ 
+ 
+ static inline int
+ after(unsigned long seq1, unsigned long seq2)
+ {
+   return(before(seq2, seq1));
+ }
+ 
+ 
+ /* is s2<=s1<=s3 ? */
+ static inline int
+ between(unsigned long seq1, unsigned long seq2, unsigned long seq3)
+ {
+   return(after(seq1+1, seq2) && before(seq1, seq3+1));
+ }
+ 
+ 
+ /*
+  * List all states of a TCP socket that can be viewed as a "connected"
+  * state.  This now includes TCP_SYN_RECV, although I am not yet fully
+  * convinced that this is the solution for the 'getpeername(2)'
+  * problem. Thanks to Stephen A. Wood <saw@cebaf.gov>  -FvK
+  */
+ static inline const int
+ tcp_connected(const int state)
+ {
+   return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT ||
+ 	 state == TCP_FIN_WAIT1   || state == TCP_FIN_WAIT2 ||
+ 	 state == TCP_SYN_RECV);
+ }
+ 
+ 
+ extern struct proto	tcp_prot;
+ 
+ 
+ extern int		tcp_devread(char *, int);
+ extern int		tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+ 
+ extern void		tcp_proto_init(void);
+ 
+ 
+ #endif	/* __KERNEL__ */
+ 
+ 
+ #endif	/* _LINUX_IP_TCP_H */
*** original/include/linux/ip_udp.h	Thu Jan  1 01:00:00 1970
--- linux/include/linux/ip_udp.h	Mon May  9 13:12:54 1994
***************
*** 0 ****
--- 1,50 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Definitions for UDP, as per RFC-768, September 1981
+  *
+  * Version:	@(#)ip_udp.h	1.2.0	03/15/94
+  *
+  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
+  *		release 4.3-RENO system, Copyright UCB 1988.
+  *
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		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.
+  */
+ #ifndef _LINUX_IP_UDP_H
+ #define _LINUX_IP_UDP_H
+ 
+ 
+ struct udp {
+   unsigned short	uh_sport;
+   unsigned short	uh_dport;
+   short			uh_len;
+   unsigned short	uh_sum;
+ };
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ 
+ #define UDP_NO_CHECK	0
+ 
+ 
+ extern struct proto	udp_prot;
+ 
+ 
+ extern int		udp_devread(char *, int);
+ extern int		udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+ 
+ extern void		udp_proto_init(void);
+ 
+ #endif	/* __KERNEL__ */
+ 
+ 
+ #endif	/* _LINUX_IP_UDP_H */
*** original/include/linux/major.h	Sat Jan 29 13:55:05 1994
--- linux/include/linux/major.h	Tue May 10 03:02:20 1994
***************
*** 36,50 ****
   * 16 - sockets
   * 17 - af_unix
   * 18 - af_inet
!  * 19 -                                               UNUSED
!  * 20 -                                               UNUSED
   * 21 - scsi generic
!  * 22 -                        (at2disk)
   * 23 -                        mitsumi cdrom
   * 24 -	                       sony535 cdrom
   * 25 -                        matsushita cdrom       minors 0..3
   * 26 -
   * 27 - qic117 tape
   */
  
  #define UNNAMED_MAJOR	0
--- 36,53 ----
   * 16 - sockets
   * 17 - af_unix
   * 18 - af_inet
!  * 19 - net_loop
!  * 20 - net_becker
   * 21 - scsi generic
!  * 22 - net_slip		(at2disk)
   * 23 -                        mitsumi cdrom
   * 24 -	                       sony535 cdrom
   * 25 -                        matsushita cdrom       minors 0..3
+  * 26 - net_dlink
   * 26 -
   * 27 - qic117 tape
+  * 28 - net_depca
+  * 29 - net_plip
   */
  
  #define UNNAMED_MAJOR	0
***************
*** 63,78 ****
  #define XT_DISK_MAJOR	13
  #define SOUND_MAJOR	14
  #define CDU31A_CDROM_MAJOR 15
! #define SOCKET_MAJOR	16
! #define AF_UNIX_MAJOR	17
! #define AF_INET_MAJOR	18
! /* unused: 19, 20 */
  #define SCSI_GENERIC_MAJOR 21
! /* unused: 22 */
  #define MITSUMI_CDROM_MAJOR 23
  #define CDU535_CDROM_MAJOR 24
  #define MATSUSHITA_CDROM_MAJOR 25
  #define QIC117_TAPE_MAJOR 27
  
  /*
   * Tests for SCSI devices.
--- 66,85 ----
  #define XT_DISK_MAJOR	13
  #define SOUND_MAJOR	14
  #define CDU31A_CDROM_MAJOR 15
! #define NET_MAJOR		16		/* char */
! #define AF_UNIX_MAJOR		17		/* char */
! #define AF_INET_MAJOR		18		/* char */
! #define NET_LOOP_MAJOR		19		/* char */
! #define NET_BECKER_MAJOR	20		/* char */
  #define SCSI_GENERIC_MAJOR 21
! #define NET_SLIP_MAJOR		22		/* char */
  #define MITSUMI_CDROM_MAJOR 23
  #define CDU535_CDROM_MAJOR 24
  #define MATSUSHITA_CDROM_MAJOR 25
+ #define NET_DLINK_MAJOR		26		/* char */
  #define QIC117_TAPE_MAJOR 27
+ #define NET_DEPCA_MAJOR		28		/* char */
+ #define NET_PLIP_MAJOR		29		/* char */
  
  /*
   * Tests for SCSI devices.
*** original/include/linux/proc_fs.h	Wed Dec  1 13:44:15 1993
--- linux/include/linux/proc_fs.h	Mon May  9 13:12:54 1994
***************
*** 3,8 ****
--- 3,12 ----
  
  /*
   * The proc filesystem constants/structures
+  *
+  * Modifications:
+  * 93/09/25  Fred Baumgarten <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *           added multiple pages support in net directory
   */
  
  #define PROC_ROOT_INO 1
***************
*** 15,20 ****
--- 19,31 ----
  	char * name;
  };
  
+ struct pagelist {
+         long length;            /* how many bytes used in this page */
+         char *page;             /* current page */
+         struct pagelist *next;  /* next entry */
+         struct pagelist *prev;  /* previous entry */
+ };
+ 
  extern struct super_block *proc_read_super(struct super_block *,void *,int);
  extern void proc_put_inode(struct inode *);
  extern void proc_put_super(struct super_block *);
***************
*** 22,27 ****
--- 33,39 ----
  extern void proc_read_inode(struct inode *);
  extern void proc_write_inode(struct inode *);
  extern int proc_match(int, const char *, struct proc_dir_entry *);
+ 
  
  extern struct inode_operations proc_root_inode_operations;
  extern struct inode_operations proc_base_inode_operations;
*** original/include/linux/route.h	Sun Feb 13 16:16:51 1994
--- linux/include/linux/route.h	Mon May  9 13:12:54 1994
***************
*** 1,15 ****
  /*
!  * INET		An implementation of the TCP/IP protocol suite for the LINUX
!  *		operating system.  INET is implemented using the  BSD Socket
!  *		interface as the means of communication with the user level.
   *
!  *		Global definitions for the IP router interface.
   *
!  * Version:	@(#)route.h	1.0.3	05/27/93
   *
!  * Authors:	Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
!  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *
   *		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
--- 1,19 ----
  /*
!  * NET		An implementation of the BSD Socket Interface as the means of
!  *		communication with the user level for the Linux operating
!  *		system.
   *
!  *		Global definitions for the routing table module.
   *
!  * Version:	@(#)route.h	1.2.0	03/15/94
   *
!  * Authors:	Most definitions and structure layouts taken from the BSD UNIX,
!  *		release 4.3-RENO system, Copyright UCB 1986-1988.
   *
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
   *		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
***************
*** 21,57 ****
  #include <linux/if.h>
  
  
- /* This structure gets passed by the SIOCADDRTOLD and SIOCDELRTOLD calls. */
- struct old_rtentry {
- 	unsigned long	rt_genmask;
- 	struct sockaddr	rt_dst;
- 	struct sockaddr	rt_gateway;
- 	short		rt_flags;
- 	short		rt_refcnt;
- 	unsigned long	rt_use;
- 	char		*rt_dev; 
- };
- 
  /* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
  struct rtentry {
! 	unsigned long	rt_hash;	/* hash key for lookups		*/
! 	struct sockaddr	rt_dst;		/* target address		*/
! 	struct sockaddr	rt_gateway;	/* gateway addr (RTF_GATEWAY)	*/
! 	struct sockaddr	rt_genmask;	/* target network mask (IP)	*/
! 	short		rt_flags;
! 	short		rt_refcnt;
! 	unsigned long	rt_use;
! 	struct ifnet	*rt_ifp;
! 	short		rt_metric;	/* +1 for binary compatibility!	*/
! 	char		*rt_dev;	/* forcing the device at add	*/
  };
- 
- 
  #define	RTF_UP		0x0001		/* route useable		*/
  #define	RTF_GATEWAY	0x0002		/* destination is a gateway	*/
  #define	RTF_HOST	0x0004		/* host entry (net otherwise)	*/
  #define RTF_REINSTATE	0x0008		/* re-instate route after tmout	*/
  #define	RTF_DYNAMIC	0x0010		/* created dyn. (by redirect)	*/
  #define	RTF_MODIFIED	0x0020		/* modified dyn. (by redirect)	*/
  
  #endif	/* _LINUX_ROUTE_H */
--- 25,96 ----
  #include <linux/if.h>
  
  
  /* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
  struct rtentry {
!   unsigned long		rt_hash;	/* hash key for lookups		*/
!   struct sockaddr	rt_dst;		/* target address		*/
!   struct sockaddr	rt_gateway;	/* gateway addr (RTF_GATEWAY)	*/
!   struct sockaddr	rt_genmask;	/* target network mask (IP)	*/
!   short			rt_flags;
!   short			rt_refcnt;
!   unsigned long		rt_use;
!   struct ifnet		*rt_ifp;
!   short			rt_metric;	/* +1 for binary compatibility!	*/
  };
  #define	RTF_UP		0x0001		/* route useable		*/
  #define	RTF_GATEWAY	0x0002		/* destination is a gateway	*/
  #define	RTF_HOST	0x0004		/* host entry (net otherwise)	*/
  #define RTF_REINSTATE	0x0008		/* re-instate route after tmout	*/
  #define	RTF_DYNAMIC	0x0010		/* created dyn. (by redirect)	*/
  #define	RTF_MODIFIED	0x0020		/* modified dyn. (by redirect)	*/
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ #include <linux/proc_fs.h>
+ 
+ #define RT_MAX_GW	4			/* in-table gw entries	*/
+ 
+ 
+ /* This is an entry in the kernel routing table. */
+ struct rtable {
+   struct rtable		*rt_next;
+   struct sockaddr	rt_dst;
+   struct sockaddr	rt_gateway;
+   struct sockaddr	rt_genmask;
+   struct sockaddr	rt_gws[RT_MAX_GW];	/* for AX.25, brrr.... */
+   u_short		rt_flags;
+   u_short		rt_metric;
+   short			rt_refcnt;
+   u_long		rt_use;
+   struct ifnet		*rt_ifp;
+ };
+ 
+ 
+ struct route_aftype {
+   char			*name;
+   int			af;
+   char			*(*sprint)	(struct sockaddr *);
+   int			(*add)		(struct rtentry *);
+   int			(*del)		(struct rtentry *);
+   struct route_aftype	*next;
+ };
+ 
+ 
+ extern struct rtable	*route_base;
+ 
+ 
+ extern void			route_flush(struct ifnet *dev);
+ extern struct pagelist		*route_get_info(void);
+ extern struct route_aftype	*route_aftype(int af);
+ extern int			route_add_af(struct route_aftype *ptr);
+ extern char			*route_sprint(struct sockaddr *sap);
+ extern int			route_dbg_ioctl(unsigned long arg, int value);
+ extern void			route_dprintf(char *fmt, ...);
+ extern int			route_ioctl(struct inode *, struct file *,
+ 					    unsigned int, unsigned long);
+ 
+ #endif	/* __KERNEL__ */
+ 
  
  #endif	/* _LINUX_ROUTE_H */
*** original/include/linux/skbuff.h	Mon May  9 00:52:05 1994
--- linux/include/linux/skbuff.h	Mon May  9 13:12:54 1994
***************
*** 1,137 ****
  /*
!  *	Definitions for the 'struct sk_buff' memory handlers.
   *
!  *	Authors:
!  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
!  *		Florian La Roche, <rzsfl@rz.uni-sb.de>
!  *
!  *	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.
   */
!  
! #ifndef _LINUX_SKBUFF_H
! #define _LINUX_SKBUFF_H
! #include <linux/malloc.h>
! #include <linux/wait.h>
! #include <linux/time.h>
! 
! #define CONFIG_SKB_CHECK	1
! 
! #define HAVE_ALLOC_SKB		/* For the drivers to know */
! 
  
- #define FREE_READ	1
- #define FREE_WRITE	0
  
- 
- struct sk_buff_head {
-   struct sk_buff		* volatile next;
-   struct sk_buff		* volatile prev;
- #if CONFIG_SKB_CHECK
-   int				magic_debug_cookie;
- #endif
- };
- 
- 
  struct sk_buff {
!   struct sk_buff		* volatile next;
!   struct sk_buff		* volatile prev;
! #if CONFIG_SKB_CHECK
!   int				magic_debug_cookie;
! #endif
!   struct sk_buff		* volatile link3;
!   struct sock			*sk;
!   volatile unsigned long	when;	/* used to compute rtt's	*/
!   struct timeval		stamp;
!   struct device			*dev;
!   void				*mem_addr;
!   union {
! 	struct tcphdr	*th;
! 	struct ethhdr	*eth;
! 	struct iphdr	*iph;
! 	struct udphdr	*uh;
! 	unsigned char	*raw;
! 	unsigned long	seq;
!   } h;
!   struct iphdr		*ip_hdr;		/* For IPPROTO_RAW */
!   unsigned long			mem_len;
!   unsigned long 		len;
!   unsigned long			fraglen;
!   struct sk_buff		*fraglist;	/* Fragment list */
!   unsigned long			truesize;
!   unsigned long 		saddr;
!   unsigned long 		daddr;
!   unsigned long			raddr;		/* next hop addr */
!   volatile char 		acked,
! 				used,
! 				free,
! 				arp;
!   unsigned char			tries,lock,localroute;
!   unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) */
!   unsigned long			padding[0];
!   unsigned char			data[0];
  };
  
- #define SK_WMEM_MAX	32767
- #define SK_RMEM_MAX	32767
  
! #ifdef CONFIG_SKB_CHECK
! #define SK_FREED_SKB	0x0DE2C0DE
! #define SK_GOOD_SKB	0xDEC0DED1
! #define SK_HEAD_SKB	0x12231298
! #endif
! 
! #ifdef __KERNEL__
! /*
!  *	Handling routines are only of interest to the kernel
!  */
!  
! #if 0
! extern void			print_skb(struct sk_buff *);
! #endif
! extern void			kfree_skb(struct sk_buff *skb, int rw);
! extern void			skb_queue_head_init(struct sk_buff_head *list);
! extern void			skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
! extern void			skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
! extern struct sk_buff *		skb_dequeue(struct sk_buff_head *list);
! extern void 			skb_insert(struct sk_buff *old,struct sk_buff *newsk);
! extern void			skb_append(struct sk_buff *old,struct sk_buff *newsk);
! extern void			skb_unlink(struct sk_buff *buf);
! extern struct sk_buff *		skb_peek_copy(struct sk_buff_head *list);
! extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
! extern void			kfree_skbmem(void *mem, unsigned size);
! extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
! extern void			skb_kept_by_device(struct sk_buff *skb);
! extern void			skb_device_release(struct sk_buff *skb,
! 					int mode);
! extern int			skb_device_locked(struct sk_buff *skb);
! /*
!  *	Peek an sk_buff. Unlike most other operations you _MUST_
!  *	be careful with this one. A peek leaves the buffer on the
!  *	list and someone else may run off with it. For an interrupt
!  *	type system cli() peek the buffer copy the data and sti();
!  */
! static __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
! {
! 	struct sk_buff *list = (struct sk_buff *)list_;
! 	return (list->next != list)? list->next : NULL;
! }
! 
! #if CONFIG_SKB_CHECK
! extern int 			skb_check(struct sk_buff *skb,int,int, char *);
! #define IS_SKB(skb)		skb_check((skb), 0, __LINE__,__FILE__)
! #define IS_SKB_HEAD(skb)	skb_check((skb), 1, __LINE__,__FILE__)
! #else
! #define IS_SKB(skb)		0
! #define IS_SKB_HEAD(skb)	0
! #endif
! 
! extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
! extern int			datagram_select(struct sock *sk, int sel_type, select_table *wait);
! extern void			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
! extern void			skb_free_datagram(struct sk_buff *skb);
  
! #endif	/* __KERNEL__ */
! #endif	/* _LINUX_SKBUFF_H */
--- 1,68 ----
  /*
!  * NET		An implementation of the SOCKET network access protocol.
   *
!  *		Definitions for the NET buffer management module.
!  *
!  * Version:	@(#)skbuff.h	1.2.0	03/15/94
!  *
!  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
!  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
!  *		Johannes Stille, <johannes@titan.westfalen.de>
!  *
!  *		Copyright 1992 Ross Biro
!  *		Copyright 1993,1994 Fred N. van Kempen
!  *
!  *		Portions Copyright 1993 Matthew C. Dillon
!  *
!  *		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.
   */
! #ifndef _LINUX_SK_BUFF_H
! #define _LINUX_SK_BUFF_H
  
  
  struct sk_buff {
!   struct sk_buff	*volatile next;	/* ptr to next skb		*/
!   unsigned long		mem_len;	/* address of skb		*/
!   void			*mem_addr;	/* total len of skb		*/
!   unsigned long 	len;		/* data len of skb		*/
!   void			*raw;		/* ptr into raw data		*/
!   struct ifnet		*dev;		/* ptr to DEV interface		*/
!   volatile unsigned char
! 			free,		/* free skb after use		*/
! 			lock;		/* skb is in use by DEV layer	*/
!   unsigned char		arp,		/* skb target is resolved	*/
! 			filler;		/* to align on 4-byte basis	*/
! 
!   /* For INET: */
!   struct sock		*sk;		/* ptr to INET socket		*/
!   struct sk_buff	*volatile qnext,/* ptr to next in backlog	*/
!   			*volatile qprev;/* ptr to prev in backlog	*/
! 
!   /* For IP: */
!   struct sockaddr	target;		/* routing target (for ARP)	*/
!   unsigned long		when,		/* used to compute rtt		*/
! 			saddr,		/* IP source address		*/
! 			daddr;		/* IP dest address		*/
! 
!   /* For TCP: */
!   struct sk_buff	*volatile link3;/* ???				*/
!   volatile char 	acked,		/* skb has been acked		*/
! 			urg_used;	/* ???				*/
!   unsigned long		seq;		/* seq# of data in this pkt	*/
!   unsigned char		used;		/* skb is in use		*/
!   unsigned long		tcp_dataindex;	/* index to base of data	*/
  };
  
  
! extern struct sk_buff	*skb_create(int len);
! extern struct sk_buff	*skb_pcreate(int len, int priority);
! extern void		skb_free(struct sk_buff *skb);
! extern void		skb_release(struct sk_buff *skb);
! extern struct sk_buff   *skb_duplicate(struct sk_buff *skb);
! extern void		skb_print(struct sk_buff *skb);
! 
  
! #endif	/* _LINUX_SK_BUFF_H */
*** original/include/linux/socket.h	Mon May  9 00:52:05 1994
--- linux/include/linux/socket.h	Mon May  9 13:12:55 1994
***************
*** 1,18 ****
  #ifndef _LINUX_SOCKET_H
  #define _LINUX_SOCKET_H
  
- #include <linux/sockios.h>		/* the SIOCxxx I/O controls	*/
- 
  
! struct sockaddr {
!   unsigned short	sa_family;	/* address family, AF_xxx	*/
!   char			sa_data[14];	/* 14 bytes of protocol address	*/
! };
  
- struct linger {
-   int 			l_onoff;	/* Linger active		*/
-   int			l_linger;	/* How long to linger for	*/
- };
  
  /* Socket types. */
  #define SOCK_STREAM	1		/* stream (connection) socket	*/
--- 1,31 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This is the master header file for the Linux NET layer,
+  *		or, in plain English: the networking handling part of the
+  *		kernel.
+  *
+  * Version:	@(#)socket.h	1.2.1	03/20/94
+  *
+  * Authors:	Orest Zborowski, <obz@Kodak.COM>
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *
+  *		Copyright 1991 Orest Zborowski
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
  #ifndef _LINUX_SOCKET_H
  #define _LINUX_SOCKET_H
  
  
! #include <linux/sockios.h>		/* the SIOCxxx I/O controls	*/
  
  
  /* Socket types. */
  #define SOCK_STREAM	1		/* stream (connection) socket	*/
***************
*** 20,45 ****
  #define SOCK_RAW	3		/* raw socket			*/
  #define SOCK_RDM	4		/* reliably-delivered message	*/
  #define SOCK_SEQPACKET	5		/* sequential packet socket	*/
- #define SOCK_PACKET	10		/* linux specific way of	*/
- 					/* getting packets at the dev	*/
- 					/* level.  For writing rarp and	*/
- 					/* other similiar things on the	*/
- 					/* user level.			*/
  
  /* Supported address families. */
  #define AF_UNSPEC	0
! #define AF_UNIX		1
! #define AF_INET		2
! #define AF_AX25		3
! #define AF_IPX		4
  
  /* Protocol families, same as address families. */
  #define PF_UNIX		AF_UNIX
  #define PF_INET		AF_INET
  #define PF_AX25		AF_AX25
  #define PF_IPX		AF_IPX
  
! /* Flags we can use with send/ and recv. */
  #define MSG_OOB		1
  #define MSG_PEEK	2
  #define MSG_DONTROUTE	4
--- 33,54 ----
  #define SOCK_RAW	3		/* raw socket			*/
  #define SOCK_RDM	4		/* reliably-delivered message	*/
  #define SOCK_SEQPACKET	5		/* sequential packet socket	*/
  
  /* Supported address families. */
  #define AF_UNSPEC	0
! #define AF_UNIX		1		/* UNIX domain sockets		*/
! #define AF_INET		2		/* INET (TCP/IP) domain sockets	*/
! #define AF_AX25		3		/* AX.25 domain sockets		*/
! #define AF_IPX		4		/* Novell IPX domain sockets	*/
  
  /* Protocol families, same as address families. */
+ #define PF_UNSPEC	AF_UNSPEC
  #define PF_UNIX		AF_UNIX
  #define PF_INET		AF_INET
  #define PF_AX25		AF_AX25
  #define PF_IPX		AF_IPX
  
! /* Flags we can use with send/recv. */
  #define MSG_OOB		1
  #define MSG_PEEK	2
  #define MSG_DONTROUTE	4
***************
*** 47,56 ****
  /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
  #define SOL_SOCKET	1
  #define SOL_IP		0
- #define SOL_IPX		256
- #define SOL_AX25	257
  #define SOL_TCP		6
  #define SOL_UDP		17
  
  /* For setsockoptions(2) */
  #define SO_DEBUG	1
--- 56,65 ----
  /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
  #define SOL_SOCKET	1
  #define SOL_IP		0
  #define SOL_TCP		6
  #define SOL_UDP		17
+ #define SOL_IPX		256
+ #define SOL_AX25	257
  
  /* For setsockoptions(2) */
  #define SO_DEBUG	1
***************
*** 67,92 ****
  #define SO_PRIORITY	12
  #define SO_LINGER	13
  
! /* IP options */
! #define IP_TOS		1
! #define	IPTOS_LOWDELAY		0x10
! #define	IPTOS_THROUGHPUT	0x08
! #define	IPTOS_RELIABILITY	0x04
! #define IP_TTL		2
! 
  /* IPX options */
  #define IPX_TYPE	1
! 
! /* AX.25 options */
! #define AX25_WINDOW	1
! 
! /* TCP options - this way around because someone left a set in the c library includes */
! #define TCP_NODELAY	1
! #define TCP_MAXSEG	2
  
  /* The various priorities. */
  #define SOPRI_INTERACTIVE	0
  #define SOPRI_NORMAL		1
  #define SOPRI_BACKGROUND	2
  
  #endif /* _LINUX_SOCKET_H */
--- 76,227 ----
  #define SO_PRIORITY	12
  #define SO_LINGER	13
  
! #if 0
  /* IPX options */
  #define IPX_TYPE	1
! #endif	/* move to <linux/ipx.h> -FvK */
  
  /* The various priorities. */
  #define SOPRI_INTERACTIVE	0
  #define SOPRI_NORMAL		1
  #define SOPRI_BACKGROUND	2
+ 
+ 
+ struct sockaddr {
+   unsigned short	sa_family;	/* address family, AF_xxx	*/
+   char			sa_data[14];	/* 14 bytes of protocol address	*/
+ };
+ 
+ struct linger {
+   int			l_onoff;	/* enable/disable LINGER	*/
+   int			l_linger;	/* how to linger for...		*/
+ };
+ 
+ 
+ #ifdef __KERNEL__
+ 
+ 
+ #include <linux/fs.h>			/* for "struct inode" et al	*/
+ #include <linux/wait.h>			/* for "struct wait_queue"	*/
+ 
+ 
+ #define NSOCKETS	1024		/* should be dynamic, later...	*/
+ #define NFAMILY		16		/* should be enough for now..	*/
+ 
+ #define	NET_MINOR	0		/* /dev/socket minor device	*/
+ #define ARP_MINOR	1		/* /dev/arp minor device	*/
+ #define ROUTE_MINOR	2		/* /dev/route minor device	*/
+ 
+ #define SYS_SOCKET	1		/* sys_socket(2)		*/
+ #define SYS_BIND	2		/* sys_bind(2)			*/
+ #define SYS_CONNECT	3		/* sys_connect(2)		*/
+ #define SYS_LISTEN	4		/* sys_listen(2)		*/
+ #define SYS_ACCEPT	5		/* sys_accept(2)		*/
+ #define SYS_GETSOCKNAME	6		/* sys_getsockname(2)		*/
+ #define SYS_GETPEERNAME	7		/* sys_getpeername(2)		*/
+ #define SYS_SOCKETPAIR	8		/* sys_socketpair(2)		*/
+ #define SYS_SEND	9		/* sys_send(2)			*/
+ #define SYS_RECV	10		/* sys_recv(2)			*/
+ #define SYS_SENDTO	11		/* sys_sendto(2)		*/
+ #define SYS_RECVFROM	12		/* sys_recvfrom(2)		*/
+ #define SYS_SHUTDOWN	13		/* sys_shutdown(2)		*/
+ #define SYS_SETSOCKOPT	14		/* sys_setsockopt(2)		*/
+ #define SYS_GETSOCKOPT	15		/* sys_getsockopt(2)		*/
+ 
+ 
+ typedef enum {
+   SS_FREE = 0,				/* not allocated		*/
+   SS_UNCONNECTED,			/* unconnected to any socket	*/
+   SS_CONNECTING,			/* in process of connecting	*/
+   SS_CONNECTED,				/* connected to socket		*/
+   SS_DISCONNECTING			/* in process of disconnecting	*/
+ } socket_state;
+ 
+ #define SO_ACCEPTCON	(1<<16)		/* performed a listen		*/
+ 
+ 
+ /*
+  * Internel representation of a socket. not all the fields are used by
+  * all configurations:
+  *
+  *		server			client
+  * conn		client connected to	server connected to
+  * iconn	list of clients		-unused-
+  *		 awaiting connections
+  * wait		sleep for clients,	sleep for connection,
+  *		sleep for i/o		sleep for i/o
+  */
+ struct socket {
+   short			type;		/* SOCK_STREAM, ...		*/
+   socket_state		state;
+   long			flags;
+   struct proto_ops	*ops;		/* protocols do most everything	*/
+   void			*data;		/* protocol data		*/
+   struct socket		*conn;		/* server socket connected to	*/
+   struct socket		*iconn;		/* incomplete client conn.s	*/
+   struct socket		*next;
+   struct wait_queue	**wait;		/* ptr to place to wait on	*/
+   struct inode		*inode;		/* inode of socket		*/
+ };
+ 
+ #define SOCK_INODE(S)	((S)->inode)
+ 
+ 
+ struct proto_ops {
+   int	family;
+ 
+   int	(*create)	(struct socket *sock, int protocol);
+   int	(*dup)		(struct socket *newsock, struct socket *oldsock);
+   int	(*release)	(struct socket *sock, struct socket *peer);
+   int	(*bind)		(struct socket *sock, struct sockaddr *umyaddr,
+ 			 int sockaddr_len);
+   int	(*connect)	(struct socket *sock, struct sockaddr *uservaddr,
+ 			 int sockaddr_len, int flags);
+   int	(*socketpair)	(struct socket *sock1, struct socket *sock2);
+   int	(*accept)	(struct socket *sock, struct socket *newsock,
+ 			 int flags);
+   int	(*getname)	(struct socket *sock, struct sockaddr *uaddr,
+ 			 int *usockaddr_len, int peer);
+   int	(*read)		(struct socket *sock, char *ubuf, int size,
+ 			 int nonblock);
+   int	(*write)	(struct socket *sock, char *ubuf, int size,
+ 			 int nonblock);
+   int	(*select)	(struct socket *sock, int sel_type,
+ 			 select_table *wait);
+   int	(*ioctl)	(struct socket *sock, unsigned int cmd,
+ 			 unsigned long arg);
+   int	(*listen)	(struct socket *sock, int len);
+   int	(*send)		(struct socket *sock, void *buff, int len, int nonblock,
+ 			 unsigned flags);
+   int	(*recv)		(struct socket *sock, void *buff, int len, int nonblock,
+ 			 unsigned flags);
+   int	(*sendto)	(struct socket *sock, void *buff, int len, int nonblock,
+ 			 unsigned flags, struct sockaddr *, int addr_len);
+   int	(*recvfrom)	(struct socket *sock, void *buff, int len, int nonblock,
+ 			 unsigned flags, struct sockaddr *, int *addr_len);
+   int	(*shutdown)	(struct socket *sock, int flags);
+   int	(*setsockopt)	(struct socket *sock, int level, int optname,
+ 			 char *optval, int optlen);
+   int	(*getsockopt)	(struct socket *sock, int level, int optname,
+ 			 char *optval, int *optlen);
+   int	(*fcntl)	(struct socket *sock, unsigned int cmd,
+ 			 unsigned long arg);	
+ };
+ 
+ 
+ extern int		sock_awaitconn(struct socket *mysock,
+ 				       struct socket *servsock);
+ 
+ extern struct proto_ops	*net_af_get(int family);
+ extern int		net_af_reg(int family, struct proto_ops *ops);
+ extern void		net_af_init(void);
+ 
+ extern void		net_add_minor(int minor, int (*ptr)(struct inode *,
+ 							    struct file *,
+ 							    unsigned int,
+ 							    unsigned long));
+ 
+ #endif	/* __KERNEL__ */
+ 
  
  #endif /* _LINUX_SOCKET_H */
*** original/include/linux/sockios.h	Mon May  9 00:51:10 1994
--- linux/include/linux/sockios.h	Mon May  9 13:12:55 1994
***************
*** 5,15 ****
   *
   *		Definitions of the socket-level I/O control calls.
   *
!  * Version:	@(#)sockios.h	1.0.2	03/09/93
   *
   * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
!  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *
   *		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
--- 5,18 ----
   *
   *		Definitions of the socket-level I/O control calls.
   *
!  * Version:	@(#)sockios.h	1.2.0	03/15/94
   *
   * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
!  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
   *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
   *		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
***************
*** 18,70 ****
  #ifndef _LINUX_SOCKIOS_H
  #define _LINUX_SOCKIOS_H
  
- /* This section will go away soon! */
  
  /* Socket-level I/O control calls. */
! #define FIOSETOWN 	0x8901
! #define SIOCSPGRP	0x8902
! #define FIOGETOWN	0x8903
! #define SIOCGPGRP	0x8904
! #define SIOCATMARK	0x8905
! #define SIOCGSTAMP	0x8096		/* Get stamp */
  
  /* Routing table calls. */
  #define SIOCADDRT	0x890B		/* add routing table entry	*/
  #define SIOCDELRT	0x890C		/* delete routing table entry	*/
  
! /* Socket configuration controls. */
! #define SIOCGIFNAME	0x8910		/* get iface name		*/
! #define SIOCSIFLINK	0x8911		/* set iface channel		*/
! #define SIOCGIFCONF	0x8912		/* get iface list		*/
  #define SIOCGIFFLAGS	0x8913		/* get flags			*/
  #define SIOCSIFFLAGS	0x8914		/* set flags			*/
! #define SIOCGIFADDR	0x8915		/* get PA address		*/
! #define SIOCSIFADDR	0x8916		/* set PA address		*/
! #define SIOCGIFDSTADDR	0x8917		/* get remote PA address	*/
! #define SIOCSIFDSTADDR	0x8918		/* set remote PA address	*/
! #define SIOCGIFBRDADDR	0x8919		/* get broadcast PA address	*/
! #define SIOCSIFBRDADDR	0x891a		/* set broadcast PA address	*/
! #define SIOCGIFNETMASK	0x891b		/* get network PA mask		*/
! #define SIOCSIFNETMASK	0x891c		/* set network PA mask		*/
! #define SIOCGIFMETRIC	0x891d		/* get metric			*/
! #define SIOCSIFMETRIC	0x891e		/* set metric			*/
! #define SIOCGIFMEM	0x891f		/* get memory address (BSD)	*/
  #define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
! #define SIOCGIFMTU	0x8921		/* get MTU size			*/
! #define SIOCSIFMTU	0x8922		/* set MTU size			*/
! #define	OLD_SIOCGIFHWADDR	0x8923		/* get hardware address		*/
! #define	SIOCSIFHWADDR	0x8924		/* set hardware address (NI)	*/
! #define SIOCGIFENCAP	0x8925		/* get/set slip encapsulation   */
! #define SIOCSIFENCAP	0x8926		
! #define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
! 
! /* Routing table calls (oldrtent - don't use) */
! #define SIOCADDRTOLD	0x8940		/* add routing table entry	*/
! #define SIOCDELRTOLD	0x8941		/* delete routing table entry	*/
  
- /* ARP cache control calls. */
- #define SIOCDARP	0x8950		/* delete ARP table entry	*/
- #define SIOCGARP	0x8951		/* get ARP table entry		*/
- #define SIOCSARP	0x8952		/* set ARP table entry		*/
  
  #endif	/* _LINUX_SOCKIOS_H */
--- 21,71 ----
  #ifndef _LINUX_SOCKIOS_H
  #define _LINUX_SOCKIOS_H
  
  
  /* Socket-level I/O control calls. */
! #define FIOSETOWN 	0x8901		/* set owner of socket		*/
! #define SIOCSPGRP	0x8902		/* set control TTY of socket	*/
! #define FIOGETOWN	0x8903		/* get current owner of socket	*/
! #define SIOCGPGRP	0x8904		/* get current TTY of socket	*/
! #define SIOCATMARK	0x8905		/* are we at an OOB mark?	*/
  
  /* Routing table calls. */
  #define SIOCADDRT	0x890B		/* add routing table entry	*/
  #define SIOCDELRT	0x890C		/* delete routing table entry	*/
  
! /* ARP cache control calls. */
! #define SIOCDARP	0x890D		/* delete ARP table entry	*/
! #define SIOCGARP	0x890E		/* get ARP table entry		*/
! #define SIOCSARP	0x890F		/* set ARP table entry		*/
! 
! /* Interface configuration controls. */
! #define SIOCGIFNAME	0x8910		/* get interface name		*/
! #define SIOCSIFUNUSED1	0x8911		/* NOT USED			*/
! #define SIOCGIFCONF	0x8912		/* get interface list		*/
  #define SIOCGIFFLAGS	0x8913		/* get flags			*/
  #define SIOCSIFFLAGS	0x8914		/* set flags			*/
! #define SIOCGIFADDR	0x8915		/* get interface proto address	*/
! #define SIOCSIFADDR	0x8916		/* set interface proto address	*/
! #define SIOCGIFDSTADDR	0x8917		/* get remote proto address	*/
! #define SIOCSIFDSTADDR	0x8918		/* set remote proto address	*/
! #define SIOCGIFBRDADDR	0x8919		/* get broadcast proto address	*/
! #define SIOCSIFBRDADDR	0x891A		/* set broadcast proto address	*/
! #define SIOCGIFNETMASK	0x891B		/* get network proto netmask	*/
! #define SIOCSIFNETMASK	0x891C		/* set network proto netmask	*/
! #define SIOCGIFMETRIC	0x891D		/* get metric			*/
! #define SIOCSIFMETRIC	0x891E		/* set metric			*/
! #define SIOCGIFMEM	0x891F		/* get memory address (BSD)	*/
  #define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
! #define SIOCGIFMTU	0x8921		/* get interface MTU size	*/
! #define SIOCSIFMTU	0x8922		/* set interface MTU size	*/
! #define SIOCGIFHWADDR	0x8923		/* get hardware address		*/
! #define SIOCSIFHWADDR	0x8924		/* set hardware address		*/
! #define SIOCGIFHWBRADDR	0x8925		/* get hardware bcast address	*/
! #define SIOCSIFHWBRADDR	0x8926		/* get hardware bcast address	*/
! #define SIOCGIFSTATS	0x8927		/* get interface statistics	*/
! #define SIOCSIFSTATS	0x8928		/* set interface statistics	*/
! #define SIOCGIFENCAP	0x8929		/* get iface link encap method	*/
! #define SIOCSIFENCAP	0x892A		/* set iface link encap method	*/
  
  
  #endif	/* _LINUX_SOCKIOS_H */
*** original/include/linux/termios.h	Tue Mar  1 18:00:15 1994
--- linux/include/linux/termios.h	Mon May  9 13:12:55 1994
***************
*** 239,243 ****
--- 239,244 ----
  #define N_SLIP		1
  #define N_MOUSE		2
  #define N_PPP		3
+ #define N_KISS		4
  
  #endif
*** original/include/linux/timer.h	Fri Feb 11 11:59:42 1994
--- linux/include/linux/timer.h	Tue May 10 14:41:35 1994
***************
*** 23,30 ****
   * 
   * SCSI_TIMER		scsi.c timeout timer
   *
-  * NET_TIMER		tcp/ip timeout timer
-  *
   * COPRO_TIMER		387 timeout for buggy hardware..
   *
   * TAPE_QIC02_TIMER	timer for QIC-02 tape driver (it's not hardcoded)
--- 23,28 ----
***************
*** 41,47 ****
  #define HD_TIMER	16
  #define FLOPPY_TIMER	17
  #define SCSI_TIMER 	18
- #define NET_TIMER	19
  #define SOUND_TIMER	20
  #define COPRO_TIMER	21
  
--- 39,44 ----
*** original/init/main.c	Mon May  9 00:52:15 1994
--- linux/init/main.c	Mon May  9 13:21:33 1994
***************
*** 75,87 ****
  extern long blk_dev_init(long,long);
  extern long chr_dev_init(long,long);
  extern void floppy_init(void);
! extern void sock_init(void);
  extern long rd_init(long mem_start, int length);
- unsigned long net_dev_init(unsigned long, unsigned long);
  
  extern void hd_setup(char *str, int *ints);
  extern void bmouse_setup(char *str, int *ints);
- extern void eth_setup(char *str, int *ints);
  extern void xd_setup(char *str, int *ints);
  extern void mcd_setup(char *str, int *ints);
  extern void st0x_setup(char *str, int *ints);
--- 75,87 ----
  extern long blk_dev_init(long,long);
  extern long chr_dev_init(long,long);
  extern void floppy_init(void);
! #ifdef CONFIG_NET
! extern void net_init(void);
! #endif
  extern long rd_init(long mem_start, int length);
  
  extern void hd_setup(char *str, int *ints);
  extern void bmouse_setup(char *str, int *ints);
  extern void xd_setup(char *str, int *ints);
  extern void mcd_setup(char *str, int *ints);
  extern void st0x_setup(char *str, int *ints);
***************
*** 170,178 ****
  	void (*setup_func)(char *, int *);
  } bootsetups[] = {
  	{ "reserve=", reserve_setup },
- #ifdef CONFIG_INET
- 	{ "ether=", eth_setup },
- #endif
  #ifdef CONFIG_BLK_DEV_HD
  	{ "hd=", hd_setup },
  #endif
--- 170,175 ----
***************
*** 402,410 ****
  	memory_start = blk_dev_init(memory_start,memory_end);
  	sti();
  	calibrate_delay();
- #ifdef CONFIG_INET
- 	memory_start = net_dev_init(memory_start,memory_end);
- #endif
  #ifdef CONFIG_SCSI
  	memory_start = scsi_dev_init(memory_start,memory_end);
  #endif
--- 399,404 ----
***************
*** 414,420 ****
  	buffer_init();
  	time_init();
  	floppy_init();
! 	sock_init();
  #ifdef CONFIG_SYSVIPC
  	ipc_init();
  #endif
--- 408,416 ----
  	buffer_init();
  	time_init();
  	floppy_init();
! #ifdef CONFIG_NET
! 	net_init();
! #endif
  #ifdef CONFIG_SYSVIPC
  	ipc_init();
  #endif
*** original/kernel/traps.c	Mon May  9 00:52:41 1994
--- linux/kernel/traps.c	Mon May  9 13:12:55 1994
***************
*** 235,237 ****
--- 235,254 ----
  	for (i=18;i<48;i++)
  		set_trap_gate(i,&reserved);
  }
+ 
+ /*
+  * Until memory synchronization works right, making sti/cli a 
+  * real subroutine fixes volatile synchronization problems.
+  */
+ 
+ void 
+ sti(void)
+ {
+     __asm__ __volatile__ ("sti": : :"memory");
+ }
+ 
+ void 
+ cli(void)
+ {
+     __asm__ __volatile__ ("cli": : :"memory");
+ }
*** original/net/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/Makefile	Mon May  9 13:12:57 1994
***************
*** 0 ****
--- 1,75 ----
+ #
+ # Makefile for the Linux NET-2 networking layer.
+ #
+ # 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 definition is now in the main makefile...
+ 
+ OBJS	    = Space.o
+ DRIVERS     = drv
+ 
+ CFLAGS	:= $(CFLAGS) #-DNETDBG
+ 
+ .c.o:
+ 	$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ .c.s:
+ 	$(CC) $(CFLAGS) -S $<
+ 
+ 
+ all:		net.o
+ 
+ net.o:		$(OBJS) subdirs
+ 		$(LD) -r -o net.o $(OBJS) $(DRIVERS)/$(DRIVERS).a network.a
+ 
+ subdirs:	$(DRIVERS) .subdirs dummy
+ 		@rm -f network.a
+ 		@for i in $(DRIVERS); do \
+ 			(cd $$i && echo $$i && $(MAKE)) || exit; \
+ 		 done
+ 		@for i in `cat .subdirs`; do \
+ 			(cd $$i && echo $$i && $(MAKE) && \
+ 			ar rcs ../network.a $$i.o) || exit; \
+ 		 done
+ 		@ranlib network.a
+ 
+ clean:		dummy .subdirs
+ 		@rm -f core *.o *.a *.s tmp_make
+ 		@for i in $(DRIVERS) `cat .subdirs`; do \
+ 			(cd $$i && echo $$i && $(MAKE) clean) || exit; \
+ 		 done
+ 		@rm -f .subdirs
+ 
+ mrproper:	dummy .subdirs
+ 		@rm -f core *.o *.a *.s tmp_make .depend
+ 		@for i in $(DRIVERS) `cat .subdirs`; do \
+ 			(cd $$i && echo $$i && $(MAKE) mrproper) || exit; \
+ 		 done
+ 		@rm -f .subdirs
+ 
+ dep:		dummy .subdirs
+ 		$(CPP) -M *.c > .depend
+ 		@for i in $(DRIVERS) `cat .subdirs`; do \
+ 			(cd $$i && echo $$i && $(MAKE) dep) || exit; \
+ 		 done
+ 
+ .subdirs:
+ 		@echo "Checking for directories..."
+ 		@> .subdirs
+ 		@for i in *; do \
+ 			if [ -d $$i -a $$i != "drv" -a -f $$i/Makefile ]; then \
+ 				echo $$i >> .subdirs; \
+ 			fi; \
+ 	 	done
+ 
+ dummy:
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/Space.c	Thu Jan  1 01:00:00 1970
--- linux/net/Space.c	Mon May  9 13:12:57 1994
***************
*** 0 ****
--- 1,306 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Define which protocol modules and I/O device drivers get
+  *		linked into the LINUX kernel.  Currently, this is only used
+  *		by the NET layer of LINUX, but it eventually might move to
+  *		an upper directory of the system.
+  *
+  * Version:	@(#)Space.c	1.1.6	03/31/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/config.h>
+ #include <linux/major.h>
+ #include <linux/types.h>
+ #include <linux/socket.h>
+ #include <linux/ddi.h>
+ 
+ 
+ /* Protocol module configuration. */
+ #define CONFIG_ARP		1	/* always present...		*/
+ #define CONFIG_ROUTE		1	/* always present...		*/
+ 
+ /* Device driver configuration. */
+ #define CONFIG_LOOPBACK		1	/* always present...		*/
+ #if defined(CONFIG_NE2000) || defined(CONFIG_WD80x3) || \
+     defined(CONFIG_ULTRA) || defined(CONFIG_EL1) || \
+     defined(CONFIG_EL2) || defined(CONFIG_EL3) || \
+     defined(CONFIG_HPLAN) || defined(CONFIG_AT1500) || \
+     defined(CONFIG_AT1700) || defined(CONFIG_ZNOTE) || \
+     defined(CONFIG_EEXPRESS) || defined(CONFIG_NI52) || \
+     defined(CONFIG_NI65) || defined(CONFIG_AC3200) || \
+     defined(CONFIG_E21) || defined(CONFIG_ATP)
+ #	define CONFIG_BECKER	1
+ #else
+ #	define CONFIG_BECKER	0
+ #endif
+ 
+ 
+ /*
+  * Section A:	Networking Protocol Handlers.
+  *		This section defines which networking protocols get
+  *		linked into the SOCKET layer of the Linux kernel.
+  *		Currently, these are ARP, AF_UNIX and AF_INET.
+  *
+  * "MODULE_NAME",	unused,		MAJOR_DEV,	MINOR_DEV,
+  *	unused,		unused,		unused,		unused,
+  *	unused,		unused,		unused,		INIT_FUNC
+  *
+  */
+ #if CONFIG_ARP
+ #   include "arp/arp.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_ROUTE
+ #   include "route/route.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_UNIX
+ #   include "unix/unix.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_INET
+ #   include "inet/inet.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_AX25
+ #   include "ax25/ax25.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_IPX
+ #   include "ipx/ipxcall.h"
+ #   undef DPRINTF
+ #endif
+ 
+ struct ddi protocols[] = {
+ #if CONFIG_ARP
+   {  "ARP",		0,		NET_MAJOR,	ARP_MINOR,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		arp_init
+   },
+ #endif
+ #if CONFIG_ROUTE
+   {  "ROUTE",		0,		NET_MAJOR,	ROUTE_MINOR,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		route_init
+   },
+ #endif
+ #if CONFIG_UNIX
+   { "UNIX",		0,		AF_UNIX_MAJOR,	0,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		unix_proto_init
+   },
+ #endif
+ #if CONFIG_INET
+   { "INET",		0,		AF_INET_MAJOR,	0,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		inet_proto_init
+   },
+ #endif
+ #if CONFIG_AX25
+   { "AX25",		0,		AX25_MAJOR,	0,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		ax25_proto_init
+   },
+ #endif
+ #if CONFIG_IPX
+   { "IPX",		0,		IPX_MAJOR,	0,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		ipx_proto_init
+   },
+ #endif
+ 
+   {  NULL,		-1,		-1,		-1,
+ 	-1,		-1,		-1,		-1,
+ 	-1,		0UL,		0UL,		NULL
+   }
+ };
+ 
+ 
+ /*
+  * Section B:	Device Driver Modules.
+  *		This section defines which network device drivers
+  *		get linked into the Linux kernel.  It is currently
+  *		only used by the INET protocol.  Any takers for the
+  *		other protocols like XNS or Novell?
+  *
+  * "MODULE_NAME",	#units,		MAJOR_DEV,	MINOR_DEV,
+  *	IO_ADDR,	IO2_ADDR,	IRQ_NUM,	IRQ2_NUM,
+  *	DMA_CHANNEL,	MEM_SIZE,	MEM_ADDR,	INIT_FUNC
+  *
+  */
+ 
+ #if CONFIG_LOOPBACK
+ #   include "drv/loop/loop.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_SLIP
+ #   include "drv/slip/slip.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_PPP
+ #   include "drv/ppp/ppp.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_KISS
+ #   include "drv/kiss/kiss.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_PLIP
+ #   include "drv/plip/plip.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_DE600
+ #   include "drv/dlink/dlink.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_BECKER
+ #   include "drv/becker/becker.h"
+ #   undef DPRINTF
+ #endif
+ #if CONFIG_DEPCA
+ #   include "drv/depca/depca.h"
+ #   undef DPRINTF
+ #endif
+ 
+ struct ddi devices[] = {
+ #if CONFIG_LOOPBACK
+   { "LOOP",		1,		NET_LOOP_MAJOR,	0,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		loop_init
+   },
+ #endif
+ #if CONFIG_SLIP
+   { "SLIP",		8,		NET_SLIP_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		slip_init
+   },
+ #endif
+ #if CONFIG_PPP
+   { "PPP",		8,		NET_PPP_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		ppp_init
+   },
+ #endif
+ #if CONFIG_KISS
+   { "KISS",		8,		NET_KISS_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		kiss_init
+   },
+ #endif
+ 
+ #if CONFIG_ULTRA
+   {  "BECKER",		BECARD_ULTRA,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_WD80x3
+   {  "BECKER",		BECARD_WD,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		16384UL,	0xD0000,	becker_init
+   },
+ #endif
+ #if CONFIG_NE2000
+   {  "BECKER",		BECARD_NE,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_HPLAN
+   {  "BECKER",		BECARD_HP,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_EL1
+   {  "BECKER",		BECARD_EL1,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_EL2
+   {  "BECKER",		BECARD_EL2,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_EL3
+   {  "BECKER",		BECARD_EL3,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_EL16
+   {  "BECKER",		BECARD_EL16,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_ELPLUS
+   {  "BECKER",		BECARD_ELPLUS,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_EEXPRESS
+   {  "BECKER",		BECARD_EEXP,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_ATP
+   {  "BECKER",		BECARD_ATP,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_ZNOTE
+   {  "BECKER",		BECARD_ZNOTE,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ #if CONFIG_AT1500
+   {  "BECKER",		BECARD_AT15,	NET_BECKER_MAJOR,	0,
+ 	DDI_IOAUTO,	0, /* BNC */	0,		0,
+ 	0,		0UL,		0UL,		becker_init
+   },
+ #endif
+ 
+ #if CONFIG_DE600
+   { "DE-600",		1,		NET_DLINK_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		dlink_init
+   },
+ #endif
+ #if CONFIG_DEPCA
+   { "DEPCA",		1,		NET_DEPCA_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0,		0UL,		depca_init
+   },
+ #endif
+ 
+ #if CONFIG_PLIP
+   { "PLIP",		3,		NET_PLIP_MAJOR,	0,
+ 	DDI_IOAUTO,	0,		0,		0,
+ 	0,		0UL,		0UL,		plip_init
+   },
+ #endif
+ 
+   {  NULL,		0,		0,		0,
+ 	0,		0,		0,		0,
+ 	0,		0UL,		0UL,		NULL
+   }
+ };
+ 
+ 
+ /* End of SPACE.C */
*** original/net/arp/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/arp/Makefile	Mon May  9 13:12:57 1994
***************
*** 0 ****
--- 1,38 ----
+ #
+ # Makefile for the Linux ARP protocol layer.
+ #
+ # 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 definition is now in the main makefile...
+ 
+ .c.o:
+ 	$(CC) $(CFLAGS) -c -o $*.o $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ .c.s:
+ 	$(CC) $(CFLAGS) -S -o $*.s $<
+ 
+ 
+ OBJS	= main.o ioctl.o utils.o packet.o cache.o queue.o
+ 
+ 
+ arp.o:		$(OBJS)
+ 		$(LD) -r -o arp.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.a *.s tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.a *.s tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/arp/arp.h	Thu Jan  1 01:00:00 1970
--- linux/net/arp/arp.h	Mon May  9 13:12:57 1994
***************
*** 0 ****
--- 1,59 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP local definitions.
+  *
+  * Version:	@(#)arp.h	1.4.1	01/20/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _ARP_H
+ #define _ARP_H
+ 
+ #include <linux/config.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ 
+ 
+ /* ARP feature selections. */
+ #define ARP_IS_SMART	0		/* use all incoming replies	*/
+ #define ARP_PROXY_OK	1		/* allow proxy stuff		*/
+ #define ARP_PROXY_MH	1		/* allow multi-home proxy stuff	*/
+ 
+ #define	ARP_MAX_TRIES	8		/* max #tries to resolve	*/
+ #define ARP_MAX_BACKLOG	16		/* max #packets allowed in que	*/
+ #define ARP_RES_TIME	(5 * HZ)	/* five seconds (resolve time)	*/
+ #define ARP_TRY_TIME	(10 * HZ)	/* ten seconds (queue time)	*/
+ #define ARP_MAX_TIME	(10 * 60 * HZ)	/* ten minutes (life time)	*/
+ 
+ 
+ #undef	ARP_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  ARP_DEBUG
+ #   define ARP_DEBUG
+ #endif
+ 
+ #ifdef	ARP_DEBUG
+ #   define DPRINTF(x)	arp_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ extern int		arp_init(struct ddi *ddi);
+ 
+ #endif	/* _ARP_H */
*** original/net/arp/cache.c	Thu Jan  1 01:00:00 1970
--- linux/net/arp/cache.c	Mon May  9 13:12:57 1994
***************
*** 0 ****
--- 1,460 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP cache handling functions.
+  *
+  * Version:	@(#)cache.c	1.4.4	02/20/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/proc_fs.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "arp.h"
+ #include "global.h"
+ 
+ 
+ static struct arp_table *volatile arp_cache = NULL;
+ 
+ 
+ static void arp_cache_expire(unsigned long arg);
+ 
+ 
+ /* Write the contents of the ARP cache to a PROCfs file. */
+ struct pagelist *
+ arp_get_info(void)
+ {
+   struct arp_table *apt;
+   unsigned char *ptr;
+   char *pos;
+   int i;
+   struct pagelist *fp, *pp;
+ 
+   fp = (struct pagelist *) __get_free_page(GFP_KERNEL);
+   if (fp == NULL) return(NULL);
+   pp = fp;
+   pp->page = (char *) __get_free_page(GFP_KERNEL);
+   if (pp->page == NULL) {
+ 	free_page((unsigned long) pp);
+ 	return(NULL);
+   }
+   pos = pp->page;
+   pp->next = NULL;
+   pp->prev = NULL;
+   cli();
+ 
+   /* Loop over the ARP table and copy structures to the buffer. */
+   for (apt = arp_cache; apt != NULL; apt = apt->next) {
+ 	DPRINTF(("ARP: get_info: 0x%08X -> 0x%08X\n", apt, apt->next));
+ 	if (pos > (pp->page + (PAGE_SIZE - 90))) {
+ 		pp->length = (long)pos - (long)pp->page;
+ 		pp->next = (struct pagelist *) __get_free_page(GFP_KERNEL);
+ 		if (pp->next == NULL) {
+ 			DPRINTF(("ARP: get_info: out of memory !\n"));
+ 			return(fp);
+ 		}
+ 		pp->next->prev = pp;
+ 		pp = pp->next;
+ 		pp->next = NULL;
+ 		pp->page = (char *) __get_free_page(GFP_KERNEL);
+ 		if (pp->page == NULL) {
+ 			DPRINTF(("ARP: get_info: out of memory !\n"));
+ 			pp->prev->next = NULL;
+ 			free_page((unsigned long) pp);
+ 			return(fp);
+ 		}
+ 		pos = pp->page;
+ 	}
+ 	ptr = (unsigned char *) &apt->sa;
+ 	for (i = 0; i < sizeof(struct sockaddr); i++)
+ 		pos += sprintf(pos, "%02X", (ptr[i] & 0377));
+ 	pos += sprintf(pos, "\t");
+ 	ptr = (unsigned char *) &apt->ha;
+ 	for (i = 0; i < sizeof(struct sockaddr); i++)
+ 		pos += sprintf(pos, "%02X", (ptr[i] & 0377));
+ 	pos += sprintf(pos, "\t%u\t%d\n",
+ 		(unsigned short) apt->flags, (apt->timer.expires / HZ));
+   }
+   pp->length = (long)pos - (long)pp->page;
+   sti();
+   return(fp);
+ }
+ 
+ 
+ /* Nuke an entry from the ARP cache. */
+ static void
+ arp_cache_kill(struct arp_table *apt, int forceit)
+ {
+   struct arp_table *ptr;
+ 
+   DPRINTF(("ARP: cache_kill:(0x%X, %d)\n", apt, forceit));
+   cli();
+ 
+   /* Reset expiration timer for this entry if in-use. */
+   if (apt->in_use) {
+ 	DPRINTF(("ARP: cannot nuke it: in_use = %d\n", apt->in_use));
+ 	del_timer(&apt->timer);
+ 	apt->timer.expires = ARP_RES_TIME;
+ 	apt->timer.function = arp_cache_expire;
+ 	apt->timer.data = (unsigned long) apt;
+ 	add_timer(&apt->timer);
+ 	sti();
+ 	return;
+   }
+ 
+   /* If this is a PERM entry, and we're not forcing it, leave it alone. */
+   if ((apt->flags & ATF_PERM) && (forceit == 0)) {
+ 	DPRINTF(("ARP: will not nuke a PERM entry without force!\n"));
+ 	sti();
+ 	return;
+   }
+ 
+   for (ptr = arp_cache; ptr != NULL; ptr = ptr->next) {
+ 	if (ptr == apt) {
+ 		/* Remove from the linked list. */
+ 		if (ptr->prev != NULL) ptr->prev->next = ptr->next;
+ 		  else arp_cache = ptr->next;
+ 		if (ptr->next != NULL) ptr->next->prev = ptr->prev;
+ 
+ 		/* Make sure the timer is gone... */
+ 		del_timer(&ptr->timer);
+ 		sti();
+ 
+ 		/* Release the memory. */
+ 		kfree_s(ptr, sizeof(struct arp_table));
+ 		return;
+ 	}
+   }
+   sti();
+ }
+ 
+ 
+ /* The timer called us because a cache entry expired.  Nuke it. */
+ static void
+ arp_cache_expire(unsigned long arg)
+ {
+   DPRINTF(("ARP: cache_expire: entry at 0x%X expired\n", arg));
+   arp_cache_kill((struct arp_table *) arg, 0);
+ }
+ 
+ 
+ /* This will find an entry in the ARP table by looking at the SAP. */
+ struct arp_table *
+ arp_cache_lookup(struct sockaddr *sap)
+ {
+   struct arp_table *apt;
+   struct arp_aftype *ap;
+   struct arp_hwtype *ht;
+ 
+   if (sap == NULL || sap == (struct sockaddr *) 0xF8F8F8F8) {
+ 	printk("ARP: ***BUG*** lookup: invalid address passed!\n");
+ 	return(NULL);
+   }
+ 
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: cache_lookup: unsupported address family %d\n",
+ 						sap->sa_family));
+ 	return(NULL);
+   }
+   DPRINTF(("ARP: cache_lookup(%s): ", ap->print(sap)));
+ 
+   /* Loop through the table for the desired address. */
+   cli();
+   for(apt = arp_cache; apt != NULL; apt = apt->next) {
+ 	if ((apt->flags & ATF_INUSE) == 0) continue;
+ 	if (ap->compare(sap, &apt->sa) == 1) {
+ 		apt->in_use++;
+ 		sti();
+ 		ht = arp_hwtype(apt->ha.sa_family);
+ 		DPRINTF(("%s\n", ht->print(&apt->ha)));
+ 		return(apt);
+ 	}
+   }
+   sti();
+   DPRINTF(("NONE\n"));
+   return(NULL);
+ }
+ 
+ 
+ /* This will find an entry in the ARP table by looking at the HAP. */
+ struct arp_table *
+ arp_cache_hlookup(struct sockaddr *hap, int af)
+ {
+   struct arp_table *apt;
+   struct arp_aftype *ap;
+   struct arp_hwtype *ht;
+ 
+   if (hap == NULL || hap == (struct sockaddr *) 0xF8F8F8F8) {
+ 	printk("ARP: ***BUG*** hlookup: invalid address passed!\n");
+ 	return(NULL);
+   }
+ 
+   ht = arp_hwtype(hap->sa_family);
+   if (ht == NULL) {
+ 	DPRINTF(("ARP: cache_hlookup: unsupported hardware type %d\n",
+ 							hap->sa_family));
+ 	return(NULL);
+   }
+   DPRINTF(("ARP: cache_hlookup(%s): ", ht->print(hap)));
+ 
+   /* Loop through the table for the desired address. */
+   cli();
+   for(apt = arp_cache; apt != NULL; apt = apt->next) {
+ 	if ((apt->flags & ATF_INUSE) == 0) continue;
+ 	if (! memcmp(hap->sa_data, apt->ha.sa_data, ht->alen)) {
+ 		apt->in_use++;
+ 		sti();
+ 		ap = arp_aftype(apt->sa.sa_family);
+ 		DPRINTF(("%s\n", ap->print(&apt->sa)));
+ 		return(apt);
+ 	}
+   }
+   sti();
+   DPRINTF(("NONE\n"));
+   return(NULL);
+ }
+ 
+ 
+ /* Create an ARP entry. */
+ struct arp_table *
+ arp_cache_create(struct sockaddr *sap, struct sockaddr *hap, int flags)
+ {
+   struct arp_table *apt;
+   struct arp_hwtype *ht;
+   struct arp_aftype *ap;
+   int new_entry = 0;
+ 
+   /* Fetch the Protocol Type. */
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: cache_create: unsupported address family %d\n",
+ 						sap->sa_family));
+ 	return(NULL);
+   }
+ 
+   /* Fetch the Hardware Type. */
+   if (hap != NULL) {
+ 	if ((ht = arp_hwtype(hap->sa_family)) == NULL) {
+ 		DPRINTF(("ARP: cache_create: unsupported hardware type %d\n",
+ 							hap->sa_family));
+ 		return(NULL);
+ 	}
+ 	DPRINTF(("ARP: cache_create(%s, ", ap->print(sap)));
+ 	DPRINTF(("%s, 0x%04X)\n", ht->print(hap), flags));
+   }
+ 
+   /* Check if we already have an entry for this address. */
+   if ((apt = arp_cache_lookup(sap)) == NULL) {
+ 	apt = (struct arp_table *)kmalloc(sizeof(struct arp_table), GFP_ATOMIC);
+ 	if (apt == NULL) {
+ 		DPRINTF(("ARP: cache_create: no memory available "));
+ 		DPRINTF(("for %s\n", ap->print(sap)));
+ 		return(NULL);
+ 	}
+ 	new_entry = 1;
+ 	memset(apt, 0, sizeof(struct arp_table));
+ 	cli();
+   	apt->in_use++;
+   }
+ 
+   cli();	/* in case we are changing an existing entry... */
+ 
+   /* Fill in the allocated ARP entry. */
+   apt->flags = (flags | ATF_INUSE);
+   if (hap != NULL) apt->flags |= ATF_COM;
+ 
+   /* Copy the protocol address. */
+   apt->plen = ap->alen;
+   memcpy(&apt->sa, sap, sizeof(struct sockaddr));
+ 
+   /* Copy the hardware address. */
+   if (hap != NULL) {
+ 	memcpy((char *) &apt->ha, (char *) hap, sizeof(struct sockaddr));
+   } else memset((char *) &apt->ha, 0, sizeof(struct sockaddr));
+ 
+   /*
+    * Set up an expiration timer for this entry.
+    * Note that this is only used for non-permanent cache
+    * entries, like those set by the arp(8) program. -FvK
+    */
+   del_timer(&apt->timer);
+   if (apt->flags & ATF_PERM) {
+ 	apt->timer.expires = 0;
+   } else {
+ 	if (hap == NULL) apt->timer.expires = ARP_RES_TIME;
+ 	  else apt->timer.expires = ARP_MAX_TIME;
+ 	apt->timer.function = arp_cache_expire;
+ 	apt->timer.data = (unsigned long) apt;
+ 	add_timer(&apt->timer);
+   }
+ 
+   if (new_entry) {
+ 	/*
+ 	 * Insert this entry into the ARP linked list.
+ 	 * FIXME: we should really do some form of sorting here, and use
+ 	 *        separate lists for each protocol family to speed up the
+ 	 *        table manipulation functions.  For now, we use a single
+ 	 *        double-linked list, which has unsorted entries.  We add
+ 	 *        new entries to the front of the list.
+ 	 */
+ 	apt->prev = NULL;
+ 	if (arp_cache == NULL) {
+ 		arp_cache = apt;
+ 		apt->next = NULL;
+ 	} else {
+ 		apt->next = arp_cache;
+ 		arp_cache->prev = apt;
+ 		arp_cache = apt;
+ 	}
+   }
+   sti();
+   DPRINTF(("ARP: cache_create: done (0x%X)\n", apt));
+   return(apt);
+ }
+ 
+ 
+ /* Delete an ARP entry in the cache. */
+ void
+ arp_cache_destroy(struct sockaddr *sap)
+ {
+   struct arp_table *apt;
+   struct arp_aftype *ap;
+ 
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: cache_destroy: unsupported address family %d\n",
+ 							sap->sa_family));
+ 	return;
+   }
+   DPRINTF(("ARP: cache_destroy(%s)\n", ap->print(sap)));
+ 
+   apt = arp_cache_lookup(sap);
+   if (apt != NULL) {
+ 	cli();
+ 	apt->in_use--;
+ 	arp_cache_kill(apt, 1);		/* does 'sti()' itself */
+   }	
+ }
+ 
+ 
+ /* Update an ARP cache entry as a result of a received message. */
+ void
+ arp_cache_update(struct sockaddr *sap, struct arp_aftype *ap,
+ 		 struct arp_hwtype *ht, struct sockaddr *hap)
+ {
+   struct arp_table *apt;
+ 
+   /* See if we already know about this address. */
+   if ((apt = arp_cache_lookup(sap)) != NULL) {
+ 	DPRINTF(("ARP: cache_update: %s --> ", ap->print(sap)));
+ 	DPRINTF(("%s\n", ht->print(hap)));
+ 	cli();
+ 	memcpy((char *) &apt->ha, (char *) hap, sizeof(struct sockaddr));
+ 	apt->flags |= ATF_COM;
+ 
+ 	/* Delete the "wait for completion" or "expiry" timer. */
+ 	del_timer(&apt->timer);
+ 
+ 	/* Set up the expiry timer instead. */
+ 	apt->timer.expires = ARP_MAX_TIME;
+ 	apt->timer.function = arp_cache_expire;
+ 	apt->timer.data = (unsigned long) apt;
+ 
+ 	add_timer(&apt->timer);
+ 	apt->in_use--;
+ 	sti();
+   } else {
+ 	DPRINTF(("ARP: cache_update: %s ==> ", ap->print(sap)));
+ 	DPRINTF(("%s\n", ht->print(hap)));
+ 	apt = arp_cache_create(sap, hap, 0);
+ 	if (apt) {
+ 		cli();
+ 		apt->in_use--;
+ 	}
+   }
+   sti();
+ }
+ 
+ 
+ /*
+  * Find an ARP mapping in the cache.  If we cannot resolve
+  * the address from our cache, post an ARP REQUEST and
+  * return a value to indicate this situation.
+  */
+ int
+ arp_find(struct sockaddr *sap, struct sockaddr *hap, struct ifnet *dev)
+ {
+   struct arp_table *apt;
+   struct arp_aftype *ap;
+   struct arp_hwtype *ht;
+   struct arpq *apq;
+ 
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: find: unsupported address family %d\n", sap->sa_family));
+ 	return(-EAFNOSUPPORT);
+   }
+   if ((ht = arp_hwtype(hap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: find: unsupported hardware type %d\n", hap->sa_family));
+ 	return(-EAFNOSUPPORT);
+   }
+ 
+   DPRINTF(("ARP: find(%s, ", ap->print(sap)));
+   DPRINTF(("0x%X, \"%s\")\n", hap, dev->if_name));
+ 
+   /* Check the ARP cache for this address. */
+   if ((apt = arp_cache_lookup(sap)) != NULL) {
+ 	if (apt->flags & ATF_COM) {
+ 		memcpy((char *) hap, (char *) &apt->ha, sizeof(struct sockaddr));
+ 		cli();
+ 		apt->in_use--;
+ 		sti();
+ 		return(0);	/* we found the desired info */
+ 	}
+ 	if (apt) {
+ 		cli();
+ 		apt->in_use--;
+ 		sti();
+ 	}
+   }
+ 
+   /* Not found, or not yet complete. Sniff... */
+   if ((apq = arp_queue_lookup(sap)) == NULL) {
+ 	/* Not waiting for a reply- post a REQUEST message! */
+ 	apt = arp_cache_create(sap, NULL, 0);
+ 	if (apt) {
+ 		cli();
+ 		apt->in_use--;
+ 		sti();
+ 	}
+ 	arp_request(sap, dev);
+   } else {
+   	cli();
+ 	apq->in_use--;
+ 	sti();
+   }
+ 
+   return(1);
+ }
*** original/net/arp/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/arp/global.h	Mon May  9 13:12:58 1994
***************
*** 0 ****
--- 1,77 ----
+ /*
+  * NET		An implementation of the BSD Socket Interface as the means of
+  *		communication with the user level for the Linux operating
+  *		system.
+  *
+  *		Definitions for ARP (RFC 826) and RARP (RFC 903).
+  *
+  * Version:	@(#)global.h	1.4.1	01/17/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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 structure defines the ARP mapping cache. */
+ struct arp_table {
+   struct arp_table	*next;			/* linked list pointers	*/
+   struct arp_table	*prev;
+   unsigned short	flags;			/* various flags	*/
+   volatile short	in_use;			/* don't delete		*/
+   struct timer_list	timer;			/* expiry timer		*/
+   struct sockaddr	sa;			/* protocol address	*/
+   unsigned char		plen;			/* protocol addr len	*/
+   struct sockaddr	ha;			/* hardware address	*/
+ };
+ 
+ /* An ARP queue entry. */
+ struct arpq {
+   struct arpq		*next;			/* linked list pointers	*/
+   struct arpq		*prev;
+   struct sockaddr	sa;			/* target address	*/
+   struct timer_list	timer;			/* "retry" timer	*/
+   volatile short	in_use;			/* don't delete		*/
+   struct ifnet		*dev;			/* device to send to	*/
+   int			retries;		/* retries counter	*/
+   int			backlog;		/* # of queued packets	*/
+   struct sk_buff	*skb;			/* list of packets	*/
+ };
+ 
+ 
+ extern struct arp_aftype  arp_af_0;		/* default AF handler	*/
+ extern struct arp_hwtype  arp_hw_0;		/* default HW handler	*/
+ extern struct packet_type arp_packet_type;	/* ARP pkt descriptor	*/
+ extern struct packet_type rarp_packet_type;	/* RARP pkt descriptor	*/
+ 
+ 
+ /* Packet I/O functions. */
+ extern void		arp_request(struct sockaddr *sap, struct ifnet *dev);
+ extern void		arp_queue_kick(struct sockaddr *sap);
+ extern struct arpq	*arp_queue_lookup(struct sockaddr *sap);
+ 
+ /* ARP table manipulation. */
+ extern void		arp_cache_destroy(struct sockaddr *sap);
+ extern struct arp_table	*arp_cache_lookup(struct sockaddr *sap);
+ extern struct arp_table	*arp_cache_hlookup(struct sockaddr *hap, int af);
+ extern struct arp_table	*arp_cache_create(struct sockaddr *sap,
+ 					  struct sockaddr *hap, int flags);
+ extern void		arp_cache_update(struct sockaddr *sap,
+ 					 struct arp_aftype *ap,
+ 					 struct arp_hwtype *ht,
+ 					 struct sockaddr *hap);
+ 
+ /* Various functions. */
+ extern struct arp_hwtype *arp_hwtype(int type);
+ extern struct arp_aftype *arp_aftype(int af);
+ extern struct arp_aftype *arp_afproto(int proto);
+ extern void		arp_print(char *name, struct arphdr *arp);
+ extern void		arp_dprintf(char *fmt, ...);
+ extern int		arp_dbg_ioctl(unsigned long arg, int level);
+ 
+ /* End of GLOBAL.H */
*** original/net/arp/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/arp/ioctl.c	Mon May  9 13:12:58 1994
***************
*** 0 ****
--- 1,153 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP I/O control handling functions.
+  *
+  * Version:	@(#)ioctl.c	1.4.1	01/12/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <asm/system.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <asm/segment.h>
+ #include "arp.h"
+ #include "global.h"
+ 
+ 
+ /* Set (create) an ARP cache entry. */
+ static int
+ arp_req_set(struct arpreq *r)
+ {
+   struct arp_table *apt;
+ 
+   /*
+    * Find out about the hardware type.
+    * We have to be compatible with BSD UNIX, so we have to
+    * assume that a "not set" value (i.e. 0) means Ethernet.
+    */
+   if (r->arp_ha.sa_family == 0) r->arp_ha.sa_family = ARPHRD_ETHER;
+ 
+   /* Create or update the ache entry. */
+   apt = arp_cache_create(&r->arp_pa, &r->arp_ha, r->arp_flags);
+   if (apt) {
+ 	cli();
+ 	apt->in_use--;
+ 	sti();
+   }
+   return(0);
+ }
+ 
+ 
+ /* Get an ARP cache entry. */
+ static int
+ arp_req_get(struct arpreq *r)
+ {
+   struct arp_table *apt;
+   struct arp_aftype *ap;
+ 
+   /* See if we know about this address family... */
+   if ((ap = arp_aftype(r->arp_pa.sa_family)) == NULL) {
+ 	DPRINTF(("ARP: get: unsupported address family %d\n",
+ 						r->arp_pa.sa_family));
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Is there an existing entry for this address? */
+   apt = arp_cache_lookup(&r->arp_pa);
+   if (apt == NULL) return(-ENXIO);
+ 
+   /* We found it; copy into structure. */
+   memcpy((char *) &r->arp_ha, (char *) &apt->ha, sizeof(struct sockaddr));
+ 
+   return(0);
+ }
+ 
+ 
+ /* Delete an ARP cache entry. */
+ static int
+ arp_req_del(struct arpreq *r)
+ {
+   struct arp_aftype *ap;
+ 
+   /* See if we know about this address family... */
+   if ((ap = arp_aftype(r->arp_pa.sa_family)) == NULL) {
+ 	DPRINTF(("ARP: del: unsupported address family %d\n",
+ 						r->arp_pa.sa_family));
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Tell the cache module to really nuke it. */
+   arp_cache_destroy(&r->arp_pa);
+ 
+   return(0);
+ }
+ 
+ 
+ /* Handle an ARP layer I/O control request. */
+ int
+ arp_ioctl(struct inode *inode, struct file *file,
+ 	  unsigned int cmd, unsigned long arg)
+ {
+   struct arpreq r;
+   int ret;
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (!suser()) return(-EPERM);
+ 		ret = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (ret < 0) return(ret);
+ 		return(arp_dbg_ioctl(arg, 1));
+ 
+ 	case SIOCDARP:
+ 		if (!suser()) return(-EPERM);
+ 		ret = verify_area(VERIFY_READ, (char *) arg,
+ 				  sizeof(struct arpreq));
+ 		if (ret < 0) return(ret);
+ 		memcpy_fromfs(&r, (char *) arg, sizeof(struct arpreq));
+ 		return(arp_req_del(&r));
+ 
+ 	case SIOCGARP:
+ 		ret = verify_area(VERIFY_WRITE, (char *) arg,
+ 				  sizeof(struct arpreq));
+ 		if (ret < 0) return(ret);
+ 		memcpy_fromfs(&r, (char *) arg, sizeof(struct arpreq));
+ 		ret = arp_req_get(&r);
+ 		memcpy_tofs((char *) arg, &r, sizeof(struct arpreq));
+ 		return(ret);
+ 
+ 	case SIOCSARP:
+ 		if (!suser()) return(-EPERM);
+ 		ret = verify_area(VERIFY_WRITE, (char *) arg,
+ 				  sizeof(struct arpreq));
+ 		if (ret < 0) return(ret);
+ 		memcpy_fromfs(&r, (char *) arg, sizeof(struct arpreq));
+ 		return(arp_req_set(&r));
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+ 
+   /*NOTREACHED*/
+   return(0);
+ }
*** original/net/arp/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/arp/main.c	Mon May  9 13:12:58 1994
***************
*** 0 ****
--- 1,135 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP main program and access functions.
+  *
+  * Version:	@(#)main.c	1.4.0	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "arp.h"
+ #include "global.h"
+ 
+ 
+ static struct arp_hwtype *arp_hw_types = &arp_hw_0;
+ static struct arp_aftype *arp_af_types = &arp_af_0;
+ 
+ 
+ /* Find an ARP hardware type. */
+ struct arp_hwtype *
+ arp_hwtype(int type)
+ {
+   struct arp_hwtype *ht;
+ 
+   ht = arp_hw_types;
+   while (ht != NULL) {
+ 	if (ht->type == type) return(ht);
+ 	ht = ht->next;
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Find an addess (protocol) family type. */
+ struct arp_aftype *
+ arp_aftype(int af)
+ {
+   struct arp_aftype *ap;
+ 
+   ap = arp_af_types;
+   while (ap != NULL) {
+ 	if (ap->af == af) return(ap);
+ 	ap = ap->next;
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Find an addess (protocol) family type by its ARP protocol ID. */
+ struct arp_aftype *
+ arp_afproto(int proto)
+ {
+   struct arp_aftype *ap;
+ 
+   ap = arp_af_types;
+   while (ap != NULL) {
+ 	if (ap->proto == proto) return(ap);
+ 	ap = ap->next;
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Add a new Address Family handler to the list. */
+ int
+ arp_add_af(struct arp_aftype *af)
+ {
+   cli();
+   af->next = arp_af_types;
+   arp_af_types = af;
+   sti();
+   return(0);
+ }
+ 
+ 
+ /* Add a new Hardware Type handler to the list. */
+ int
+ arp_add_hw(struct arp_hwtype *hw)
+ {
+   cli();
+   hw->next = arp_hw_types;
+   arp_hw_types = hw;
+   sti();
+   return(0);
+ }
+ 
+ 
+ /* Initialize the ARP module. */
+ int
+ arp_init(struct ddi *ddi)
+ {
+   int ret;
+ 
+ #ifdef NETDBG
+   printk("%s: device %d/%d, protocol IDs 0x%04X/0x%04X: ",
+ 		ddi->name, ddi->major, ddi->minor, ETH_P_ARP, ETH_P_RARP);
+ #endif
+ 
+   /* Tell SOCKET about our minor device. */
+   if (ddi->minor) {
+ 	(void) net_add_minor(ddi->minor, arp_ioctl);;
+   }
+ 
+   /* We should set the ARP protocol ID from the DDI struct... */
+   ret = net_add_packet(&arp_packet_type);
+   if (ret == 0) ret = net_add_packet(&rarp_packet_type);
+ 
+ #ifdef NETDBG
+   printk("%s\n", (ret == 0) ? "OK" : "ERROR");
+ #endif
+   return(ret);
+ }
*** original/net/arp/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/arp/packet.c	Mon May  9 13:12:58 1994
***************
*** 0 ****
--- 1,465 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP and RARP packet handling functions.
+  *
+  * Version:	@(#)packet.c	1.4.3	03/08/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/in.h>			/* for ntohs() et al - YUCK!	*/
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "arp.h"
+ #include "global.h"
+ 
+ 
+ /* Create and send an ARP or RARP packet. */
+ static void
+ arp_send(char *proto, int op, struct arp_aftype *ap,
+ 	 struct arp_hwtype *ht, struct ifnet *dev,
+ 	 struct sockaddr *psource, struct sockaddr *hsource,
+ 	 struct sockaddr *pdest, struct sockaddr *hdest)
+ {
+   struct sk_buff *skb;
+   struct arphdr *arp;
+   unsigned char *ptr;
+   int len, mac;
+ 
+   /* A sanity check. */
+   if (ap == NULL || ht == NULL) {
+ 	DPRINTF(("%s: send: ap or ht = NULL !\n", proto));
+ 	return;
+   }
+   if (dev == NULL || dev->if_addr == NULL) {
+ 	DPRINTF(("%s: send: dev%s = NULL !\n",
+ 			proto, dev==NULL ? "" : "->if_addr"));
+ 	return;
+   }
+ 
+   /* Set up some defaults.  This is UGLY, but inherent to ARP. */
+   if (hsource == NULL) hsource = &(dev->if_macaddr->ifa_addr);
+   if (psource == NULL) psource = &(dev->if_addr->ifa_addr);
+   if (hdest == NULL) hdest = &(dev->if_macaddr->ifa_broadaddr);
+ 
+   DPRINTF(("%s: send(%d, %s, ", proto, op, ap->print(psource)));
+   DPRINTF(("%s, ", ht->print(hsource)));
+   DPRINTF(("%s, ", ap->print(pdest)));
+   DPRINTF(("%s, ", ht->print(hdest)));
+   DPRINTF(("%s, 0x%X)\n", dev->if_name, ap->proto));
+ 
+   len = sizeof(struct arphdr) + (2 * ht->alen) +
+ 	(2 * ap->alen) + dev->if_machlen;
+   if ((skb = skb_create(len)) == NULL) {
+ 	DPRINTF(("%s: send: no memory available for %s\n",
+ 					proto, ap->print(pdest)));
+ 	return;
+   }
+   memset(skb->raw, 0, len);
+   
+   /* Fill in the request. */
+   skb->free = 1;
+   skb->arp = 1;
+   skb->dev = dev;
+ 
+   /* Create the MAC header, if any. */
+   if (dev->mac1 != NULL) {
+ 	if ((op == ARPOP_RREQUEST) || (op == ARPOP_RREPLY))
+ 		mac = dev->mac1(dev, skb, ETH_P_RARP, hsource, hdest);
+ 	  else
+ 		mac = dev->mac1(dev, skb, ETH_P_ARP, hsource, hdest);
+ 	if (mac < 0) {
+ 		DPRINTF(("%s: send: cannot create MAC header !\n", proto));
+ 		skb_free(skb);
+ 		return;
+ 	}
+   } else mac = 0;
+ 
+   /* Fill in the ARP message packet. */
+   arp = (struct arphdr *) ((unsigned char *) (skb+1) + mac);
+   arp->ar_op = htons(op);
+   arp->ar_hrd = htons(ht->type);
+   arp->ar_hln = ht->alen;
+   arp->ar_pro = htons(ap->proto);
+   arp->ar_pln = ap->alen;
+   ptr = ((unsigned char *) &arp->ar_op) + sizeof(unsigned short);
+   memcpy(ptr, hsource->sa_data, ht->alen);
+   ptr += ht->alen;
+   memcpy(ptr, ap->unsock(psource), ap->alen);
+   ptr += ap->alen;
+   if ((op == ARPOP_REQUEST) || (op == ARPOP_RREQUEST)) {
+ 	memset(ptr, 0, ht->alen);
+ 	ptr += ht->alen;
+   } else {
+ 	memcpy(ptr, hdest->sa_data, ht->alen);
+ 	ptr += ht->alen;
+   }
+   memcpy(ptr, ap->unsock(pdest), ap->alen);
+ 
+   DPRINTF((">>\n"));
+ #ifdef ARP_DEBUG
+   arp_print(proto, arp);
+ #endif
+   dev->send(dev, skb, NULL, 0);
+ }
+ 
+ 
+ /* A RARP packet has arrived, so process it. */
+ static void
+ rarp_recv(struct ifnet *dev, struct sk_buff *skb, struct packet_type *pt)
+ {
+   struct sockaddr s_ha;		/* sender hardware address */
+   struct sockaddr s_pa;		/* sender physical address */
+   struct sockaddr t_ha;		/* target hardware address */
+   struct sockaddr t_pa;		/* target physical address */
+   struct arphdr *arp;
+   struct arp_hwtype *ht;
+   struct arp_aftype *ap;
+   struct arp_table *apt;
+   unsigned char *ptr;
+ 
+   DPRINTF(("<<\n"));
+   arp = (struct arphdr *) skb->raw;
+ #ifdef ARP_DEBUG
+   arp_print("RARP", arp);
+ #endif
+ 
+   /* A little sanity check for packets that arrive early. */
+   if (dev == NULL || dev->if_addr == NULL) {
+ 	DPRINTF(("RARP: recv: dev%s = NULL !\n", dev==NULL ? "" : "->if_addr"));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* If this test doesn't pass, something fishy is going on. */
+   if ((unsigned char) arp->ar_hln != (unsigned char) dev->if_macalen ||
+       dev->if_macaddr->ifa_addr.sa_family != ntohs((unsigned int)arp->ar_hrd)) {
+ 	DPRINTF(("RARP: recv: invalid packet on \"%s\" !\n", dev->if_name));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Check if RARP is allowed on this interface. */
+   if (dev->if_flags & IFF_NOARP) {
+ 	DPRINTF(("RARP: recv: [R]ARP disabled on \"%s\" !\n", dev->if_name));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* See if we support this address family. */
+   if ((ap = arp_afproto(ntohs(arp->ar_pro))) == NULL) {
+ 	if (arp->ar_op != htons(ARPOP_REQUEST))
+ 		DPRINTF(("RARP: recv: unsupported address family %d\n",
+ 						ntohs(arp->ar_pro)));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* See if we support this type of hardware. */
+   if ((ht = arp_hwtype(ntohs(arp->ar_hrd))) == NULL) {
+ 	if (arp->ar_op != htons(ARPOP_REQUEST))
+ 		DPRINTF(("RARP: recv: unsupported hardware type %d\n",
+ 						ntohs(arp->ar_hrd)));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Point to the `variable data' area. */
+   ptr = ((unsigned char *) &arp->ar_op) + sizeof(unsigned short);
+   memcpy((char *) &s_ha.sa_data, ptr, ht->alen);
+   s_ha.sa_family = ht->type;
+   ptr += arp->ar_hln;
+   ap->mksock(&s_pa, ptr);
+ 
+   ptr += arp->ar_pln;
+   memcpy((char *) &t_ha.sa_data, ptr, ht->alen);
+   t_ha.sa_family = ht->type;
+   ptr += arp->ar_hln;
+   ap->mksock(&t_pa, ptr);
+ 
+   /*
+    * Check if the request was a RARP REQUEST for one of the
+    * addresses in our ARP cache.  This is cheating, but it
+    * is better than using an external RARP daemon IMHO -FvK
+    */
+   if (arp->ar_op != htons(ARPOP_RREQUEST)) {
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* It was.  See if we know this hardware address.... */
+   if ((apt = arp_cache_hlookup(&s_ha, t_pa.sa_family)) == NULL) {
+ 	DPRINTF(("RARP: recv: data for request not found !\n"));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Is this entry OK? */
+   if (! (apt->flags & ATF_COM)) {
+ 	DPRINTF(("RARP: recv: data for request not valid !\n"));
+ 	cli();
+ 	apt->in_use--;
+ 	sti();
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Are we allowed to use this entry for RARP? */
+   if (! (apt->flags & ATF_RARP)) {
+ 	DPRINTF(("RARP: recv: data available, but not for RARP.\n"));
+ 	cli();
+ 	apt->in_use--;
+ 	sti();
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Copy the data and release the entry. */
+   t_pa = apt->sa;
+   t_ha = apt->ha;
+   cli();
+   apt->in_use--;
+   sti();
+ 
+   /* Allocate, fill in and send a RARP REPLY packet. */
+   arp_send("RARP", ARPOP_RREPLY, ap, ht, dev, NULL, NULL, &t_pa, &t_ha);
+ 
+   skb_free(skb);
+ }
+ 
+ 
+ /* An ARP packet has arrived, so process it. */
+ static void
+ arp_recv(struct ifnet *dev, struct sk_buff *skb, struct packet_type *pt)
+ {
+   struct sockaddr s_ha;		/* sender hardware address */
+   struct sockaddr s_pa;		/* sender physical address */
+   struct sockaddr t_ha;		/* target hardware address */
+   struct sockaddr t_pa;		/* target physical address */
+   struct arphdr *arp;
+   struct arp_hwtype *ht;
+   struct arp_aftype *ap;
+ #if ARP_PROXY_OK
+   struct sockaddr *sap;
+   struct arp_table *apt;
+ #endif
+   unsigned char *ptr;
+ 
+   DPRINTF(("<<\n"));
+   arp = (struct arphdr *) skb->raw;
+ #ifdef ARP_DEBUG
+   arp_print("ARP", arp);
+ #endif
+ 
+   /* A little sanity check for packets that arrive early. */
+   if (dev == NULL || dev->if_addr == NULL) {
+ 	DPRINTF(("ARP: recv: dev%s = NULL !\n", dev==NULL ? "" : "->if_addr"));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* If this test doesn't pass, something fishy is going on. */
+   if ((unsigned char) arp->ar_hln != (unsigned char) dev->if_macalen ||
+       dev->if_macaddr->ifa_addr.sa_family != ntohs((unsigned int)arp->ar_hrd)) {
+ 	DPRINTF(("ARP: recv: invalid packet on \"%s\" !\n", dev->if_name));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Check if ARP is allowed on this interface. */
+   if (dev->if_flags & IFF_NOARP) {
+ 	DPRINTF(("ARP: recv: ARP disabled on \"%s\" !\n", dev->if_name));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* See if we support this address family. */
+   if ((ap = arp_afproto(ntohs(arp->ar_pro))) == NULL) {
+ 	if (arp->ar_op != htons(ARPOP_REQUEST))
+ 		DPRINTF(("ARP: recv: unsupported address family %d\n",
+ 						ntohs(arp->ar_pro)));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* See if we support this type of hardware. */
+   if ((ht = arp_hwtype(ntohs(arp->ar_hrd))) == NULL) {
+ 	if (arp->ar_op != htons(ARPOP_REQUEST))
+ 		DPRINTF(("ARP: recv: unsupported hardware type %d\n",
+ 						ntohs(arp->ar_hrd)));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* Point to the `variable data' area. */
+   ptr = ((unsigned char *) &arp->ar_op) + sizeof(unsigned short);
+   memcpy((char *) &s_ha.sa_data, ptr, ht->alen);
+   s_ha.sa_family = ht->type;
+   ptr += arp->ar_hln;
+   ap->mksock(&s_pa, ptr);
+ 
+   ptr += arp->ar_pln;
+   memcpy((char *) &t_ha.sa_data, ptr, ht->alen);
+   t_ha.sa_family = ht->type;
+   ptr += arp->ar_hln;
+   ap->mksock(&t_pa, ptr);
+ 
+   /*
+    * It is considered an optimization to use the information contained
+    * in incoming ARP packets for later use.  This is true, because it
+    * might save us the trouble of sending an ARP request ourselves.
+    * However, on large networks with lots of ARPing hosts, this can be
+    * a real memory killer, which is the reason for this code being set
+    * up as "configurable".  Turn it off if you find that your machine
+    * is using too much memory for the ARP cache, otherwise leave it on.
+    */
+   if (
+ #if ARP_IS_SMART
+ 	(1) 	/* so, always */
+ #else
+ 	(arp->ar_op == htons(ARPOP_REPLY))
+ #endif
+ 		) {
+ 	DPRINTF(("ARP: recv: update of %s --> ", ap->print(&s_pa)));
+ 	DPRINTF(("%s\n", ht->print(&s_ha)));
+ 	arp_cache_update(&s_pa, ap, ht, &s_ha);
+ 
+ 	/*
+ 	 * Since we updated the ARP cache, we might have enough
+ 	 * information to actually send out some of the packets
+ 	 * that we previously had to queue up for transmission...
+ 	 */
+ 	arp_queue_kick(&s_pa);
+   }
+ 
+   /* OK, now check if it was an ARP REQUEST... */
+   if (arp->ar_op != htons(ARPOP_REQUEST)) {
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /*
+    * It was.  The packet could be either for us, or for another system.
+    * We first check for our own address.  If that works, we're really
+    * happy and send our reply.  If not, we check if we still know about
+    * the address.  If that address mapping has been marked as PUBLISHED,
+    * we are allowed to send a reply even though it is now our own addres.
+    * This feature is called PROXY-ARP, and it *can* be useful at times.
+    */
+   if (ap->check(&t_pa) == 1) {
+ 	/* Allocate, fill in and send an ARP REPLY packet. */
+ 	arp_send("ARP", ARPOP_REPLY, ap, ht, dev, NULL, NULL, &s_pa, &s_ha);
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+ #if ARP_PROXY_OK
+   /* It was not for us, so check for proxy. */
+   if ((apt = arp_cache_lookup(&t_pa)) == NULL) {
+ 	DPRINTF(("ARP: no data available for request.\n"));
+ 	skb_free(skb);
+ 	return;
+   }
+ 
+   /* We at least know about the address.  Check if the PROXY flag is set. */
+   if (! (apt->flags & ATF_PROXY)) {
+ 	DPRINTF(("ARP: data available, but not usable for PROXY.\n"));
+ 	/* Release the cache entry. */
+ 	cli();
+ 	apt->in_use--;
+ 	sti();
+ 	skb_free(skb);
+ 	return;
+   }
+   sap = &apt->ha;
+ 
+ #if ARP_PROXY_MH
+   /*
+    * There is a problem with this code, though.
+    * This works well *unless* you are a multi-homed
+    * host with multiple Ethernet connections.  In
+    * that case, you'd probably don't want to use the
+    * ARP CACHE h/w address, but the h/w address from
+    * the interface that received the original REQUEST
+    * packet. -FvK
+    */
+   sap = NULL;	/* tell arp_send to use iface hw addr */
+ #endif	/* ARP_PROXY_MH */
+ 
+   /* Yes- we are allowed to do proxy arp here! */
+   arp_send("ARP", ARPOP_REPLY, ap, ht, dev, &apt->sa, sap, &s_pa, &s_ha);
+ 
+   /* Release the cache entry. */
+   cli();
+   apt->in_use--;
+   sti();
+ 
+ #endif	/* ARP_PROXY_OK */
+ 
+   skb_free(skb);
+ }
+ 
+ 
+ /* Send an ARP REQUEST packet for this SAP. */
+ void
+ arp_request(struct sockaddr *sap, struct ifnet *dev)
+ {
+   struct arp_aftype *ap;
+   struct arp_hwtype *ht;
+ 
+   DPRINTF(("ARP: request(sap=0x%X, dev=0x%X)\n", sap, dev));
+ 
+   /* See if we support this address family. */
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: request: unsupported address family %d\n",
+ 							sap->sa_family));
+ 	return;
+   }
+ 
+   /* See if we support this type of hardware. */
+   if ((ht = arp_hwtype(dev->if_macaddr->ifa_addr.sa_family)) == NULL) {
+ 	DPRINTF(("ARP: request: unsupported hardware type %d\n",
+ 					dev->if_macaddr->ifa_addr.sa_family));
+ 	return;
+   }
+ 
+   /* Send the message... */
+   arp_send("ARP", ARPOP_REQUEST, ap, ht, dev, NULL, NULL, sap, NULL);
+ }
+ 
+ 
+ struct packet_type arp_packet_type = {
+   "ARP",
+   ETH_P_ARP,
+   0,
+   arp_recv
+ };
+ struct packet_type rarp_packet_type = {
+   "RARP",
+   ETH_P_RARP,
+   0,
+   rarp_recv
+ };
*** original/net/arp/queue.c	Thu Jan  1 01:00:00 1970
--- linux/net/arp/queue.c	Mon May  9 13:12:58 1994
***************
*** 0 ****
--- 1,413 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP packet queue handling functions.
+  *
+  * Version:	@(#)queue.c	1.4.0	12/17/93
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "arp.h"
+ #include "global.h"
+ 
+ 
+ static struct arpq *volatile arp_q = NULL;
+ 
+ 
+ /*
+  * Nuke an entry from the ARP queue.
+  * This can be caused by either an entry timing out, or because one
+  * of the upper layers killed a socket which had some packets queued
+  * up here.  Called with interrupts off, are reenabled as soon as
+  * possible.
+  */
+ static void
+ arp_queue_kill(struct arpq *q)
+ {
+   struct sk_buff *next;
+   struct sk_buff *skb;
+   struct arpq *ptr;
+ 
+   DPRINTF(("ARP: queue_kill: nuking queue entry at 0x%X\n", q));
+   for(ptr = arp_q; ptr != NULL; ptr = ptr->next) {
+ 	if (ptr == q) {
+ 		/* Remove from the linked list. */
+ 		if (ptr->prev != NULL) ptr->prev->next = ptr->next;
+ 		  else arp_q = ptr->next;
+ 		if (ptr->next != NULL) ptr->next->prev = ptr->prev;
+ 
+ 		/* Make sure the timer is gone... */
+ 		del_timer(&ptr->timer);
+ 
+ 		/* We're safe now... */
+ 		sti();
+ 
+ 		/* Release the list of `skb' pointers. */
+ 		skb = ptr->skb;
+ 		while(skb != NULL) {
+ 			next = skb->next;
+ 			skb_release(skb);
+ 			skb = next;
+ 		}
+ 
+ 		/* Release the queue entry itself. */
+ 		kfree_s(ptr, sizeof(struct arpq));
+ 		return;
+ 	}
+   }
+   sti();
+ }
+ 
+ 
+ /*
+  * This will find an entry in the ARP queue by looking
+  * at the SAP. Increments in_use, caller must decrement
+  * after use!
+  */
+ struct arpq *
+ arp_queue_lookup(struct sockaddr *sap)
+ {
+   struct arp_aftype *ap;
+   struct arpq *q;
+ 
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: queue_lookup: unsupported address family %d\n",
+ 						sap->sa_family));
+ 	return(NULL);
+   }
+   DPRINTF(("ARP: queue_lookup(%s): ", ap->print(sap)));
+ 
+   /* Loop through the table for the desired address. */
+   cli();
+   for (q = arp_q; q != NULL; q = q->next) {
+ 	if (ap->compare(sap, &q->sa) == 1) {
+ 		q->in_use++;
+ 		sti();
+ 		DPRINTF(("found at 0x%X\n", q));
+ 		return(q);
+ 	}
+   }
+   sti();
+   DPRINTF(("NONE\n"));
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * The timer called us because a queue entry expired.
+  * This usually means that the entry timed out, which in turn probably
+  * means that the machine is down.  Further trying is no use, so simply
+  * free all packets in the queue and then free the entry itself.
+  */
+ static void
+ arp_queue_expire(unsigned long arg)
+ {
+   struct arp_aftype *ap;
+   struct arpq *q;
+ 
+   DPRINTF(("ARP: queue_expire: entry at 0x%X expired\n", arg));
+   q = (struct arpq *) arg;
+   ap = arp_aftype(q->sa.sa_family);
+ 
+   /* Decrease the "retry" counter, and see if we're dead... */
+   cli();
+   if (--q->retries <= 0 && !q->in_use) {
+ 	DPRINTF(("ARP: queue_expire: time-out, nuking queue for %s !\n",
+ 							ap->print(&q->sa)));
+ 	arp_queue_kill(q);	/* reenables interrupts */
+ 	return;
+   }
+   q->in_use++;
+ 
+   /* Prevent kicking (and deleting) this queue while we reset the timer. */
+   sti();
+ 
+   /* No, it's not time yet.  Give the guy another chance.. */
+   DPRINTF(("ARP: queue_expire: resending ARP REQUEST for %s (%d) !\n",
+ 			ap->print(&q->sa), (ARP_MAX_TRIES - q->retries)));
+   arp_request(&q->sa, q->dev);
+ 
+   /* Re-set the timer. */
+   del_timer(&q->timer);
+   q->timer.expires = ARP_TRY_TIME;
+   q->timer.function = arp_queue_expire;
+   q->timer.data = (unsigned long) q;
+   add_timer(&q->timer);
+   cli();
+   q->in_use--;
+   sti();
+ }
+ 
+ 
+ /*
+  * The contents of the ARP cache changed (possibly because we received
+  * an ARP REPLY packet, or by spying from other REQUEST packets on the
+  * network.) See if there were any packets waiting for this address to
+  * be resolved.  Send them out if we find any...
+  */
+ void
+ arp_queue_kick(struct sockaddr *sap)
+ {
+   struct sk_buff *skb;
+   struct arp_table *apt;
+   struct arpq *q;
+ 
+   /* A sanity check... */
+   if (sap == NULL) {
+ 	DPRINTF(("ARP: queue_kick(NULL)\n"));
+ 	return;
+   }
+ 
+   /* See if there are any packets waiting for this target. */
+   if ((q = arp_queue_lookup(sap)) == NULL) return;
+ 
+   /* Another sanity check.  One never knows... */
+   if (q->dev == NULL) {
+ 	DPRINTF(("ARP: queue_kick: q->dev == NULL (q=0x%X)\n", q));
+ 	cli();
+ 	q->in_use--;
+ 	sti();
+ 	return;
+   }
+ 
+   /* Lookup this target address. */
+   apt = arp_cache_lookup(sap);
+   if (apt == NULL) {	/* weird! */
+ 	cli();
+ 	q->in_use--;
+ 	sti();
+ 	return;
+   }
+ 
+   /* Yes, so disable their expiry timer. */
+   del_timer(&q->timer);
+ 
+   /* Loop over all packets in the queue, sending them out. */
+   cli();
+   while ((skb = q->skb)) {
+ 	q->backlog--;
+   	q->skb = skb->next;
+   	skb->next = NULL;
+ 	if (q->dev->mac2 != NULL) {
+ 		q->dev->mac2(q->dev, skb, &apt->ha);	/* rebuild MAC	*/
+ 		skb->arp = 2;	/* just being sent by ARP module! */
+ 		sti();
+ 		q->dev->send(q->dev, skb, NULL, 0);	/* boink!	*/
+ 		cli();
+ 	} else {
+ 		printk("ARP: entry resolved, but no MAC2 !!!\n");
+ 	}
+   }
+ 
+   apt->in_use--;
+   if (--q->in_use) {	/* catch race condition */
+ 	DPRINTF(("ARP: cannot kill queue: in_use = %d\n", q->in_use));
+ 	sti();
+   } else {
+ 	/* Yes, so disable their expiry timer. */
+ 	del_timer(&q->timer);
+ 
+ 	/* All packets in this queue have been sent.  Nuke the queue. */
+ 	arp_queue_kill(q);		/* re-enables interrupts */
+   }
+ }
+ 
+ 
+ /*
+  * Create a new entry for the ARP packet queue.
+  * Set in_use = 1, caller must decrement it.
+  */
+ static struct arpq *
+ arp_queue_create(struct sockaddr *sap, struct ifnet *dev)
+ {
+   struct arpq *q;
+ 
+   /* Allocate some memory for this queue entry. */
+   q = (struct arpq *) kmalloc(sizeof(struct arpq), GFP_ATOMIC);
+   if (q == NULL) {
+ 	DPRINTF(("ARP: queue_create: no memory for new entry\n"));
+ 	return(NULL);
+   }
+   DPRINTF(("ARP: queue_create: new entry at 0x%X\n", q));
+ 
+   /* Fill in the ARP queue entry. */
+   q->retries = ARP_MAX_TRIES;
+   q->dev = dev;
+   q->skb = NULL;
+   q->backlog = 0;
+   q->in_use = 1;
+ 
+   memcpy(&q->sa, sap, sizeof(struct sockaddr));
+ 
+   /* Put this entry into the linked list. */
+   cli();
+ 
+   /* Set the timer. */
+   q->timer.expires = ARP_TRY_TIME;
+   q->timer.function = arp_queue_expire;
+   q->timer.data = (unsigned long) q;
+   add_timer(&q->timer);
+ 
+   q->prev = NULL;
+   if (arp_q == NULL) {
+ 	arp_q = q;
+ 	q->next = NULL;
+   } else {
+ 	q->next = arp_q;
+ 	arp_q->prev = q;
+ 	arp_q = q;
+   }
+   sti();
+ 
+   return(q);
+ }
+ 
+ 
+ /*
+  * An upper layer found out that it could not resolve the address
+  * for a packet, and wants us to queue the packet for transmission
+  * whenever the address gets resolved.  To do this with a bit of
+  * thought, we look up (and, if not found, create) the entry for
+  * the address desired, and then add this packet to the list of
+  * packets that share the same target address.
+  *
+  * If the address is found to be resolved after we queue the packet,
+  * we need to kick the queue.  This can and does occur all the time.
+  */
+ void
+ arp_queue(struct sk_buff *skb, struct sockaddr *sap)
+ {
+   struct arp_aftype *ap;
+   struct arp_table *apt;
+   struct arpq *q;
+ 
+   /* A little sanity check... */
+   if (sap == NULL || skb == NULL) {
+ 	DPRINTF(("ARP: queue(NULL): skb=0x%X sap=0x%X\n", skb, sap));
+ 	return;
+   }
+   skb->arp = 0;
+ 
+   /* Do we support this kind of packets? */
+   if ((ap = arp_aftype(sap->sa_family)) == NULL) {
+ 	DPRINTF(("ARP: queue: unsupported address family %d\n",
+ 							sap->sa_family));
+ 	skb_release(skb);
+ 	return;
+   }
+   DPRINTF(("ARP: queue(0x%X, %s)\n", skb, ap->print(sap)));
+ 
+   /* See if we already have an entry for this target. */
+   if ((q = arp_queue_lookup(sap)) == NULL) {
+ 	q = arp_queue_create(sap, skb->dev);
+ 	if (q == NULL) {
+ 		DPRINTF(("ARP: queue: no memory for skb at 0x%X\n", skb));
+ 		skb_release(skb);
+ 		return;
+ 	}
+   }
+ 
+   /* See if we are allowed to add another packet (thanks, Jac!) */
+   if (q->backlog >= ARP_MAX_BACKLOG) {
+ 	DPRINTF(("ARP: queue: backlog limit of entry 0x%X reached !\n", q));
+ 	skb_release(skb);
+ 	cli();
+ 	q->in_use--;
+ 	sti();
+ 	return;
+   }
+   cli();
+   q->backlog++;
+ 
+   /* Add this packet to the backlog. */
+   skb->next = q->skb;
+   q->skb = skb;
+   q->in_use--;
+   sti();
+ 
+   /* Kick the queue if we got resolved in the mean time. */
+   apt = arp_cache_lookup(sap);
+   if (apt != NULL) {
+ 	if (apt->flags & ATF_COM) {
+ 		DPRINTF(("ARP: going too fast, running too hard, "
+ 					"gonna have a heart attack\n"));
+ 		arp_queue_kick(sap);
+ 	}
+ 	cli();
+ 	apt->in_use--;
+ 	sti();
+   }
+ }
+ 
+ 
+ /*
+  * An upper layer killed a socket, and found that we might have some
+  * of its packets left in our queue, waiting to have their address
+  * resolved.  Those packets are of no use anymore, so we can kill
+  * them from any lists.
+  */
+ void
+ arp_delque(struct sk_buff *skb)
+ {
+   struct arpq *q, *next;
+   struct sk_buff *s, *ss, *prev;
+ 
+   cli();
+   q = arp_q;
+   while(q != NULL) {				/* for all targets	*/
+ 	next = q->next;
+ 	s = q->skb;
+ 	prev = NULL;
+ 	while(s != NULL) {			/* check all packets	*/
+ 		ss = s->next;
+ 		if (s == skb) {			/* match!		*/
+ 			if (prev != NULL) prev->next = s->next;
+ 			  else q->skb = s->next;
+ 
+ 			/* Decrease the backlog. */
+ 			q->backlog--;
+ 			if (q->backlog == 0) {
+ 				DPRINTF(("ARP: queue_del: nothing left, zapping!\n"));
+ 				arp_queue_kill(q);
+ 			}
+ 			sti();
+ 			skb_release(skb);
+ 			return;
+ 		}
+ 		prev = s;
+ 		s = ss;
+ 	}
+ 
+ 	/* Try next target queue. */
+ 	q = next;
+   }
+   sti();
+ }
*** original/net/arp/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/arp/utils.c	Mon May  9 13:12:58 1994
***************
*** 0 ****
--- 1,206 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the Address Resolution Protocol
+  *		(ARP), which can be used by protocol layers to convert
+  *		their addresses into hardware addresses suitable for
+  *		low-level device drivers like Ethernet.
+  *
+  *		ARP various utility functions.
+  *
+  * Version:	@(#)utils.c	1.4.0	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/in.h>			/* for ntohs() et al - YUCK!	*/
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <stdarg.h>
+ #include "arp.h"
+ #include "global.h"
+ 
+ 
+ extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+ 
+ /* Forward declarations. */
+ static char	*arp_hprint(struct sockaddr *);
+ static char	*arp_pprint(struct sockaddr *);
+ 
+ 
+ static int arp_debug = 0;		/* ARP debugging flag		*/
+ #ifdef ARP_DEBUG
+ static char *arp_cmds[] = {
+   "0x%04X\n",
+   "REQUEST\n",
+   "REPLY\n",
+   "REVERSE REQUEST\n",
+   "REVERSE REPLY\n",
+   NULL
+ };
+ #define	ARP_MAX_CMDS	(sizeof(arp_cmds) / sizeof(arp_cmds[0]))
+ #endif
+ struct arp_hwtype arp_hw_0 = {
+   0,	"0x%04X",	14,	arp_hprint,	NULL
+ };
+ struct arp_aftype arp_af_0 = {
+   "0x%04X",	0,	14,	0,	arp_pprint,
+   NULL,	NULL,	NULL,	NULL,	NULL
+ };
+ 
+ 
+ /* Dump an UNSPEC hw address. */
+ static char *
+ arp_hprint(struct sockaddr *sap)
+ {
+   static char buff[64];
+   char *bufp = buff;
+   int i;
+ 
+   if (sap == NULL) return("[NULL]");
+   bufp += sprintf(bufp, "%d:", sap->sa_family);
+   for (i = 0; i < 14; i++)
+ 	bufp += sprintf(bufp, "%02X ", (sap->sa_data[i] & 0377));
+   return(buff);
+ }
+ 
+ 
+ /* Dump an AF_UNSPEC address. */
+ static char *
+ arp_pprint(struct sockaddr *sap)
+ {
+   static char buff[64];
+   char *bufp = buff;
+   int i;
+ 
+   if (sap == NULL) return("[NULL]");
+   bufp += sprintf(bufp, "%d:", sap->sa_family);
+   for (i = 0; i < 14; i++)
+ 	bufp += sprintf(bufp, "%02X ", (sap->sa_data[i] & 0377));
+   return(buff);
+ }
+ 
+ 
+ /* Dump an ARP packet. Not complete yet for non-Ethernet packets. */
+ #ifdef ARP_DEBUG
+ void
+ arp_print(char *name, struct arphdr *arp)
+ {
+   struct arp_hwtype *ht;
+   struct arp_aftype *at;
+   struct sockaddr sa;
+   unsigned char *ptr;
+   int len;
+ 
+   if (arp_debug == 0) return;
+ 
+   printk("%s: ", name);
+   if (arp == NULL) {
+ 	printk("[NULL]\n");
+ 	return;
+   }
+ 
+   /* Print the opcode name. */
+   len = ntohs(arp->ar_op);
+   if (len >= ARP_MAX_CMDS) len = 0;
+   printk("op ");
+   printk(arp_cmds[len], ntohs(arp->ar_op));
+ 
+   /* Print the ARP header. */
+   ht = arp_hwtype(ntohs(arp->ar_hrd));
+   if (ht == NULL) ht = arp_hwtype(0);
+ 
+   /* Find the protocol family type. */
+   at = arp_afproto(ntohs(arp->ar_pro));
+   if (at == NULL) at = arp_aftype(0);
+ 
+   printk("     hrd="); printk(ht->name, ht->alen);
+   printk(" pro="); printk(at->name, at->alen);
+   printk(" hlen=%d  plen=%d\n", arp->ar_hln, arp->ar_pln);
+ 
+   /* Check the HLEN and PLEN parameters. */
+   if (ht->alen != arp->ar_hln) {
+ 	printk("     weird: HWlen %d != ARP Hlen %d !\n",
+ 					ht->alen, arp->ar_hln);
+   }
+   if (at->alen != arp->ar_pln) {
+ 	printk("     weird: AFlen %d != ARP Plen %d !\n",
+ 					at->alen, arp->ar_pln);
+   }
+ 
+   /* Print the variable data. */
+   ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
+   memcpy(sa.sa_data, ptr, ht->alen);
+   sa.sa_family = ht->type;
+   printk("     sender HA=%s ", ht->print(&sa));
+   ptr += arp->ar_hln;
+   at->mksock(&sa, ptr);
+   printk("  PA=%s\n", at->print(&sa));
+   ptr += arp->ar_pln;
+   memcpy(sa.sa_data, ptr, ht->alen);
+   sa.sa_family = ht->type;
+   printk("     target HA=%s ", ht->print(&sa));
+   ptr += arp->ar_hln;
+   at->mksock(&sa, ptr);
+   printk("  PA=%s\n", at->print(&sa));
+ }
+ 
+ 
+ void
+ arp_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+ 
+   if (arp_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* ARP_DEBUG */
+ 
+ 
+ int
+ arp_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		arp_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		arp_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			arp_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/drv/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/Makefile	Mon May  9 13:12:59 1994
***************
*** 0 ****
--- 1,61 ----
+ #
+ # Makefile for the Linux network device drivers.
+ #
+ # 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 $<
+ 
+ drv.a:		dummy .subdirs
+ 		@rm -f drv.a
+ 		@for i in `cat .subdirs`; do \
+ 			(cd $$i && echo $$i && $(MAKE) && \
+ 			 $(AR) rcs ../drv.a $$i.o) || \
+ 			exit; \
+ 		 done
+ 
+ clean:		dummy .subdirs
+ 		@rm -f core *.o *.s *.a tmp_make
+ 		@for i in `cat .subdirs`; do \
+ 			(cd $$i && $(MAKE) clean); \
+ 		 done
+ 		@rm -f .subdirs
+ 
+ mrproper:	dummy .subdirs
+ 		@rm -f core *.o *.s *.a tmp_make .depend
+ 		@for i in `cat .subdirs`; do \
+ 			(cd $$i && $(MAKE) mrproper); \
+ 		 done
+ 		@rm -f .subdirs
+ 
+ dep:		dummy .subdirs
+ 		@> .depend
+ 		@for i in `cat .subdirs`; do \
+ 			(cd $$i && echo $$i && $(MAKE) dep) || exit; \
+ 		 done
+ 
+ .subdirs:
+ 		@echo "Checking for directories..."
+ 		@> .subdirs
+ 		@for i in *; do \
+ 			if [ -d $$i -a -f $$i/Makefile ]; then \
+ 				echo $$i >> .subdirs; \
+ 			fi; \
+ 	 	done
+ 
+ dummy:
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/becker/3c501.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/3c501.c	Mon May  9 13:12:59 1994
***************
*** 0 ****
--- 1,501 ----
+ /* 3c501.c: A 3Com 3c501 ethernet driver for linux. */
+ /*
+     Copyright (C) 1992,1993  Donald Becker
+ 
+     Copyright 1993 United States Government as represented by the
+     Director, National Security Agency.  This software may be used and
+     distributed according to the terms of the GNU Public License,
+     incorporated herein by reference.
+ 
+     This is a device driver for the 3Com Etherlink 3c501.
+     Do not purchase this card, even as a joke.  It's performance is horrible,
+     and it breaks in many ways.  
+ 
+     The Author may be reached as becker@super.org or
+     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+     I'll only accept bug fixes, not reports, for the 3c501 driver.
+ */
+ 
+ /*
+   Braindamage remaining:
+   The 3c501 board.
+   */
+ 
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/ptrace.h>
+ #include <linux/fcntl.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/ioport.h>
+ #include <linux/interrupt.h>
+ #include <linux/malloc.h>
+ #include <linux/ioport.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <errno.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ 
+ #ifdef notdef
+ static char *version =
+ 	"3c501.c: 3/3/94 Donald Becker (becker@super.org).\n";
+ #endif
+ 
+ 
+ #define RX_STATUS	(ei->iobase + 0x06)
+ #define RX_CMD		RX_STATUS
+ #define TX_STATUS	(ei->iobase + 0x07)
+ #define TX_CMD		TX_STATUS
+ #define GP_LOW		(ei->iobase + 0x08)
+ #define GP_HIGH		(ei->iobase + 0x09)
+ #define RX_BUF_CLR	(ei->iobase + 0x0A)
+ #define RX_LOW		(ei->iobase + 0x0A)
+ #define RX_HIGH		(ei->iobase + 0x0B)
+ #define SAPROM		(ei->iobase + 0x0C)
+ #define AX_STATUS	(ei->iobase + 0x0E)
+ #define AX_CMD		AX_STATUS
+ #define DATAPORT	(ei->iobase + 0x0F)
+ #define TX_RDY		0x08		/* In TX_STATUS */
+ 
+ #define EL1_DATAPTR	0x08
+ #define EL1_RXPTR	0x0A
+ #define EL1_SAPROM	0x0C
+ #define EL1_DATAPORT 	0x0F
+ 
+ /* Writes to the ax command register. */
+ #define AX_OFF		0x00		/* Irq off, buffer access on */
+ #define AX_SYS		0x40		/* Load the buffer */
+ #define AX_XMIT		0x44		/* Transmit a packet */
+ #define AX_RX		0x48		/* Receive a packet */
+ #define AX_LOOP		0x0C		/* Loopback mode */
+ #define AX_RESET	0x80
+ 
+ /*
+  * Normal receive mode written to RX_STATUS.  We must intr on
+  * short packets to avoid bogus rx lockups.
+  */
+ #define RX_NORM		0xA8	/* 0x68 == all addrs, 0xA8 only to me. */
+ #define RX_PROM		0x68	/* Senior Prom, uhmm promiscuous mode. */
+ #define RX_MULT		0xE8	/* Accept multicast packets. */
+ #define TX_NORM		0x0A	/* Interrupt on everything that might hang the chip */
+ 
+ /* TX_STATUS register. */
+ #define TX_COLLISION	0x02
+ #define TX_16COLLISIONS	0x04
+ #define TX_READY	0x08
+ 
+ #define RX_RUNT		0x08
+ #define RX_MISSED	0x01	/* Missed a packet due to 3c501 braindamage. */
+ #define RX_GOOD		0x30	/* Good packet 0x20, or simple overflow 0x10. */
+ 
+ 
+ static struct ei *el1_irq2ei_map[16];
+ 
+ 
+ /*
+  * Hard reset the card.  This used to pause for the same
+  * period that a 8390 reset command required, but that
+  * shouldn't be necessary.
+  */
+ static void
+ el1_reset(struct ei *ei)
+ {
+   unsigned char *ptr;
+   int i;
+ 
+   DPRINTF((DBG_EL1, "3C501 reset..."));
+ 
+   outb(AX_RESET, AX_CMD);	/* reset the chip */
+   outb(AX_LOOP, AX_CMD);	/* aux control, IRQ and loopback enabled */
+ 
+   /* Set the station address. */
+   ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+   for(i = 0; i < ETH_ALEN; i++) {
+ 	outb(ptr[i], ei->ioaddr + i);
+   }
+ 
+   outb(0, RX_BUF_CLR);		/* set rx packet area to 0 */
+   cli();			/* avoid glitch on writes to CMD regs */
+   outb(TX_NORM, TX_CMD);	/* tx irq on done, collision */
+   outb(RX_NORM, RX_CMD);	/* set Rx commands */
+   inb(RX_STATUS);		/* clear status */
+   inb(TX_STATUS);
+   ei->interrupt = 0;
+   ei->dev->if_tbusy = 0;
+   sti();
+ }
+ 
+ 
+ static int
+ el1_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int length, send_length;
+     
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in el1_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+ 
+   if (dev->if_tbusy) {
+ 	if (jiffies - dev->if_txtimer < 20) {
+ 		DPRINTF((DBG_EL1, "%s: transmitter busy, deferred.\n",
+ 							dev->if_name));
+ 		return(1);
+ 	}
+ 	printk("%s: transmit timed out, txsr 0x%2X axsr=0x%02X rxsr=0x%02X\n",
+ 		dev->if_name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
+ 
+ 	dev->if_stats->tx_errors++;
+ 	outb(TX_NORM, TX_CMD);
+ 	outb(RX_NORM, RX_CMD);
+ 	outb(AX_OFF, AX_CMD);	/* just trigger a false interrupt */
+ 	outb(AX_RX, AX_CMD);	/* aux control, irq and receive enabled */
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+   }
+ 
+   /*
+    * Some higher layer thinks we've missed an tx-done interrupt.
+    * Caution: dev_tint() handles the cli()/sti() itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   if (skb->len <= 0) return(0);
+   length = skb->len;
+   send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+ 
+   /* Avoid timer-based retransmission conflicts. */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 	printk("%s: transmitter access conflict.\n", dev->if_name);
+     else {
+ 	int gp_start = 0x800 - send_length;
+ 
+ 	ei->tx_start_page = gp_start;
+ 	dev->if_stats->tx_collision = 0;
+ 
+ 	outb(AX_SYS, AX_CMD);
+ 	inb(RX_STATUS);
+ 	inb(TX_STATUS);
+ 	outb(0x00, RX_BUF_CLR);	/* set rx packet area to 0 */
+ 	outw(gp_start, GP_LOW);
+ 	outsb(DATAPORT, skb->raw, skb->len);
+ 	outw(gp_start, GP_LOW);
+ 	outb(AX_XMIT, AX_CMD);		/* trigger xmit  */
+ 	dev->if_txtimer = jiffies;
+   }
+   DPRINTF((DBG_EL1, "%s: queued xmit.\n", dev->if_name));
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * We have a good packet. Well, not really "good", just mostly
+  * not broken.  We must check everything to see if it is good.
+  */
+ static void
+ el1_receive(struct ei *ei)
+ {
+   struct sk_buff *skb;
+   int pkt_len;
+ 
+   pkt_len = inw(RX_LOW);
+   DPRINTF((DBG_EL1, "%s: receive(%d)\n", ei->dev->if_name, pkt_len));
+ 
+   /* Is packet size OK? */
+   if ((pkt_len < ETH_ZLEN) || (pkt_len > ETH_FRAME_LEN)) {
+ 	DPRINTF((DBG_EL1, "%s: bogus packet, length=%d\n",
+ 					ei->dev->if_name, pkt_len));
+ 	ei->dev->if_stats->rx_overrun++;
+ 	return;
+   }
+   outb(AX_SYS, AX_CMD);
+ 
+   /* Yes, it was. Allocate an skb. */
+   skb = skb_create(pkt_len);
+   outw(0x00, GP_LOW);
+   if (skb == NULL) {
+ 	DPRINTF((DBG_EL1, "%s: couldn't allocate a sk_buff of size %d.\n",
+ 				ei->dev->if_name, pkt_len));
+ 	ei->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+   skb->dev = ei->dev;
+   skb->len = pkt_len;
+ 
+   /* Pull the packet data from the card. */
+   insb(DATAPORT, (void *)skb->raw, pkt_len);
+ 
+   /* Tell the NET layer a new packet is available. */
+   if (dev_rint(ei->dev, skb, pkt_len)) {
+ 	ei->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+   ei->dev->if_stats->rx_packets++;
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the ether interface interrupts.
+  */
+ static void
+ el1_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = el1_irq2ei_map[irq];
+   int axsr;
+ 
+   if (ei == NULL) {
+         printk("3C501: interrupt: IRQ%d for unknown device.\n", irq);
+  	return;
+   }
+   if (ei->irq != irq) {
+ 	printk("3C501: IRQ%d for unknown device.\n", irq);
+ 	return;
+   }
+   axsr = inb(AX_STATUS);
+ 
+   DPRINTF((DBG_EL1, "%s: interrupt() aux=0x%02X", ei->dev->if_name, axsr));
+   if (ei->interrupt)
+ 	printk("%s: reentering the interrupt driver!\n", ei->dev->if_name);
+   ei->interrupt = 1;
+ 
+   if (ei->dev->if_tbusy) {
+ 	int txsr = inb(TX_STATUS);
+ 
+ 	DPRINTF((DBG_EL1, "%s: txsr=0x%02X gp=0x%04X rp=0x%04X",
+ 		ei->dev->if_name, txsr, inw(GP_LOW), inw(RX_LOW)));
+ 
+ 	if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
+ 		printk("%s: unusual interrupt during TX, txsr=0x%02X axsr=0x%02X gp=0x%03X rp=0x%03X\n",
+ 			ei->dev->if_name, txsr, axsr,
+ 			inw(ei->iobase + EL1_DATAPTR),
+ 			inw(ei->iobase + EL1_RXPTR));
+ 		ei->dev->if_tbusy = 0;
+ 		mark_bh(NET_BH);
+ 	} else if (txsr & TX_16COLLISIONS) {
+ 		printk("%s: transmit failed 16 times, Ethernet jammed?\n",
+ 							ei->dev->if_name);
+ 		outb(AX_SYS, AX_CMD);
+ 		ei->dev->if_stats->tx_aborted++;
+ 	} else if (txsr & TX_COLLISION) {	/* retrigger xmit */
+ 		printk(" retransmitting after a collision\n");
+ 		outb(AX_SYS, AX_CMD);
+ 		outw(ei->tx_start_page, GP_LOW);
+ 		outb(AX_XMIT, AX_CMD);
+ 		ei->dev->if_stats->tx_collision++;
+ 		ei->interrupt = 0;
+ 		return;
+ 	}
+ 	ei->dev->if_stats->tx_packets++;
+ 	printk(" TX succeeded %s\n",
+ 		(txsr & TX_RDY) ? "" : "but TX is busy!");
+ 	ei->dev->if_tbusy = 0;
+ 	mark_bh(NET_BH);
+   } else {
+ 	int rxsr = inb(RX_STATUS);
+ 	DPRINTF((DBG_EL1, "%s: rxsr=0x%02X txsr=0x%02X rp=0x%04X",
+ 			ei->dev->if_name, rxsr, inb(TX_STATUS), inw(RX_LOW)));
+ 
+ 	/* Just reading rx_status fixes most errors. */
+ 	if (rxsr & RX_MISSED) ei->dev->if_stats->rx_missed++;
+ 	if (rxsr & RX_RUNT) {	/* handled to avoid board lock-up */
+ 		ei->dev->if_stats->rx_length++;
+ 		DPRINTF((DBG_EL1, "%s: runt\n", ei->dev->if_name));
+ 	} else if (rxsr & RX_GOOD) {
+ 	    el1_receive(ei);
+ 	} else {	/* nothing?  Something is broken! */
+ 		DPRINTF((DBG_EL1,
+ 			"%s: No packet seen, rxsr=0x%02X, resetting 3C501\n",
+ 				ei->dev->if_name, rxsr));
+ 		el1_reset(ei);
+ 	}
+   }
+ 
+   outb(AX_RX, AX_CMD);
+   outb(0x00, RX_BUF_CLR);
+   (void) inb(RX_STATUS);	/* be certain that interrupts are cleared */
+   (void) inb(TX_STATUS);
+   ei->interrupt = 0;
+ }
+ 
+ 
+ static int
+ el1_close(struct ei *ei)
+ {
+   DPRINTF((DBG_EL1, "%s: shutting down ethercard at 0x%03X\n",
+ 					ei->dev->if_name, ei->ioaddr));
+   ei->dev->if_tbusy = 1;
+ 
+   /* Free and disable the IRQ. */
+   free_irq(ei->irq);
+   outb(AX_RESET, AX_CMD);	/* reset the chip */
+   el1_irq2ei_map[ei->irq] = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /* Open/initialize the board. */
+ static int
+ el1_open(struct ei *ei)
+ {
+   DPRINTF((DBG_EL1, "%s: doing el1_open()...\n", ei->dev->if_name));
+ 
+   if (request_irq(ei->irq, el1_interrupt)) {
+ 	DPRINTF((DBG_EL1, "%s: interrupt busy, exiting el1_open().\n",
+ 							ei->dev->if_name));
+ 	return(-EAGAIN);
+   }
+   el1_irq2ei_map[ei->irq] = ei;
+ 
+   el1_reset(ei);
+ 
+   outb(AX_RX, AX_CMD);	/* aux control, irq and receive enabled */
+   DPRINTF((DBG_EL1, "%s: finished el1_open()...\n", ei->dev->if_name));
+  
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ el1_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   if (num_addrs > 0) {
+ 	outb(RX_MULT, RX_CMD);
+ 	inb(RX_STATUS);		/* clear status */
+   } else if (num_addrs < 0) {
+ 	outb(RX_PROM, RX_CMD);
+ 	inb(RX_STATUS);
+   } else {
+ 	outb(RX_NORM, RX_CMD);
+ 	inb(RX_STATUS);
+   }
+ }
+ 
+ 
+ static int
+ el1probe1(int ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int autoirq = 0;
+   int i;
+ 
+   DPRINTF((DBG_EL1, "3C501 ethercard probe at 0x%3X:", ioaddr));
+ 
+   /* Read the station address PROM data from the special port. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	outw(i, ioaddr + EL1_DATAPTR);
+ 	sa.sa_data[i] = inb(ioaddr + EL1_SAPROM);
+ 	DPRINTF((DBG_EL1, " %02.2X", sa.sa_data[i]));
+   }
+ 
+   /* Check the first three octets of the S.A. for 3Com's code. */ 
+   if ((unsigned char)sa.sa_data[0] != (unsigned char)0x02 ||
+       (unsigned char)sa.sa_data[1] != (unsigned char)0x60 ||
+       (unsigned char)sa.sa_data[2] != (unsigned char)0x8c) {
+ 	DPRINTF((DBG_EL1, " not found (bad SA)\n", sa.sa_data[i]));
+ 	return(-ENODEV);
+   }
+ 
+   /* Grab the region so we can find another board if autoIRQ fails. */
+   snarf_region(ioaddr, 16);
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+ 
+   /* We auto-IRQ by shutting off the intr line and letting it float high.  */
+   if (ei->irq < 2) {
+ 	autoirq_setup(2);
+ 
+ 	(void) inb(RX_STATUS);		/* clear pending interrupts */
+ 	(void) inb(TX_STATUS);
+ 	outb(AX_LOOP + 1, AX_CMD);
+ 
+ 	outb(0x00, AX_CMD);
+ 
+ 	autoirq = autoirq_report(1);
+ 	if (autoirq == 0) {
+ 		printk("%s: 3C501 probe failed to detect IRQ line.\n",
+ 							ei->dev->if_name);
+ 		return(-ENODEV);
+ 	}
+ 	ei->irq = autoirq;
+   }
+ 
+   /* OKOK, so not all people know this... */
+   if (ei->irq == 2) ei->irq = 9;
+ 
+   outb(AX_RESET+AX_LOOP, AX_CMD);		/* loopback mode */
+ 
+   /* Set up the chip-specific stuff. We don't have any... */
+   ei->pingpong = 0;
+   ei->sigaction = NULL;
+   ei->chip_init = NULL;
+   ei->chip_open = NULL;
+   ei->chip_s_multi = el1_set_multicast;
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = el1_xmit;
+ 
+   DPRINTF((DBG_EL1,
+ 	"%s: 3C501 at 0x%3X, using %sIRQ %d\n",
+ 	ei->dev->if_name, ei->ioaddr, autoirq ? "auto":"assigned ", ei->irq));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = el1_reset;
+   ei->card_open = el1_open;
+   ei->card_close = el1_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ int
+ el1_probe(struct ei *ei)
+ {
+   static int el1_card_nr = 0;
+   int *port, ports[] = { 0x280, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "3C501";
+   sprintf(ei->dev->if_name, "el%d", el1_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(el1probe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	if (check_region(*port, 16)) continue;
+ 
+ 	/* Check if a card is present. */
+ 	if (el1probe1(*port, ei) == 0) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/becker/3c503.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/3c503.c	Mon May  9 13:12:59 1994
***************
*** 0 ****
--- 1,509 ----
+ /* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
+ /*
+     Written 1992,1993 by Donald Becker.
+ 
+     Copyright 1993 United States Government as represented by the
+     Director, National Security Agency.  This software may be used and
+     distributed according to the terms of the GNU Public License,
+     incorporated herein by reference.
+ 
+     This driver should work with the 3c503 and 3c503/16.  It should be used
+     in shared memory mode for best performance, although it may also work
+     in programmed-I/O mode.
+ 
+     The Author may be reached as becker@super.org or
+     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/ioport.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/string.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ #if 0
+ static char *version =
+ 	"3c503.c:v0.99.15k 3/3/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ #define EL2H		(ei->iobase + 0x400)
+ #define EL2L		(ei->iobase)
+ 
+ 
+ /* Shared memory management parameters */
+ #define EL2SM_START_PG	(0x20)		/* First page of TX buffer */
+ #define EL2SM_STOP_PG	(0x40)		/* Last page +1 of RX ring */
+ #define EL2_MEMSIZE	(EL2SM_STOP_PG - EL2SM_START_PG)*256
+ 
+ /* 3Com 3C503 ASIC registers */
+ #define E33G_STARTPG	(EL2H+0)	/* Start page, matching EN0_STARTPG */
+ #define E33G_STOPPG	(EL2H+1)	/* Stop page, must match EN0_STOPPG */
+ #define E33G_DRQCNT	(EL2H+2)	/* DMA burst count */
+ #define E33G_IOBASE	(EL2H+3)	/* Read of I/O base jumpers. */
+ 	/* (non-useful, but it also appears at the end of EPROM space) */
+ #define E33G_ROMBASE	(EL2H+4)	/* Read of memory base jumpers. */
+ #define E33G_GACFR	(EL2H+5)	/* Config/setup bits for the ASIC GA */
+ #define E33G_CNTRL	(EL2H+6)	/* Board's main control register */
+ #define E33G_STATUS	(EL2H+7)	/* Status on completions. */
+ #define E33G_IDCFR	(EL2H+8)	/* Interrupt/DMA config register */
+ 				/* (Which IRQ to assert, DMA chan to use) */
+ #define E33G_DMAAH	(EL2H+9)	/* High byte of DMA address reg */
+ #define E33G_DMAAL	(EL2H+10)	/* Low byte of DMA address reg */
+ 
+ /*
+  * "Vector pointer" - if this address matches a read, the EPROM
+  * (rather than shared RAM) is mapped into memory space.
+  */
+ #define E33G_VP2	(EL2H+11)
+ #define E33G_VP1	(EL2H+12)
+ #define E33G_VP0	(EL2H+13)
+ #define E33G_FIFOH	(EL2H+14)	/* FIFO for programmed I/O moves */
+ #define E33G_FIFOL	(EL2H+15)	/* ... low byte of above. */
+ 
+ /* Bits in E33G_CNTRL register: */
+ #define ECNTRL_RESET	(0x01)	/* Software reset of the ASIC and 8390 */
+ #define ECNTRL_THIN	(0x02)	/* Onboard xcvr enable, AUI disable */
+ #define ECNTRL_AUI	(0x00)	/* Onboard xcvr disable, AUI enable */
+ #define ECNTRL_SAPROM	(0x04)	/* Map the station address prom */
+ #define ECNTRL_DBLBFR	(0x20)	/* FIFO configuration bit */
+ #define ECNTRL_OUTPUT	(0x40)	/* PC-to-3C503 direction if 1 */
+ #define ECNTRL_INPUT	(0x00)	/* 3C503-to-PC direction if 0 */
+ #define ECNTRL_START	(0x80)	/* Start the DMA logic */
+ 
+ /* Bits in E33G_STATUS register: */
+ #define ESTAT_DPRDY	(0x80)	/* Data port (of FIFO) ready */
+ #define ESTAT_UFLW	(0x40)	/* Tried to read FIFO when it was empty */
+ #define ESTAT_OFLW	(0x20)	/* Tried to write FIFO when it was full */
+ #define ESTAT_DTC	(0x10)	/* Terminal Count from PC bus DMA logic */
+ #define ESTAT_DIP	(0x08)	/* DMA In Progress */
+ 
+ /* Bits in E33G_GACFR register: */
+ #define EGACFR_NORM	(0x49)	/* Enable 8K shared mem, no DMA TC int */
+ #define EGACFR_IRQOFF	(0xc9)	/* Above, and disable 8390 IRQ line */
+ 
+ 
+ static int ports[] = {
+ 	0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0, 0
+ };
+ 
+ 
+ /* Initialize the 3c503 GA registers after a reset. */
+ static void
+ el2_init_card(struct ei *ei)
+ {
+   /* Unmap the station PROM and select the DIX or BNC connector. */
+   outb_p(ei->interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ 
+   /*
+    * Set ASIC copy of rx's first and last+1 buffer pages.
+    * These must be the same as in the 8390.
+    */
+   outb(ei->rx_start_page, E33G_STARTPG);
+   outb(ei->stop_page, E33G_STOPPG);
+ 
+   /* Point the vector pointer registers somewhere ?harmless?. */
+   outb(0xFF, E33G_VP2);	/* Point at the ROM restart location 0xffff0 */
+   outb(0xFF, E33G_VP1);
+   outb(0x00, E33G_VP0);
+ 
+   /* Turn off all interrupts until we're opened. */
+   outb_p(0x00,  ei->iobase + EN0_IMR);
+ 
+   /* Enable IRQs iff started. */
+   outb(EGACFR_NORM, E33G_GACFR);
+ 
+   /* Set the interrupt line. */
+   outb_p((0x04 << (ei->irq == 9 ? 2 : ei->irq)), E33G_IDCFR);
+   outb_p(8, E33G_DRQCNT);		/* Set burst size to 8 */
+   outb_p(0x20, E33G_DMAAH);	/* Put a valid addr in the GA DMA */
+   outb_p(0x00, E33G_DMAAL);
+ }
+ 
+ 
+ /*
+  * This is called whenever we have a unrecoverable failure:
+  *	transmit timeout
+  *	Bad ring buffer packet header
+  */
+ static void
+ el2_reset(struct ei *ei)
+ {
+   DPRINTF((DBG_EL2, "%s: resetting the 3C503 board...", ei->dev->if_name));
+   DPRINTF((DBG_EL2, "0x%X=0x%02X 0x%X=0x%02X 0x%X=0x%02X...",
+ 	E33G_IDCFR, inb(E33G_IDCFR), E33G_CNTRL, inb(E33G_CNTRL),
+ 	E33G_GACFR, inb(E33G_GACFR)));
+   outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
+   ei->txing = 0;
+   outb_p(ei->interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+   el2_init_card(ei);
+   DPRINTF((DBG_EL2, "done\n"));
+ }
+ 
+ 
+ static int
+ el2_open(struct ei *ei)
+ {
+   int irqlist[] = { 5, 9, 3, 4, 0 };
+   int *irqp = irqlist;
+ 
+   /* Easy case: programmed IRQ. */
+   if (ei->irq >= 2) {
+ 	if (irqaction(ei->irq, ei->sigaction)) return(-EAGAIN);
+ 	el2_init_card(ei);
+ 	(void) ei->chip_open(ei);
+ 	return(0);
+   }
+ 
+   /* We have to check out which IRQ to use first. */
+   outb(EGACFR_NORM, E33G_GACFR);	/* enable RAM and interrupts. */
+   do {
+ 	if (request_irq(*irqp, NULL) != -EBUSY) {
+ 		/* Twinkle the interrupt, and check if it's seen. */
+ 		autoirq_setup(0);
+ 		outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
+ 		outb_p(0x00, E33G_IDCFR);
+ 		if (*irqp == autoirq_report(0) && /* it's a good IRQ line! */
+ 			irqaction(ei->irq = *irqp, ei->sigaction) == 0) break;
+ 	}
+   } while(*++irqp);
+ 
+   if (*irqp == 0) {
+ 	outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts */
+ 	return(-EAGAIN);
+   }
+ 
+   /* All went well. Enable the hardware. */
+   el2_init_card(ei);
+   (void) ei->chip_open(ei);
+   return(0);
+ }
+ 
+ 
+ static int
+ el2_close(struct ei *ei)
+ {
+   free_irq(ei->irq);
+   NS8390_irq2ei_map[ei->irq] = NULL;
+   outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts */
+ 
+   ei->chip_init(ei, 0);
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Either use the shared memory (if enabled on the board) or put
+  * the packet out through the ASIC FIFO.  The latter is probably
+  * much slower.
+  */
+ static void
+ el2_output(struct ei *ei, int count, char *buf, int start_page)
+ {
+   int i;			/* buffer index */
+   int boguscount = 0;		/* timeout counter */
+ 
+   /* This should really be set with during an open(). */
+   outb(EGACFR_NORM, E33G_GACFR);	/* enable RAM and interrupts */
+ 
+   if (ei->mem_start) {	/* shared memory transfer */
+ 	void *dest_addr = (void *)(ei->mem_start +
+ 	    ((start_page - ei->tx_start_page) << 8));
+ 
+ 	memcpy(dest_addr, buf, count);
+ #ifdef BECKER_DEBUG
+ 	if (memcmp(dest_addr, buf, count))
+ 		DPRINTF((DBG_EL2,
+ 			"%s: 3C503 output() bad memory copy at 0x%05lX\n",
+ 				ei->dev->if_name, (int) dest_addr));
+ #endif
+ 	return;
+   }
+ 
+   /*
+    * No shared memory, put the packet out the slow way.
+    * Set up then start the internal memory transfer to
+    * Tx Start Page
+    */
+   outb(0x00, E33G_DMAAL);
+   outb_p(start_page, E33G_DMAAH);
+   outb_p((ei->interface_num
+ 	? ECNTRL_AUI
+ 	: ECNTRL_THIN ) | ECNTRL_OUTPUT | ECNTRL_START, E33G_CNTRL);
+ 
+   /*
+    * This is the byte copy loop: it should probably be tuned for
+    * for speed once everything is working.  I think it is possible
+    * to output 8 bytes between each check of the status bit.
+    */
+   for(i = 0; i < count; i++) {
+ 	if (i % 8 == 0) {
+ 		while((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) {
+ 			if (++boguscount > (i<<3) + 32) {
+ 				printk("%s: FIFO blocked in el2_output (at %d of %d, bc=%d).\n",
+ 				ei->dev->if_name, i, count, boguscount);
+ 				return;
+ 			}
+ 		}
+ 	}
+ 	outb(buf[i], E33G_FIFOH);
+   }
+   outb_p(ei->interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ }
+ 
+ 
+ /* Returns the new ring pointer. */
+ static int
+ el2_input(struct ei *ei, int count, char *buf, int ring_offset)
+ {
+   int boguscount = 0;
+   int end_of_ring = ei->rmem_end;
+   unsigned int i;
+ 
+   /* Maybe enable shared memory just be to be safe... nahh.*/
+   if (ei->mem_start) {	/* use the shared memory */
+ 	ring_offset -= (EL2SM_START_PG << 8);
+ 	if (ei->mem_start + ring_offset + count > end_of_ring) {
+ 		/* We must wrap the input move. */
+ 		int semi_count = end_of_ring - (ei->mem_start + ring_offset);
+ 		memcpy(buf, (char *)ei->mem_start + ring_offset, semi_count);
+ 		count -= semi_count;
+ 		memcpy(buf + semi_count, (char *)ei->rmem_start, count);
+ 		return(ei->rmem_start + count);
+ 	}
+ 	memcpy(buf, (char *)ei->mem_start + ring_offset, count);
+ 	return(ring_offset + count);
+   }
+ 
+   /* No shared memory, use programmed I/O. */
+   outb(ring_offset & 0xFF, E33G_DMAAL);
+   outb_p((ring_offset >> 8) & 0xFF, E33G_DMAAH);
+   outb_p((ei->interface_num == 0
+ 	? ECNTRL_THIN
+ 	: ECNTRL_AUI) | ECNTRL_INPUT | ECNTRL_START, E33G_CNTRL);
+ 
+   /*
+    * This is the byte copy loop: it should probably be
+    * tuned for for speed once everything is working.
+    */
+   for(i = 0; i < count; i++) {
+ 	if (i % 8 == 0) {
+ 		while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) {
+ 			if (++boguscount > (i<<3) + 32) {
+ 				printk("%s: FIFO blocked in el2_input() (at %d of %d, bc=%d).\n",
+ 					ei->dev->if_name, i, count, boguscount);
+ 				boguscount = 0;
+ 				break;
+ 			}
+ 		}
+ 	}
+ 	buf[i] = inb_p(E33G_FIFOH);
+   }
+   outb_p(ei->interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+   return(0);
+ }
+ 
+ 
+ /*
+  * Probe for the Etherlink II card at I/O port base IOADDR,
+  * returning non-zero on sucess.  If found, set the station
+  * address and memory parameters in EI.
+  */
+ static int
+ el2probe1(int ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int i, iobase_reg, membase_reg, saved_406;
+ 
+   /*
+    * We verify that it's a 3C503 board by checking the first
+    * three octets of its ethernet address.
+    */
+   DPRINTF((DBG_EL2, "3C503 probe at 0x%03X:", ioaddr));
+ 
+   iobase_reg = inb(ioaddr+0x403);
+   membase_reg = inb(ioaddr+0x404);
+ 
+   /* Verify ASIC register that should be 0 or have a single bit set. */
+   if ((iobase_reg & (iobase_reg - 1)) || (membase_reg & (membase_reg - 1))) {
+ 	DPRINTF((DBG_EL2, " not found.\n"));
+ 	return(-ENODEV);
+   }
+   saved_406 = inb_p(ioaddr + 0x406);
+   outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* reset it... */
+   outb_p(ECNTRL_THIN, ioaddr + 0x406);
+ 
+   /* Map the station addr PROM into the lower I/O ports. */
+   outb(ECNTRL_SAPROM | ECNTRL_THIN, ioaddr + 0x406);
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(ioaddr + i);
+ 	DPRINTF((DBG_EL2, " %02.2X", sa.sa_data[i]));
+   }
+   if ((unsigned char)sa.sa_data[0] != (unsigned char)0x02 ||
+       (unsigned char)sa.sa_data[1] != (unsigned char)0x60 ||
+       (unsigned char)sa.sa_data[2] != (unsigned char)0x8c) {
+ 	DPRINTF((DBG_EL2, " 3C503 not found.\n"));
+ 
+ 	/* Restore the register we frobbed. */
+ 	outb(saved_406, ioaddr + 0x406);
+ 	return(-ENODEV);
+   }
+   DPRINTF((DBG_EL2, "\n"));
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+   snarf_region(ioaddr, 16);
+ 
+   /* Map the 8390 back into the window. */
+   outb(ECNTRL_THIN, ioaddr + 0x406);
+ 
+   /* Probe for, turn on and clear the board's shared memory. */
+   DPRINTF((DBG_EL2, "%s: memory jumpers 0x%02.2X ",
+ 			ei->dev->if_name, membase_reg));
+   outb(EGACFR_NORM, ioaddr + 0x405);	/* Enable RAM */
+ 
+   if ((membase_reg & 0xf0) == 0) {
+ 	ei->membase = 0;
+   } else {
+ 	ei->membase = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
+ 	    ((membase_reg & 0xA0) ? 0x4000 : 0);
+ 
+ #if EL2_MEMTEST
+ 	/* This has never found an error, but someone might care. */
+ 	{			/* Check the card's memory. */
+ 	    int *mem_base = (int *)ei->membase;
+ 	    int memtest_value = 0xbbadf00d;
+ 	    mem_base[0] = 0xba5eba5e;
+ 	    for (i = 1; i < EL2_MEMSIZE / sizeof(mem_base[0]); i++) {
+ 		mem_base[i] = memtest_value;
+ 		if (mem_base[0] != 0xba5eba5e ||
+ 		    mem_base[i] != memtest_value) {
+ 		    printk("%s: memory failure or memory address conflict.\n",
+ 							ei->dev->if_name);
+ 		    ei->membase = 0;
+ 		    break;
+ 		}
+ 		memtest_value += 0x55555555;
+ 		mem_base[i] = 0;
+ 	    }
+ 	}
+ #endif
+ 
+ 	/*
+ 	 * Divide the on-board memory into a single maximum-sized
+ 	 * transmit (double-sized for ping-pong transmit) buffer at
+ 	 * the base, and use the rest as a receive ring.
+ 	 */
+ 	ei->mem_start = ei->membase;
+ 	ei->mem_end = ei->rmem_end = ei->mem_start + EL2_MEMSIZE;
+ 	ei->rmem_start = TX_PAGES*256 + ei->mem_start;
+   }
+ 
+   /* Finish setting the board's parameters. */
+   ei->tx_start_page = EL2SM_START_PG;
+   ei->rx_start_page = EL2SM_START_PG + TX_PAGES;
+   ei->stop_page = EL2SM_STOP_PG;
+ 
+   /* Set up the chip-specific stuff. */
+   (void) NS8390_setup(ei);
+ 
+   /* OKOK, not all people know this... */
+   if (ei->irq == 2) ei->irq = 9;
+ 
+   ei->start = 0;
+ #ifdef BECKER_DEBUG
+   if (ei->mem_start) DPRINTF((DBG_EL2,
+ 		"%s: %s with shared memory at 0x%06lX-0x%06lX,\n",
+ 		ei->dev->if_name, ei->name, ei->mem_start, ei->mem_end-1));
+     else DPRINTF((DBG_EL2,
+ 		"%s: %s using programmed I/O (REJUMPER for SHARED MEMORY).\n",
+ 		ei->dev->if_name, ei->name));
+ #endif
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = el2_reset;
+   ei->card_open = el2_open;
+   ei->card_close = el2_close;
+   ei->card_input = el2_input;
+   ei->card_output = el2_output;
+ 
+   return(0);
+ }
+ 
+ 
+ #if EL2_NONSHARED_MEM
+ /*
+  * Try all of the locations that aren't obviously empty.  This touches
+  * a lot of locations, and is much riskier than the code above.
+  */
+ static int
+ el2_pio_autoprobe(struct ei *ei)
+ {
+   int i;
+   for (i = 0; i < 8; i++) {
+ 	if (check_region(ports[i], 16)) continue;
+ 
+ 	/* Reset and/or avoid any lurking NE2000 */
+ 	if (inb_p(ports[i] + 0x408) == 0xff) continue;
+ 	if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
+ 	    && el2probe1(ports[i], ei) == 0) return(0);
+   }
+   return(-ENODEV);
+ }
+ #endif
+ 
+ 
+ /*
+  * This routine probes for a memory-mapped 3c503 board by looking for
+  * the "location register" at the end of the jumpered boot PROM space.
+  * This works even if a PROM isn't there.
+  *
+  * If the ethercard isn't found there is an optional probe for
+  * ethercard jumpered to programmed-I/O mode.
+  */
+ int
+ el2_probe(struct ei *ei)
+ {
+   static int el2_card_nr = 0;
+   int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0 };
+ 
+   /* Set up the interface name. */
+   ei->name = "3C503";
+   sprintf(ei->dev->if_name, "ec%d", el2_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(el2probe1(ei->ioaddr, ei));
+   }
+ 
+   for (addr = addrs; *addr; addr++) {
+ 	int i;
+ 	unsigned int base_bits = *(unsigned char *)*addr;
+ 
+ 	/* Find first set bit. */
+ 	for(i = 7; i >= 0; i--, base_bits >>= 1)
+ 	    if (base_bits & 0x1) break;
+ 	if (base_bits != 1) continue;
+ 	if (check_region(ports[i], 16)) continue;
+ 
+ 	if (el2probe1(ports[i], ei) == 0) return(0);
+   }
+ 
+ #if EL2_NONSHARED_MEM
+   return(el2_pio_autoprobe(ei));
+ #else
+   return(-ENODEV);
+ #endif
+ }
*** original/net/drv/becker/3c507.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/3c507.c	Mon May  9 13:12:59 1994
***************
*** 0 ****
--- 1,830 ----
+ /* 3c507.c: An EtherLink16 device driver for Linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 	Copyright 1993 United States Government as represented by the Director,
+ 	National Security Agency.  This software may only be used and distributed
+ 	according to the terms of the GNU Public License as modified by SRC,
+ 	incorported herein by reference.
+ 
+ 	The author may be reached as becker@super.org or
+ 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+ 	Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
+ 	and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
+ 	Mark Salazar <leslie@access.digex.net> made the changes for cards with
+ 	only 16K packet buffers.
+ 
+ 	Things remaining to do:
+ 	Verify that the tx and rx buffers don't have fencepost errors.
+ 	Move the theory of operation and memory map documentation.
+ 	The statistics need to be updated correctly.
+ */
+ 
+ /*
+   Sources:
+ 	This driver wouldn't have been written with the availability of the
+ 	Crynwr driver source code.	It provided a known-working implementation
+ 	that filled in the gaping holes of the Intel documention.  Three cheers
+ 	for Russ Nelson.
+ 
+ 	Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ 	info that the casual reader might think that it documents the i82586.
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/malloc.h>
+ #include <linux/string.h>
+ #include <linux/interrupt.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ #if 0
+ static char *version =
+ 	"3c507.c:v0.99-15f 2/17/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ /*
+   			Details of the i82586.
+ 
+    You'll really need the databook to understand the details of this part,
+    but the outline is that the i82586 has two seperate processing units.
+    Both are started from a list of three configuration tables, of which only
+    the last, the System Control Block (SCB), is used after reset-time.  The SCB
+    has the following fileds:
+ 		Status word
+ 		Command word
+ 		Tx/Command block addr.
+ 		Rx block addr.
+    The command word accepts the following controls for the Tx and Rx units:
+   */
+ 
+ #define	 CUC_START	 0x0100
+ #define	 CUC_RESUME	 0x0200
+ #define	 CUC_SUSPEND 0x0300
+ #define	 RX_START	 0x0010
+ #define	 RX_RESUME	 0x0020
+ #define	 RX_SUSPEND	 0x0030
+ 
+ /* The Rx unit uses a list of frame descriptors and a list of data buffer
+    descriptors.  We use full-sized (1518 byte) data buffers, so there is
+    a one-to-one pairing of frame descriptors to buffer descriptors.
+ 
+    The Tx ("command") unit executes a list of commands that look like:
+ 		Status word		Written by the 82586 when the command is done.
+ 		Command word	Command in lower 3 bits, post-command action in upper 3
+ 		Link word		The address of the next command.
+ 		Parameters		(as needed).
+ 
+ 	Some definitions related to the Command Word are:
+  */
+ #define CMD_EOL		0x8000			/* The last command of the list, stop. */
+ #define CMD_SUSP	0x4000			/* Suspend after doing cmd. */
+ #define CMD_INTR	0x2000			/* Interrupt after doing cmd. */
+ 
+ enum commands {
+   CmdNOp = 0,
+   CmdSASetup = 1,
+   CmdConfigure = 2,
+   CmdMulticastList = 3,
+   CmdTx = 4,
+   CmdTDR = 5,
+   CmdDump = 6,
+   CmdDiagnose = 7
+ };
+ 
+ 
+ /*
+   		Details of the EtherLink16 Implementation
+   The 3c507 is a generic shared-memory i82586 implementation.
+   The host can map 16K, 32K, 48K, or 64K of the 64K memory into
+   0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
+   */
+ 
+ /* Offsets from the base I/O address. */
+ #define	SA_DATA		0	/* Station address data, or 3Com signature. */
+ #define MISC_CTRL	6	/* Switch the SA_DATA banks, and bus config bits. */
+ #define RESET_IRQ	10	/* Reset the latched IRQ line. */
+ #define SIGNAL_CA	11	/* Frob the 82586 Channel Attention line. */
+ #define ROM_CONFIG	13
+ #define MEM_CONFIG	14
+ #define IRQ_CONFIG	15
+ 
+ /* The ID port is used at boot-time to locate the ethercard. */
+ #define ID_PORT		0x100
+ 
+ /* Offsets to registers in the mailbox (SCB). */
+ #define iSCB_STATUS	0x8
+ #define iSCB_CMD		0xA
+ #define iSCB_CBL		0xC	/* Command BLock offset. */
+ #define iSCB_RFA		0xE	/* Rx Frame Area offset. */
+ 
+ /*
+  * Since the 3c507 maps the shared memory window so that the last byte is
+  * at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
+  * 48K cooresponding to window sizes of 64K, 48K, 32K and 16K respectively. 
+  * We can account for this be setting the 'SBC Base' entry in the ISCP table
+  * below for all the 16 bit offset addresses, and also adding the 'SCB Base'
+  * value to all 24 bit physical addresses (in the SCP table and the TX and RX
+  * Buffer Descriptors).
+  *			-Mark	
+  */
+ #define SCB_BASE	((unsigned)64*1024 - (ei->mem_end - ei->mem_start))
+  
+ /*
+   What follows in 'init_words[]' is the "program" that is downloaded to the
+   82586 memory.	 It's mostly tables and command blocks, and starts at the
+   reset address 0xfffff6.  This is designed to be similar to the EtherExpress,
+   thus the unusual location of the SCB at 0x0008.
+ 
+   Even with the additional "don't care" values, doing it this way takes less
+   program space than initializing the individual tables, and I feel it's much
+   cleaner.
+ 
+   The databook is particularly useless for the first two structures, I had
+   to use the Crynwr driver as an example.
+ 
+    The memory setup is as follows:
+    */
+ 
+ #define CONFIG_CMD	0x0018
+ #define SET_SA_CMD	0x0024
+ #define SA_OFFSET	0x002A
+ #define IDLELOOP	0x30
+ #define TDR_CMD		0x38
+ #define TDR_TIME	0x3C
+ #define DUMP_CMD	0x40
+ #define DIAG_CMD	0x48
+ #define SET_MC_CMD	0x4E
+ #define DUMP_DATA	0x56	/* A 170 byte buffer for dump and Set-MC into. */
+ 
+ #define TX_BUF_START	0x0100
+ #define NUM_TX_BUFS 	4
+ #define TX_BUF_SIZE 	(1518+14+20+16) /* packet+header+TBD */
+ 
+ #define RX_BUF_START	0x2000
+ #define RX_BUF_SIZE 	(1518+14+18)	/* packet+header+RBD */
+ #define RX_BUF_END	(ei->mem_end - ei->mem_start)
+ 
+ /*
+   That's it: only 86 bytes to set up the beast, including every extra
+   command available.  The 170 byte buffer at DUMP_DATA is shared between the
+   Dump command (called only by the diagnostic program) and the SetMulticastList
+   command. 
+ 
+   To complete the memory setup you only have to write the station address at
+   SA_OFFSET and create the Tx & Rx buffer lists.
+ 
+   The Tx command chain and buffer list is setup as follows:
+   A Tx command table, with the data buffer pointing to...
+   A Tx data buffer descriptor.  The packet is in a single buffer, rather than
+ 	chaining together several smaller buffers.
+   A NoOp command, which initially points to itself,
+   And the packet data.
+ 
+   A transmit is done by filling in the Tx command table and data buffer,
+   re-writing the NoOp command, and finally changing the offset of the last
+   command to point to the current Tx command.  When the Tx command is finished,
+   it jumps to the NoOp, when it loops until the next Tx command changes the
+   "link offset" in the NoOp.  This way the 82586 never has to go through the
+   slow restart sequence.
+ 
+   The Rx buffer list is set up in the obvious ring structure.  We have enough
+   memory (and low enough interrupt latency) that we can avoid the complicated
+   Rx buffer linked lists by alway associating a full-size Rx data buffer with
+   each Rx data frame.
+ 
+   I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+   use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+ 
+   */
+ 
+ struct ei *el16_irq2ei_map[16] = { NULL };
+ unsigned short init_words[] = {
+   /* System Configuration Pointer (SCP). */
+   0x0000,			/* Set bus size to 16 bits. */
+   0,0,				/* pad words. */
+   0x0000,0x0000,		/* ISCP phys addr, set in init_82586_mem(). */
+ 
+   /* Intermediate System Configuration Pointer (ISCP). */
+   0x0001,			/* Status word that's cleared when init is done. */
+   0x0008,0,0,			/* SCB offset, (skip, skip) */
+ 
+   /* System Control Block (SCB). */
+   0,0xf000|RX_START|CUC_START,	/* SCB status and cmd. */
+   CONFIG_CMD,			/* Command list pointer, points to Configure. */
+   RX_BUF_START,			/* Rx block list. */
+   0,0,0,0,			/* Error count: CRC, align, buffer, overrun. */
+ 
+   /* 0x0018: Configure command.  Change to put MAC data with packet. */
+   0, CmdConfigure,		/* Status, command.		*/
+   SET_SA_CMD,			/* Next command is Set Station Addr. */
+   0x0804,			/* "4" bytes of config data, 8 byte FIFO. */
+   0x2e40,			/* Magic values, including MAC data location. */
+   0,				/* Unused pad word. */
+ 
+   /* 0x0024: Setup station address command. */
+   0, CmdSASetup,
+   SET_MC_CMD,			/* Next command. */
+   0xaa00,0xb000,0x0bad,	/* Station address (to be filled in) */
+ 
+   /* 0x0030: NOP, looping back to itself.  Point to first Tx buffer to Tx. */
+   0, CmdNOp, IDLELOOP, 0 /* pad */,
+ 
+   /* 0x0038: A unused Time-Domain Reflectometer command. */
+   0, CmdTDR, IDLELOOP, 0,
+ 
+   /* 0x0040: An unused Dump State command. */
+   0, CmdDump, IDLELOOP, DUMP_DATA,
+ 
+   /* 0x0048: An unused Diagnose command. */
+   0, CmdDiagnose, IDLELOOP,
+ 
+   /* 0x004E: An empty set-multicast-list command. */
+   0, CmdMulticastList, IDLELOOP, 0,
+ };
+ 
+ 
+ /* Initialize the 82586 Rx-block list. */
+ static void
+ i82586_bufinit(struct ei *ei)
+ {
+   unsigned short *write_ptr;
+   unsigned short SCB_base = SCB_BASE;
+ 
+   int cur_rxbuf = ei->rx_head = RX_BUF_START;
+ 
+   /* Initialize each Rx frame + data buffer. */
+   do {	/* while there is room for one more */
+ 
+ 	write_ptr = (unsigned short *) (ei->mem_start + cur_rxbuf);
+ 
+ 	*write_ptr++ = 0x0000; 			/* status */
+ 	*write_ptr++ = 0x0000;			/* command */
+ 	*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* link */
+ 	*write_ptr++ = cur_rxbuf + 22;		/* buffer offset */
+ 	*write_ptr++ = 0x0000; 			/* pad for dest addr */
+ 	*write_ptr++ = 0x0000;
+ 	*write_ptr++ = 0x0000;
+ 	*write_ptr++ = 0x0000; 			/* pad for source addr */
+ 	*write_ptr++ = 0x0000;
+ 	*write_ptr++ = 0x0000;
+ 	*write_ptr++ = 0x0000;			/* pad for protocol */
+ 		
+ 	*write_ptr++ = 0x0000;			/* buffer: Actual count */
+ 	*write_ptr++ = -1;			/* buffer: Next (none) */
+ 	*write_ptr++ = cur_rxbuf + 0x20 + SCB_base;	/* buffer: Address low */
+ 	*write_ptr++ = 0x0000;
+ 
+ 	/* Finally, the number of bytes in the buffer. */
+ 	*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+ 		
+ 	ei->rx_tail = cur_rxbuf;
+ 	cur_rxbuf += RX_BUF_SIZE;
+   } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+ 	
+   /*
+    * Terminate the list by setting the EOL bit, and wrap the
+    * pointer to make the list a ring.
+    */
+   write_ptr = (unsigned short *) (ei->mem_start + ei->rx_tail + 2);
+   *write_ptr++ = 0xC000;			/* command, mark as last */
+   *write_ptr++ = ei->rx_head;			/* link */
+ }
+ 
+ 
+ void
+ i82586_init(struct ei *ei)
+ {
+   short ioaddr = ei->ioaddr;
+   ushort *shmem = (short *) ei->mem_start;
+   unsigned char *ptr;
+ 
+   /*
+    * Enable loopback to protect the wire while starting up,
+    * and hold the 586 in reset during the memory initialization.
+    */
+   outb(0x20, ioaddr + MISC_CTRL);
+ 
+   /* Fix the ISCP address and base. */
+   init_words[3] = SCB_BASE;
+   init_words[7] = SCB_BASE;
+ 
+   /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
+   memcpy((void*) ei->mem_end-10, init_words, 10);
+ 
+   /* Write the words at 0x0000. */
+   memcpy((char*) ei->mem_start, init_words + 5, sizeof(init_words) - 10);
+ 
+   /* Fill in the station address. */
+   ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+   memcpy((char*) ei->mem_start+SA_OFFSET, ptr, ETH_ALEN);
+ 
+   /* The Tx-block list is written as needed.  We just set up the values. */
+   ei->tx_cmd_link = IDLELOOP + 4;
+   ei->tx_head = ei->tx_reap = TX_BUF_START;
+ 
+   i82586_bufinit(ei);
+ 
+   /* Start the 586 by releasing the reset line, but leave loopback. */
+   outb(0xA0, ioaddr + MISC_CTRL);
+ 
+   /*
+    * This was time consuming to track down: you need to give two
+    * channel attention signals to reliably start up the i82586.
+    */
+   outb(0, ioaddr + SIGNAL_CA);
+ 
+   {
+ 	int boguscnt = 50;
+ 	while (shmem[iSCB_STATUS>>1] == 0)
+ 		if (--boguscnt == 0) {
+ 			printk("%s: i82586 initialization timed out with status 0x%04X,"
+ 				   "cmd 0x%04X\n", ei->dev->if_name,
+ 				   shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+ 			break;
+ 		}
+ 	/* Issue channel-attn -- the 82586 won't start. */
+ 	outb(0, ioaddr + SIGNAL_CA);
+   }
+ 
+   /* Disable loopback and enable interrupts. */
+   outb(0x84, ioaddr + MISC_CTRL);
+   DPRINTF((DBG_EL16, "%s: Initialized 82586, status 0x%04X\n",
+ 			ei->dev->if_name, shmem[iSCB_STATUS>>1]));
+ }
+ 
+ 
+ static void
+ i82586_xmit(struct ei *ei, void *buf, short length)
+ {
+   ushort tx_block = ei->tx_head;
+   ushort *write_ptr = (ushort *) (ei->mem_start + tx_block);
+ 
+   /* Set the write pointer to the Tx block, and put out the header. */
+   *write_ptr++ = 0x0000;			/* TX status */
+   *write_ptr++ = CMD_INTR|CmdTx;		/* TX command */
+   *write_ptr++ = tx_block+16;			/* next command is a NoOp */
+   *write_ptr++ = tx_block+8;			/* data Buffer offset */
+ 
+   /* Output the data buffer descriptor. */
+   *write_ptr++ = length | 0x8000;		/* byte count parameter */
+   *write_ptr++ = -1;				/* no next data buffer */
+   *write_ptr++ = tx_block+22+SCB_BASE;  /* buffer follows the NoOp command */
+   *write_ptr++ = 0x0000;	/* buffer address high bits (always zero) */
+ 
+   /* Output the Loop-back NoOp command. */
+   *write_ptr++ = 0x0000;			/* TX status */
+   *write_ptr++ = CmdNOp;			/* TX command */
+   *write_ptr++ = tx_block+16;			/* next is myself */
+ 
+   /* Output the packet at the write pointer. */
+   memcpy(write_ptr, buf, length);
+ 
+   /* Set the old command link pointing to this send packet. */
+   *(ushort *) (ei->mem_start + ei->tx_cmd_link) = tx_block;
+   ei->tx_cmd_link = tx_block + 20;
+ 
+   /* Set the next free tx region. */
+   ei->tx_head = tx_block + TX_BUF_SIZE;
+   if (ei->tx_head > RX_BUF_START - TX_BUF_SIZE)
+   ei->tx_head = TX_BUF_START;
+ 
+   DPRINTF((DBG_EL16,
+ 	"%s: 3C507 at 0x%03X send length=%d, tx_block 0x%3X, next 0x%3X\n",
+ 	ei->dev->if_name, ei->ioaddr, length, tx_block, ei->tx_head));
+ 
+   if (ei->tx_head != ei->tx_reap) ei->dev->if_tbusy = 0;
+ }
+ 
+ 
+ static void
+ el16_receive(struct ei *ei)
+ {
+   short *shmem = (short *) ei->mem_start;
+   ushort rx_head = ei->rx_head;
+   ushort rx_tail = ei->rx_tail;
+   ushort boguscount = 10;
+   short frame_status;
+ 
+   while ((frame_status = shmem[rx_head>>1]) < 0) {   /* command complete */
+ 	ushort *read_frame = (short *) (ei->mem_start + rx_head);
+ 	ushort rfd_cmd = read_frame[1];
+ 	ushort next_rx_frame = read_frame[2];
+ 	ushort data_buffer_addr = read_frame[3];
+ 	ushort *data_frame = (short *) (ei->mem_start + data_buffer_addr);
+ 	ushort pkt_len = data_frame[0];
+ 
+ 	if (rfd_cmd != 0 || (data_buffer_addr != rx_head + 22) ||
+ 	    pkt_len & 0xC000 != 0xC000) {
+ 		printk("%s: RX frame at 0x%X corrupted, status 0x%04X cmd 0x%04X"
+ 			"next 0x%04X data-buf at 0x%04X 0x%04X\n",
+ 			ei->dev->if_name, rx_head, frame_status, rfd_cmd,
+ 			next_rx_frame, data_buffer_addr, pkt_len);
+ 	} else if ((frame_status & 0x2000) == 0) {
+ 		/* Frame received, but with error. */
+ 		ei->dev->if_stats->rx_errors++;
+ 		if (frame_status & 0x0800) ei->dev->if_stats->rx_crc++;
+ 		if (frame_status & 0x0400) ei->dev->if_stats->rx_frame++;
+ 		if (frame_status & 0x0200) ei->dev->if_stats->rx_fifo++;
+ 		if (frame_status & 0x0100) ei->dev->if_stats->rx_overrun++;
+ 		if (frame_status & 0x0080) ei->dev->if_stats->rx_length++;
+ 	} else {
+ 		/* Malloc up new buffer. */
+ 		struct sk_buff *skb;
+ 
+ 		pkt_len &= 0x3fff;
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ 			printk("%s: memory squeeze, dropping packet.\n",
+ 							ei->dev->if_name);
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		skb->dev = ei->dev;
+ 
+ 		/* 'skb->raw' points to the start of sk_buff data area. */
+ 		memcpy(skb->raw, data_frame + 5, pkt_len);
+ 		
+ 		if (dev_rint(ei->dev, skb, pkt_len) != 0) {
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		ei->dev->if_stats->rx_packets++;
+ 	}
+ 
+ 	/* Clear the status word and set End-of-List on the RX frame. */
+ 	read_frame[0] = 0;
+ 	read_frame[1] = 0xC000;
+ 
+ 	/* Clear the end-of-list on the prev. RFD. */
+ 	*(short *) (ei->mem_start + rx_tail + 2) = 0x0000;
+ 
+ 	rx_tail = rx_head;
+ 	rx_head = next_rx_frame;
+ 	if (--boguscount == 0) break;
+   }
+   ei->rx_head = rx_head;
+   ei->rx_tail = rx_tail;
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the network interface interrupts.
+  */
+ static void
+ el16_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = el16_irq2ei_map[irq];
+   int ioaddr, status, boguscount = 0;
+   ushort ack_cmd = 0;
+   ushort *shmem;
+ 	
+   if (ei == NULL) {
+ 	printk("3C507: interrupt: IRQ%d for unknown device.\n", irq);
+ 	return;
+   }
+   ei->interrupt = 1;
+ 	
+   ioaddr = ei->ioaddr;
+   shmem = ((ushort *) ei->mem_start);
+ 	
+   status = shmem[iSCB_STATUS>>1];
+ 
+   DPRINTF((DBG_EL16, "%s: 3C507 interrupt, status 0x%04.4X\n",
+ 					ei->dev->if_name, status));
+ 
+   /* Disable the 82586's input to the interrupt line. */
+   outb(0x80, ioaddr + MISC_CTRL);
+ 
+   /* Reap the Tx packet buffers. */
+   while (ei->tx_reap != ei->tx_head) {
+ 	unsigned short tx_status = shmem[ei->tx_reap>>1];
+ 
+ 	if (tx_status == 0) {
+ 		printk("Couldn't reap 0x%X\n", ei->tx_reap);
+ 		break;
+ 	}
+ 	if (tx_status & 0x2000) {
+ 		ei->dev->if_stats->tx_packets++;
+ 		ei->dev->if_stats->tx_collision += tx_status & 0xf;
+ 		ei->dev->if_tbusy = 0;
+ 		mark_bh(NET_BH);	/* Inform upper layers. */
+ 	} else {
+ 		ei->dev->if_stats->tx_errors++;
+ 		if (tx_status & 0x0600)
+ 			ei->dev->if_stats->tx_carrier++;
+ 		if (tx_status & 0x0100)
+ 			ei->dev->if_stats->tx_fifo++;
+ 		if (!(tx_status & 0x0040))
+ 			ei->dev->if_stats->tx_heartbeat++;
+ 		if (tx_status & 0x0020)
+ 			ei->dev->if_stats->tx_aborted++;
+ 	}
+ 	DPRINTF((DBG_EL16, "Reaped 0x%X, TX status 0x%04X\n",
+ 					ei->tx_reap, tx_status));
+ 	ei->tx_reap += TX_BUF_SIZE;
+ 	if (ei->tx_reap > RX_BUF_START - TX_BUF_SIZE)
+ 				ei->tx_reap = TX_BUF_START;
+ 	if (++boguscount > 4) break;
+   }
+ 
+   if (status & 0x4000) { /* packet received */
+ 	DPRINTF((DBG_EL16, "Received packet, rx_head 0x%04X\n", ei->rx_head));
+ 	el16_receive(ei);
+   }
+ 
+   /* Acknowledge the interrupt sources. */
+   ack_cmd = status & 0xF000;
+ 
+   if ((status & 0x0700) != 0x0200 && ei->start) {
+ 	DPRINTF((DBG_EL16,
+ 		"%s: command unit stopped, status 0x%04X, restarting\n",
+ 						ei->dev->if_name, status));
+ 
+ 	/*
+ 	 * If this ever occurs we should really re-write the
+ 	 * idle loop, reset the Tx list, and do a complete
+ 	 * restart of the command unit.
+ 	 * For now we rely on the Tx timeout if the resume
+ 	 * doesn't work.
+ 	 */
+ 	ack_cmd |= CUC_RESUME;
+   }
+ 
+   if ((status & 0x0070) != 0x0040 && ei->start) {
+ 	/*
+ 	 * The Rx unit is not ready, it must be hung. Restart
+ 	 * the receiver by initializing the rx buffers, and
+ 	 * issuing an Rx start command.
+ 	 */
+ 	DPRINTF((DBG_EL16,
+ 		"%s: RX unit stopped, status 0x%04X, restarting\n",
+ 						ei->dev->if_name, status));
+ 	i82586_bufinit(ei);
+ 	shmem[iSCB_RFA >> 1] = RX_BUF_START;
+ 	ack_cmd |= RX_START;
+   }
+ 
+   shmem[iSCB_CMD>>1] = ack_cmd;
+   outb(0, ioaddr + SIGNAL_CA);		/* issue channel-attn */
+ 
+   /* Clear the latched interrupt. */
+   outb(0, ioaddr + RESET_IRQ);
+ 
+   /* Enable the 82586's interrupt input. */
+   outb(0x84, ioaddr + MISC_CTRL);
+ }
+ 
+ 
+ static int
+ el16_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int ioaddr;
+   short *shmem;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in el16_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+   shmem = (short *) ei->mem_start;
+ 
+   if (dev->if_tbusy) {
+ 	/*
+ 	 * If we get here, some higher level has decided we
+ 	 * are broken.  There should really be a "kick me"
+ 	 * function call instead.
+ 	 */
+ 	int tickssofar = jiffies - dev->if_txtimer;
+ 	if (tickssofar < 5) return(1);
+ 	DPRINTF((DBG_EL16, "%s: transmit timed out, %s?  ",
+ 		dev->if_name,
+ 		shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict"
+ 					: "network cable problem"));
+ 
+ 	/* Try to restart the adaptor. */
+ 	if (ei->last_restart == dev->if_stats->tx_packets) {
+ 		DPRINTF((DBG_EL16, "resetting board.\n"));
+ 
+ 		/* Completely reset the adaptor. */
+ 		i82586_init(ei);
+ 	} else {
+ 		/* Issue the channel attention signal and hope it "gets better". */
+ 		DPRINTF((DBG_EL16, "kicking board\n"));
+ 		shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+ 		outb(0, ioaddr + SIGNAL_CA);	/* Issue channel-attn. */
+ 		ei->last_restart = ei->dev->if_stats->tx_packets;
+ 	}
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+   }
+ 
+   /*
+    * If some higher layer thinks we've missed an tx-done
+    * interrupt we are passed NULL.
+    * Caution: dev_tint() handles the cli()/sti() itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   /* Block a timer-based transmit from overlapping. */
+   if (set_bit(0, (void *) &dev->if_tbusy) != 0)
+ 	printk("%s: transmitter access conflict\n", dev->if_name);
+     else {
+ 	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ 	unsigned char *buf = (void *)(skb+1);
+ 
+ 	/* Disable the 82586's input to the interrupt line. */
+ 	outb(0x80, ioaddr + MISC_CTRL);
+ 	i82586_xmit(ei, buf, length);
+ 	dev->if_txtimer = jiffies;
+ 
+ 	/* Enable the 82586 interrupt input. */
+ 	outb(0x84, ioaddr + MISC_CTRL);
+   }
+ 
+   /* You might need to clean up and record TX statistics here. */
+   return(0);
+ }
+ 
+ 
+ static int
+ el16_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+   ushort *shmem = (short *) ei->mem_start;
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   /* Flush the Tx and disable Rx. */
+   shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+   outb(0, ioaddr + SIGNAL_CA);
+ 
+   /* Disable the 82586's input to the interrupt line. */
+   outb(0x80, ioaddr + MISC_CTRL);
+ 
+   /*
+    * We always physically use the IRQ line, so we don't do
+    * free_irq().  We do remove ourselves from the map.
+    */
+   el16_irq2ei_map[ei->irq] = NULL;
+ 
+   /* Update the statistics here. */
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ el16_open(struct ei *ei)
+ {
+   el16_irq2ei_map[ei->irq] = ei;
+ 
+   /* Initialize the 82586 memory and start it. */
+   i82586_init(ei);
+ 
+   ei->dev->if_tbusy = 0;
+   ei->interrupt = 0;
+   ei->start = 1;
+   return(0);
+ }
+ 
+ 
+ static int
+ el16probe1(short ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int i, irq, irqval;
+   int base, size;
+   char mem_config;
+ 
+   DPRINTF((DBG_EL16, "%s: 3C507 probe at 0x%3X,", ei->dev->if_name, ioaddr));
+ 
+   /*
+    * We should make a few more checks here, like the first three
+    * octets of the S.A. for the manufactor's code.
+    */ 
+   irq = inb(ioaddr + IRQ_CONFIG) & 0x0F;
+   irqval = request_irq(irq, el16_interrupt);
+   if (irqval) {
+ 	printk("unable to get IRQ%d (irqval=%d).\n", irq, irqval);
+ 	return(-ENODEV);
+   }
+ 	
+   /* We've committed to using the board, and can start filling in 'ei'. */
+   snarf_region(ioaddr, 16);
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+ 
+   outb(0x01, ioaddr + MISC_CTRL);
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(ioaddr + i);
+ 	DPRINTF((DBG_EL16, " %2.2X", sa.sa_data[i]));
+   }
+ 
+   mem_config = inb(ioaddr + MEM_CONFIG);
+   if (mem_config & 0x20) {
+ 	size = 64*1024;
+ 	base = 0xf00000 + (mem_config & 0x08 ? 0x080000
+ 			   : ((mem_config & 3) << 17));
+   } else {
+ 	size = ((mem_config & 3) + 1) << 14;
+ 	base = 0x0c0000 + ((mem_config & 0x18) << 12);
+   }
+   ei->mem_start = base;
+   ei->mem_end = base + size;
+ 
+   ei->port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
+   ei->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+ 
+   DPRINTF((DBG_EL16, ", IRQ%d, %sternal xcvr, memory 0x%lX-0x%lX.\n",
+ 	ei->irq, ei->port ? "ex" : "in", ei->mem_start, ei->mem_end-1));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = el16_xmit;
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = NULL;
+   ei->card_open = el16_open;
+   ei->card_close = el16_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /* Check for a network adaptor of this type, and return '0' iff one exists. */
+ int
+ el16_probe(struct ei *ei)
+ {
+   /* Don't probe all settable addresses, 0x[23][0-F]0, just common ones. */
+   static int el16_card_nr = 0;
+   int *port, ports[] = { 0x300, 0x320, 0x340, 0x280, 0 };
+   ushort lrs_state = 0xff, i;
+   char res[5];
+ 
+   /* Set up the device name. */
+   ei->name = "3C507";
+   sprintf(ei->dev->if_name, "ec%d", el16_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(el16probe1(ei->ioaddr, ei));
+   }
+ 
+   /* Send the ID sequence to the ID_PORT to enable the board. */
+   outb(0x00, ID_PORT);
+   for(i = 0; i < 255; i++) {
+ 	outb(lrs_state, ID_PORT);
+ 	lrs_state <<= 1;
+ 	if (lrs_state & 0x100) lrs_state ^= 0xe7;
+   }
+   outb(0x00, ID_PORT);
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	short ioaddr = *port;
+ 
+ 	if (check_region(ioaddr, 16)) continue;
+ 
+ 	/*
+ 	 * This is code from jennings@Montrouge.SMR.slb.com,
+ 	 * done so that the string can be printed out.
+ 	 */
+ 	res[0] = inb(ioaddr);
+ 	res[1] = inb(ioaddr+1);
+ 	res[2] = inb(ioaddr+2);
+ 	res[3] = inb(ioaddr+3);
+ 	res[4] = 0;
+ 	if (res[0] == '*' && res[1] == '3' && res[2] == 'C' &&
+ 	    res[3] == 'O' && el16probe1(*port, ei) == 0) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/becker/3c509.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/3c509.c	Mon May  9 14:25:25 1994
***************
*** 0 ****
--- 1,693 ----
+ /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 
+ 	Copyright 1993 United States Government as represented by the
+ 	Director, National Security Agency.	 This software may be used and
+ 	distributed according to the terms of the GNU Public License,
+ 	incorporated herein by reference.
+ 	
+ 	This driver is for the 3Com EtherLinkIII series.
+ 
+ 	The author may be reached as becker@super.org or
+ 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/interrupt.h>
+ #include <linux/ptrace.h>
+ #include <linux/errno.h>
+ #include <linux/in.h>
+ #include <linux/malloc.h>
+ #include <linux/ioport.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"3c509.c:pl15k 3/5/94 becker@super.org\n";
+ #endif
+ 
+ 
+ /*
+  * To minimize the size of the driver source I only define operating
+  * constants if they are used several times.  You'll need the manual
+  * if you want to understand driver details.
+  */
+ 
+ /* Offsets from base I/O address. */
+ #define EL3_DATA	0x00
+ #define EL3_CMD		0x0e
+ #define EL3_STATUS	0x0e
+ #define ID_PORT		0x100
+ #define  EEPROM_READ	0x80
+ 
+ #define EL3WINDOW(win_num)	outw(0x0800+(win_num), ioaddr + EL3_CMD)
+ 
+ 
+ /* Register window 1 offsets, the window used in normal operation. */
+ #define TX_FIFO		0x00
+ #define RX_FIFO		0x00
+ #define RX_STATUS 	0x08
+ #define TX_STATUS 	0x0B
+ #define TX_FREE		0x0C	/* Remaining free bytes in Tx buffer. */
+ 
+ #define WN4_MEDIA	0x0A	/* Window 4: Various transceiver/media bits. */
+ #define  MEDIA_TP	0x00C0	/* Enable link beat and jabber for 10baseT. */
+ 
+ 
+ #ifdef BECKER_DEBUG
+ static char *el3_if_names[] = {
+   "10baseT",
+   "AUI",
+   "undefined",
+   "BNC"
+ };
+ #endif
+ static struct ei *el3_irq2ei_map[16] = { NULL };
+ 
+ 
+ /*
+  * Read a word from the EEPROM using the regular EEPROM access
+  * register.  Assume that we are in register window zero.
+  */
+ static ushort
+ el3_read_eeprom(short ioaddr, int index)
+ {
+   int timer;
+ 
+   outw(EEPROM_READ + index, ioaddr + 10);
+ 
+   /* Pause for at least 162 us. for the read to take place. */
+   for (timer = 0; timer < 162*4 + 400; timer++)
+ 	SLOW_DOWN_IO;
+   return(inw(ioaddr + 12));
+ }
+ 
+ 
+ /* Read a word from the EEPROM when in the ISA ID probe state. */
+ static ushort
+ el3_id_read_eeprom(int index)
+ {
+   int timer, bit, word = 0;
+ 	
+   /*
+    * Issue read command, and pause for at least 162 us.
+    * for it to complete.  Assume extra-fast 16Mhz bus.
+    */
+   outb(EEPROM_READ + index, ID_PORT);
+ 
+   /*
+    * This should really be done by looking at one of the
+    * timer channels.
+    */
+   for (timer = 0; timer < 162*4 + 400; timer++)
+ 	SLOW_DOWN_IO;
+ 
+   for (bit = 15; bit >= 0; bit--)
+ 	word = (word << 1) + (inb(ID_PORT) & 0x01);
+ 
+   DPRINTF((DBG_EL3, "3C509 EEPROM word %d 0x%04.4X\n", index, word));
+   return(word);
+ }
+ 
+ 
+ /*
+  * Update statistics. We change to register window 6, so this should
+  * be run single-threaded if the device is active. This is expected
+  * to be a rare operation, and it's simpler for the rest of the driver
+  * to assume that window 1 is always valid rather than use a special
+  * window-state variable.
+  */
+ static void
+ el3_update_stats(int ioaddr, struct ei *ei)
+ {
+   DPRINTF((DBG_EL3, "%s: updating statistics\n",
+ 				ei->dev->if_name));
+ 
+   /* Turn off statistics updates while reading. */
+   outw(0xB000, ioaddr + EL3_CMD);
+ 
+   /* Switch to the stats window, and read everything. */
+   EL3WINDOW(6);
+ 
+   ei->dev->if_stats->tx_carrier += inb(ioaddr + 0);
+   ei->dev->if_stats->tx_heartbeat += inb(ioaddr + 1);
+ 
+   /* Multiple collisions. */
+   (void) inb(ioaddr + 2);
+   ei->dev->if_stats->tx_collision += inb(ioaddr + 3);
+   ei->dev->if_stats->tx_window += inb(ioaddr + 4);
+   ei->dev->if_stats->rx_fifo += inb(ioaddr + 5);
+   ei->dev->if_stats->tx_packets += inb(ioaddr + 6);
+   ei->dev->if_stats->rx_packets += inb(ioaddr + 7);
+ 
+   /* Tx deferrals */
+   (void) inb(ioaddr + 8);
+   (void) inw(ioaddr + 10);	/* total Rx and Tx octets */
+   (void) inw(ioaddr + 12);
+ 
+   /* Back to window 1, and turn statistics back on. */
+   EL3WINDOW(1);
+   outw(0xA800, ioaddr + EL3_CMD);
+ }
+ 
+ 
+ static int
+ el3_receive(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+   short rx_status;
+ 
+   DPRINTF((DBG_EL3, "%s: el3_receive: status 0x%04.4X, rx_status 0x%04.4X\n",
+ 	ei->dev->if_name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)));
+ 
+   while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+ 	if (rx_status & 0x4000) {
+ 		/* Error, update stats. */
+ 		short error = rx_status & 0x3800;
+ 
+ 		ei->dev->if_stats->rx_errors++;
+ 		switch (error) {
+ 			case 0x0000:
+ 				ei->dev->if_stats->rx_overrun++;
+ 				break;
+ 
+ 			case 0x0800:
+ 				ei->dev->if_stats->rx_length++;
+ 				break;
+ 
+ 			case 0x1000:
+ 				ei->dev->if_stats->rx_frame++;
+ 				break;
+ 
+ 			case 0x1800:
+ 				ei->dev->if_stats->rx_length++;
+ 				break;
+ 
+ 			case 0x2000:
+ 				ei->dev->if_stats->rx_frame++;
+ 				break;
+ 
+ 			case 0x2800:
+ 				ei->dev->if_stats->rx_crc++;
+ 				break;
+ 		}
+ 	}
+ 
+ 	if ((! (rx_status & 0x4000)) || ! (rx_status & 0x1000)) {
+ 		/* Dribble bits are OK. */
+ 		short pkt_len = rx_status & 0x7ff;
+ 		struct sk_buff *skb;
+ 
+ 		DPRINTF((DBG_EL3,
+ 			"%s: receiving packet size %d status 0x%04.4X\n",
+ 				ei->dev->if_name, pkt_len, rx_status));
+ 
+ 		skb = skb_create(pkt_len + 3);
+ 		if (skb != NULL) {
+ 			skb->dev = ei->dev;
+ 
+ 			/* skb->raw points to the start of sk_buff data area. */
+ 			insl(ioaddr + RX_FIFO, (void *)(skb->raw),
+ 					     (pkt_len + 3) >> 2);
+ 
+ 			if (dev_rint(ei->dev, skb, pkt_len) == 0) {
+ 				DPRINTF((DBG_EL3,
+ 				  "dev_rint() happy, status 0x%04.4X\n",
+ 				  inb(ioaddr + EL3_STATUS)));
+ 
+ 				/* RX discard */
+ 				outw(0x4000, ioaddr + EL3_CMD);
+ 
+ 				while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ 					DPRINTF((DBG_EL3,
+ 					  "Waiting for 3C509 to discard packet, status 0x%X\n",
+ 						 inw(ioaddr + EL3_STATUS)));
+ 
+ 				DPRINTF((DBG_EL3,
+ 				  "discarded packet, status 0x%04.4X\n",
+ 				  inb(ioaddr + EL3_STATUS)));
+ 				continue;
+ 			} else {
+ 				printk("%s: receive buffers full\n",
+ 						ei->dev->if_name);
+ 			}
+ 		} else {
+ 			printk("%s: couldn't allocate a sk_buff of size %d\n",
+ 				   ei->dev->if_name, pkt_len);
+ 		}
+ 	}
+ 
+ 	ei->dev->if_stats->rx_dropped++;
+ 	outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
+ 	while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ 		DPRINTF((DBG_EL3,
+ 			"Waiting for 3C509 to discard packet, status 0x%X\n",
+ 			 inw(ioaddr + EL3_STATUS)));
+   }
+ 
+   DPRINTF((DBG_EL3,
+ 	"%s: exiting el3_receive, status 0x%04.4X, rx_status 0x%04.4X\n",
+ 		ei->dev->if_name, inw(ioaddr+EL3_STATUS), inw(ioaddr+8)));
+   return(0);
+ }
+ 
+ 
+ /* The EL3 interrupt handler. */
+ static void
+ el3_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = (el3_irq2ei_map[irq]);
+   int ioaddr, status;
+   int i = 0;
+ 
+   if (ei == NULL) {
+ 	printk("3C509: interrupt: IRQ%d for unknown device\n", irq);
+ 	return;
+   }
+ 
+   if (ei->interrupt)
+ 	printk("%s: re-entering the interrupt handler\n", ei->dev->if_name);
+   ei->interrupt = 1;
+ 
+   ioaddr = ei->ioaddr;
+   status = inw(ioaddr + EL3_STATUS);
+ 
+   DPRINTF((DBG_EL3, "%s: interrupt, status 0x%04.4X\n",
+ 				ei->dev->if_name, status));
+ 	
+   while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) {
+ 	if (status & 0x10) el3_receive(ei);
+ 
+ 	if (status & 0x08) {
+ 		DPRINTF((DBG_EL3, "TX room bit was handled\n"));
+ 
+ 		/* There's room in the FIFO for a full-sized packet. */
+ 		outw(0x6808, ioaddr + EL3_CMD); /* ack IRQ */
+ 		ei->dev->if_tbusy = 0;
+ 		mark_bh(NET_BH);
+ 	}
+ 
+ 	/* Statistics full. */
+ 	if (status & 0x80) el3_update_stats(ioaddr, ei);
+ 		
+ 	if (++i > 10) {
+ 		printk("%s: infinite loop in interrupt, status 0x%04.4X\n",
+ 			   ei->dev->if_name, status);
+ 		/* Clear all interrupts we have handled. */
+ 		outw(0x6899, ioaddr + EL3_CMD); /* ack IRQ */
+ 		break;
+ 	}
+ 
+ 	/* Acknowledge the IRQ. */
+ 	outw(0x6891, ioaddr + EL3_CMD);
+   }
+ 
+   DPRINTF((DBG_EL3, "%s: exiting interrupt, status 0x%04.4X\n",
+ 	ei->dev->if_name, inw(ioaddr + EL3_STATUS)));
+ 	
+   ei->interrupt = 0;
+ }
+ 
+ 
+ static int
+ el3_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int length, send_length;
+   int ioaddr;
+     
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in el3_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+ 
+   /* Transmitter timeout, serious problems. */
+   if (dev->if_tbusy) {
+ 	int tickssofar = jiffies - dev->if_txtimer;
+ 	if (tickssofar < 10) return(1);
+ 	printk("%s: transmit timed out, tx_status 0x%02.2X status 0x%04.4X\n",
+ 		dev->if_name, inb(ioaddr + TX_STATUS),
+ 				inw(ioaddr + EL3_STATUS));
+ 	dev->if_txtimer = jiffies;
+ 
+ 	/* Issue TX_RESET and TX_START commands. */
+ 	outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */
+ 	outw(0x4800, ioaddr + EL3_CMD); /* TX_START */
+ 	dev->if_tbusy = 0;
+   }
+ 
+   /*
+    * Some higher layer thinks we've missed an tx-done interrupt.
+    * Caution: dev_tint() handles the cli()/sti() itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   if (skb->len <= 0) return(0);
+   length = skb->len;
+   send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+   DPRINTF((DBG_EL3, "%s: el3_xmit(%d)... status 0x%4.4X",
+ 	dev->if_name, length, inw(ioaddr + EL3_STATUS)));
+ 
+ #ifndef final_version
+   {	/* Error-checking code, delete for 1.00. */
+ 	ushort status = inw(ioaddr + EL3_STATUS);
+ 	if (status & 0x0001 /* IRQ line active, missed one. */
+ 		&& inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
+ 		printk("%s: Missed interrupt, status then %04X now %04X"
+ 			   "  TX %2.2X RX %4.4X.\n", dev->if_name, status,
+ 			   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
+ 			   inw(ioaddr + RX_STATUS));
+ 		outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */
+ 		outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
+ 		outw(0x78ff, ioaddr + EL3_CMD); /* Set all status bits visible. */
+ 	}
+   }
+ #endif
+ 
+   /* Avoid timer-based retransmission conflicts. */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0) {
+ 	printk("%s: transmitter access conflict\n", dev->if_name);
+   } else {
+ 	/* Put out the doubleword header... */
+ 	outw(skb->len, ioaddr + TX_FIFO);
+ 	outw(0x00, ioaddr + TX_FIFO);
+ 
+ 	/* ... and the packet rounded to a doubleword. */
+ 	outsl(ioaddr + TX_FIFO, (void *)(skb+1), (skb->len + 3) >> 2);
+ 
+ 	dev->if_txtimer = jiffies;
+ 	if (inw(ioaddr + TX_FREE) > 1536) {
+ 		dev->if_tbusy = 0;
+ 	} else {
+ 		/* Interrupt us when the FIFO has room for max-sized packet. */
+ 		outw(0x9000 + 1536, ioaddr + EL3_CMD);
+ 	}
+   }
+ 
+   /* Clear the TX status stack. */
+   {
+ 	short tx_status;
+ 	int i = 4;
+ 
+ 	while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ 		DPRINTF((DBG_EL3, "TX status 0x%4.4X\n", tx_status));
+ 		if (tx_status & 0x38) dev->if_stats->tx_aborted++;
+ 		if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD);
+ 		if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD);
+ 		outb(0x00, ioaddr + TX_STATUS); /* pop the status stack */
+ 	}
+   }
+   return(0);
+ }
+ 
+ 
+ /* Open the board, and allocate the resources for it. */
+ static int
+ el3_open(struct ei *ei)
+ {
+   unsigned char *ptr;
+   int ioaddr = ei->ioaddr;
+   int i;
+ 
+   if (request_irq(ei->irq, el3_interrupt)) return(-EAGAIN);
+ 
+   EL3WINDOW(0);
+   DPRINTF((DBG_EL3, "%s: open: IRQ%d Interface %s, status@%X 0x%04.4X\n",
+ 	ei->dev->if_name, ei->irq, el3_if_names[ei->port],
+ 	ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)));
+ 
+   /* Activate board: this is probably unnecessary. */
+   outw(0x0001, ioaddr + 4);
+ 
+   el3_irq2ei_map[ei->irq] = ei;
+ 
+   /* Set the IRQ line. */
+   outw((ei->irq << 12) | 0x0F00, ioaddr + 8);
+ 
+   /* Set the station address in window 2 each time opened. */
+   EL3WINDOW(2);
+   ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+   for(i = 0; i < ETH_ALEN; i++) {
+ 	outb(ptr[i], ioaddr + i);
+   }
+ 
+   if (ei->port == 3)
+ 	/* Start the thinnet transceiver. We should really wait 50ms...*/
+ 	outw(0x1000, ioaddr + EL3_CMD);
+     else if (ei->port == 0) {
+ 	/* 10baseT interface, enabled link beat and jabber check. */
+ 	EL3WINDOW(4);
+ 	outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+   }
+ 
+   /* Switch to register set 1 for normal use. */
+   EL3WINDOW(1);
+ 
+   outw(0x8005, ioaddr + EL3_CMD); /* accept b-case and phys addr only */
+   outw(0xA800, ioaddr + EL3_CMD); /* turn on statistics */
+   outw(0x2000, ioaddr + EL3_CMD); /* enable the receiver */
+   outw(0x4800, ioaddr + EL3_CMD); /* enable transmitter */
+   outw(0x78ff, ioaddr + EL3_CMD); /* allow all status bits to be seen */
+ 
+   ei->interrupt = 0;
+   ei->dev->if_tbusy = 0;
+   ei->start = 1;
+   outw(0x7098, ioaddr + EL3_CMD); /* set interrupt mask */
+ 
+   DPRINTF((DBG_EL3, "%s: Opened 3C509 IRQ%d status 0x%04.4X\n",
+ 	ei->dev->if_name, ei->irq, inw(ioaddr + EL3_STATUS)));
+ 
+   /* Always succeed. */
+   return(0);
+ }
+ 
+ 
+ static int
+ el3_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   DPRINTF((DBG_EL3, "%s: shutting down\n", ei->dev->if_name));
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   /* Turn off statistics. We update the stats below. */
+   outw(0xB000, ioaddr + EL3_CMD);
+ 
+   /* Disable the receiver and transmitter. */
+   outw(0x1800, ioaddr + EL3_CMD);
+   outw(0x5000, ioaddr + EL3_CMD);
+ 
+   if (ei->port == 3)
+ 	/* Turn off thinnet power. */
+ 	outw(0xb800, ioaddr + EL3_CMD);
+     else if (ei->port == 0) {
+ 	/* Disable link beat and jabber, port may change at next open(). */
+ 	EL3WINDOW(4);
+ 	outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+   }
+ 
+   /* Release the IRQ line. */
+   free_irq(ei->irq);
+ 
+   /* Switching back to window 0 disables the IRQ. */
+   EL3WINDOW(0);
+ 
+   /* But we explicitly zero the IRQ line select anyway. */
+   outw(0x0F00, ioaddr + 8);
+ 
+   el3_irq2ei_map[ei->irq] = NULL;
+ 
+   el3_update_stats(ioaddr, ei);
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ el3_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   if (num_addrs > 0) {
+ 	outw(0x8007, ioaddr + EL3_CMD);
+   } else if (num_addrs < 0) {
+ 	outw(0x8008, ioaddr + EL3_CMD);
+   } else {
+ 	outw(0x8005, ioaddr + EL3_CMD);
+   }
+ }
+ 
+ 
+ int
+ el3_probe(struct ei *ei)
+ {
+   static int el3_card_nr = 0;
+   struct sockaddr sa;
+   short lrs_state = 0xFF, i;
+   unsigned short iobase;
+   ushort ioaddr, irq, if_port;
+ 
+   /* Set up the device name. */
+   ei->name = "3C509";
+   sprintf(ei->dev->if_name, "ec%d", el3_card_nr++);
+   irq = 0;
+   if_port = 0;
+ 
+   /* First check for a board on the EISA bus. */
+   if (EISA_bus) {
+ 	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ 		/* Check the standard EISA ID register for an encoded '3Com'. */
+ 		if (inw(ioaddr + 0xC80) != 0x6d50) continue;
+ 
+ 		/* Change the register set to the configuration window 0. */
+ 		outw(0x0800, ioaddr + 0xC80 + EL3_CMD);
+ 
+ 		irq = inw(ioaddr + 8) >> 12;
+ 		if_port = inw(ioaddr + 6) >> 14;
+ 		for (i = 0; i < 3; i++) {
+ 			irq = htons(el3_read_eeprom(ioaddr, i));
+ 			sa.sa_data[i * 2] = (irq & 0xFF);
+ 			sa.sa_data[(i * 2) + 1] = ((irq >> 8) & 0xFF);
+ 		}
+ 
+ 		/* Restore the "Product ID" to the EEPROM read register. */
+ 		el3_read_eeprom(ioaddr, 3);
+ 
+ 		/* Was the EISA code an add-on hack?  Nahhhhh... */
+ 		goto found;
+ 	}
+   }
+ 
+   if (MCA_bus) {
+ #ifdef CONFIG_MCA
+ 	mca_adaptor_select_mode(1);
+ 	for (i = 0; i < 8; i++) {
+ 		if ((mca_adaptor_id(i) | 1) == 0x627C) {
+ 			ioaddr = mca_pos_base_addr(i);
+ 			irq = inw(ioaddr + 8) >> 12;
+ 			if_port = inw(ioaddr + 6) >> 14;
+ 			for (i = 0; i < 3; i++) {
+ 				irq = htons(el3_read_eeprom(ioaddr, i));
+ 				sa.sa_data[i * 2] = (irq & 0xFF);
+ 				sa.sa_data[(i * 2) + 1] = ((irq >> 8) & 0xFF);
+ 			}
+ 			mca_adaptor_select_mode(0);
+ 			goto found;
+ 		}
+ 	}
+ 	mca_adaptor_select_mode(0);
+ #endif
+   } 
+ 
+   /* Send the ID sequence to the ID_PORT. */
+   outb(0x00, ID_PORT);
+   outb(0x00, ID_PORT);
+   for(i = 0; i < 255; i++) {
+ 	outb(lrs_state, ID_PORT);
+ 	lrs_state <<= 1;
+ 	lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xCF : lrs_state;
+   }
+ 
+   /*
+    * For the first probe, clear all board's tag registers.
+    * Otherwise kill off already-found boards.
+    */
+   if (el3_card_nr == 0) outb(0xD0, ID_PORT);
+     else outb(0xD8, ID_PORT);
+ 
+   if (el3_id_read_eeprom(7) != 0x6D50) return(-ENODEV);
+ 
+   /*
+    * Read in EEPROM data, which does contention-select.
+    * Only the lowest address board will stay "on-line".
+    * 3Com got the byte order backwards.
+    */
+   for (i = 0; i < 3; i++) {
+ 	irq = htons(el3_id_read_eeprom(i));
+ 	sa.sa_data[i * 2] = (irq & 0xFF);
+ 	sa.sa_data[(i * 2) + 1] = ((irq >> 8) & 0xFF);
+   }
+ 
+   iobase = el3_id_read_eeprom(8);
+   if_port = iobase >> 14;
+   ioaddr = 0x200 + ((iobase & 0x1F) << 4);
+   irq = el3_id_read_eeprom(9) >> 12;
+ 
+ #if 0
+   /*
+    * The current Space.c structure makes it difficult to have
+    * more than one adaptor initialized.  Send me email if you
+    * have a need for multiple adaptors, and we'll work out
+    * something.				-becker@super.org
+    */
+ if ((ei->ioaddr != 0) &&
+     (ei->ioaddr != (unsigned short) ioaddr)) return(-ENODEV);
+ #endif
+ 
+   /* Set the adaptor tag so that the next card can be found. */
+   outb(0xD0 + ++el3_card_nr, ID_PORT);
+ 
+   /* Activate the adaptor at the EEPROM location. */
+   outb(0xFF, ID_PORT);
+ 
+   EL3WINDOW(0);
+   if (inw(ioaddr) != 0x6D50) return(-ENODEV);
+ 
+ found:
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+   ei->irq = irq;
+   ei->port = if_port;
+   snarf_region(ei->ioaddr, 16);
+ 
+   DPRINTF((DBG_EL3, "%s: %s at 0x%03.3X tag %d, %s port, address",
+ 	ei->dev->if_name, ei->name, ei->ioaddr, el3_card_nr,
+ 	el3_if_names[ei->port]));
+ 
+   /* Read in the station address. */
+   for (i = 0; i < 6; i++)
+ 	DPRINTF((DBG_EL3, " %02.2X", sa.sa_data[i]));
+   DPRINTF((DBG_EL3, ", IRQ%d.\n", ei->irq));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = el3_xmit;
+   ei->chip_s_multi = el3_set_multicast;
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = NULL;
+   ei->card_open = el3_open;
+   ei->card_close = el3_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
*** original/net/drv/becker/LICENSE	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/LICENSE	Mon May  9 13:13:00 1994
***************
*** 0 ****
--- 1,15 ----
+ Code in this directory written at the IDA Supercomputing Research Center
+ carries the following copyright and license.
+ 
+     Copyright 1993 United States Government as represented by the
+     Director, National Security Agency.  This software may be used
+     and distributed according to the terms of the GNU Public License,
+     incorporated herein by reference.
+ 
+     In addition to the disclaimers in the GPL, SRC expressly disclaims any
+     and all warranties, expressed or implied, concerning the enclosed software.
+     This software was developed at SRC for use in internal research, and the
+     intent in sharing this software is to promote the productive interchange
+     of ideas throughout the research community.   All software is furnished
+     on an "as-is" basis.  No further updates to this software should be
+     expected.  Although updates may occur, no commitment exists.
*** original/net/drv/becker/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/Makefile	Mon May  9 13:13:00 1994
***************
*** 0 ****
--- 1,97 ----
+ #
+ # Makefile for the Linux BECKER device driver kit.
+ #
+ # 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...
+ 
+ OBJS	= main.o utils.o ioctl.o auto_irq.o dummy.o
+ CHIPS	=
+ CARDS	=
+ 
+ 
+ ifdef CONFIG_ULTRA
+ CARDS	:= $(CARDS) smc-ultra.o
+ CHIPS	= ns8390.o
+ endif
+ 
+ ifdef CONFIG_WD80x3
+ CARDS	:= $(CARDS) wd.o
+ CHIPS	= ns8390.o
+ endif
+ 
+ ifdef CONFIG_NE2000
+ CARDS	:= $(CARDS) ne.o
+ CHIPS	= ns8390.o
+ endif
+ 
+ ifdef CONFIG_HPLAN
+ CARDS	:= $(CARDS) hp.o
+ CHIPS	= ns8390.o
+ endif
+ 
+ ifdef CONFIG_EL1
+ CARDS	:= $(CARDS) 3c501.o
+ endif
+ 
+ ifdef CONFIG_EL2
+ CARDS	:= $(CARDS) 3c503.o
+ CHIPS	= ns8390.o
+ endif
+ 
+ ifdef CONFIG_EL16
+ CARDS	:= $(CARDS) 3c507.o
+ endif
+ 
+ ifdef CONFIG_EL3
+ CARDS	:= $(CARDS) 3c509.o
+ endif
+ 
+ ifdef CONFIG_ATP
+ CARDS	:= $(CARDS) atp.o
+ endif
+ 
+ ifdef CONFIG_EEXPRESS
+ CARDS	:= $(CARDS) eexpress.o
+ endif
+ 
+ ifdef CONFIG_ZNOTE
+ CARDS	:= $(CARDS) znote.o
+ endif
+ 
+ ifdef CONFIG_AT1500
+ CARDS	:= $(CARDS) at1500.o
+ endif
+ 
+ ifdef CONFIG_AT1700
+ CARDS	:= $(CARDS) at1700.o
+ endif
+ 
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ becker.o:	$(OBJS) $(CHIPS) $(CARDS)
+ 		ld -r -o becker.o $(OBJS) $(CHIPS) $(CARDS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/becker/VERSION	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/VERSION	Mon May  9 13:13:00 1994
***************
*** 0 ****
--- 1,3 ----
+ The ported drivers have version 1.3.0
+ 
+ Updated from the originals up to Linux release 1.1.11
*** original/net/drv/becker/at1500.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/at1500.c	Mon May  9 13:13:00 1994
***************
*** 0 ****
--- 1,871 ----
+ /* at1500.c: An AMD LANCE ethernet driver for linux. */
+ /*
+     Written 1993 by Donald Becker.
+ 
+     Copyright 1993 United States Government as represented by the
+     Director, National Security Agency.  This software may be used and
+     distributed according to the terms of the GNU Public License,
+     incorporated herein by reference.
+ 
+     This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
+     with most other LANCE-based bus-master (NE2100 clone) ethercards.
+ 
+     The author may be reached as becker@super.org or
+     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/fcntl.h>
+ #include <linux/malloc.h>
+ #include <linux/string.h>
+ #include <linux/interrupt.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include <errno.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"lance.c:v0.14g 12/21/93 becker@super.org\n";
+ #endif
+ 
+ /*
+   		Theory of Operation
+ 
+ I. Board Compatibility
+ 
+ This device driver is designed for the AMD 79C960, the "PCnet-ISA
+ single-chip ethernet controller for ISA".  This chip is used in a wide
+ variety of boards from vendors such as Allied Telesis, HP, Kingston,
+ and Boca.  This driver is also intended to work with older AMD 7990
+ designs, such as the NE1500 and NE2100.  For convenience, I use the name
+ LANCE to refer to either AMD chip.
+ 
+ II. Board-specific settings
+ 
+ The driver is designed to work the boards that use the faster
+ bus-master mode, rather than in shared memory mode.  (Only older designs
+ have on-board buffer memory needed to support the slower shared memory mode.)
+ 
+ Most boards have jumpered settings for the I/O base, IRQ line, and DMA channel.
+ This driver probes the likely base addresses, {0x300, 0x320, 0x340, 0x360}.
+ After the board is found it generates an DMA-timeout interrupt and uses
+ autoIRQ to find the IRQ line.  The DMA channel defaults to LANCE_DMA, or it
+ can be set with the low bits of the otherwise-unused dev->mem_start value.
+ 
+ The HP-J2405A board is an exception: with this board it's easy to read the
+ EEPROM-set values for the base, IRQ, and DMA.  Of course you must already
+ _know_ the base address, but that entry is for changing the EEPROM.
+ 
+ III. Driver operation
+ 
+ IIIa. Ring buffers
+ The LANCE uses ring buffers of Tx and Rx descriptors.  Each entry describes
+ the base and length of the data buffer, along with status bits.  The length
+ of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
+ the buffer length (rather than being directly the buffer length) for
+ implementation ease.  The current values are 2 (Tx) and 4 (Rx), which leads to
+ ring sizes of 4 (Tx) and 16 (Rx).  Increasing the number of ring entries
+ needlessly uses extra space and reduces the chance that an upper layer will
+ be able to reorder queued Tx packets based on priority.  Decreasing the number
+ of entries makes it more difficult to achieve back-to-back packet transmission
+ and increases the chance that Rx ring will overflow.  (Consider the worst case
+ of receiving back-to-back minimum-sized packets.)
+ 
+ The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
+ statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
+ avoid the administrative overhead. For the Rx side this avoids dynamically
+ allocating full-sized buffers "just in case", at the expense of a
+ memory-to-memory data copy for each packet received.  For most systems this
+ is an good tradeoff: the Rx buffer will always be in low memory, the copy
+ is inexpensive, and it primes the cache for later packet processing.  For Tx
+ the buffers are only used when needed as low-memory bounce buffers.
+ 
+ IIIB. 16M memory limitations.
+ For the ISA bus master mode all structures used directly by the LANCE,
+ the initialization block, Rx and Tx rings, and data buffers, must be
+ accessable from the ISA bus, i.e. in the lower 16M of real memory.
+ This is a problem for current Linux kernels on >16M machines. The network
+ devices are initialized after memory initialization, and the kernel doles out
+ memory from the top of memory downward.  The current solution is to have a
+ special network initialization routine that's called before memory
+ initialization; this will eventually be generalized for all network devices.
+ As mentioned before, low-memory "bounce-buffers" are used when needed.
+ 
+ IIIC. Synchronization
+ The driver runs as two independent, single-threaded flows of control.  One
+ is the send-packet routine, which enforces single-threaded use by the
+ dev->tbusy flag.  The other thread is the interrupt handler, which is single
+ threaded by the hardware and other software.
+ 
+ The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+ flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+ queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+ the 'lp->tx_full' flag.
+ 
+ The interrupt handler has exclusive control over the Rx ring and records stats
+ from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
+ we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+ stats.)  After reaping the stats, it marks the queue entry as empty by setting
+ the 'base' to zero.  Iff the 'lp->tx_full' flag is set, it clears both the
+ tx_full and tbusy flags.
+ 
+ */
+ 
+ /* Set the number of Tx and Rx buffers, using Log_2(# buffers).
+    Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+    That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). */
+ #ifndef LANCE_LOG_TX_BUFFERS
+ #define LANCE_LOG_TX_BUFFERS	4
+ #define LANCE_LOG_RX_BUFFERS	4
+ #endif
+ 
+ #define TX_RING_SIZE		(1 << (LANCE_LOG_TX_BUFFERS))
+ #define TX_RING_MOD_MASK	(TX_RING_SIZE - 1)
+ #define TX_RING_LEN_BITS	((LANCE_LOG_TX_BUFFERS) << 29)
+ 
+ #define RX_RING_SIZE		(1 << (LANCE_LOG_RX_BUFFERS))
+ #define RX_RING_MOD_MASK	(RX_RING_SIZE - 1)
+ #define RX_RING_LEN_BITS	((LANCE_LOG_RX_BUFFERS) << 29)
+ 
+ #define PKT_BUF_SZ		1544
+ 
+ /* Offsets from base I/O address. */
+ #define LANCE_DATA		0x10
+ #define LANCE_ADDR		0x12
+ #define LANCE_RESET		0x14
+ #define LANCE_BUS_IF		0x16
+ #define LANCE_TOTAL_SIZE	0x18
+ 
+ 
+ /* The LANCE Rx and Tx ring descriptors. */
+ struct lance_rx_head {
+   int		base;
+   short 	buf_length;	/* This length is 2's complement (negative)! */
+   short		msg_length;	/* This length is "normal". */
+ };
+ 
+ struct lance_tx_head {
+   int		base;
+   short		length;		/* Length is 2's complement (negative)! */
+   short		misc;
+ };
+ 
+ /* The LANCE initialization block, described in databook. */
+ struct lance_i_block {
+   unsigned short	mode;	/* Pre-set mode (reg. 15) */
+   unsigned char		phys_addr[ETH_ALEN];	/* Physical ethernet address */
+   unsigned		filter[2];	/* Multicast filter (unused). */
+ 
+   /* Receive and transmit ring base, along with extra bits. */
+   unsigned		rx_ring;	/* Tx and Rx ring base pointers */
+   unsigned		tx_ring;
+ };
+ 
+ struct lance {
+   char			devname[8];
+ 
+   /* These must aligned on 8-byte boundaries. */
+   struct lance_rx_head	rx_ring[RX_RING_SIZE];
+   struct lance_tx_head	tx_ring[TX_RING_SIZE];
+   struct lance_i_block	init_block;
+   long			rx_buffs;	/* Address of Rx and Tx buffers. */
+ 
+   /* Tx low-memory "bounce buffer" address. */
+   char			(*tx_bounce_buffs)[PKT_BUF_SZ];
+   int			cur_rx, cur_tx;	/* The next free ring entry */
+   int			dirty_rx,	/* The ring entries to be free()ed. */
+ 			dirty_tx;
+   int			dma;
+   char			old_lance;
+   int			pad0, pad1;	/* Used for alignment */
+ };
+ #define EIPVT(ei)	((struct lance *) (ei)->pvt)
+ 
+ 
+ static struct ei *lance_irq2ei_map[16] = { NULL };
+ 
+ 
+ /* Initialize the LANCE Rx and Tx rings. */
+ static void
+ lance_init_ring(struct ei *ei)
+ {
+   int i;
+ 
+   EIPVT(ei)->cur_rx = EIPVT(ei)->cur_tx = 0;
+   EIPVT(ei)->dirty_rx = EIPVT(ei)->dirty_tx = 0;
+ 
+   for (i = 0; i < RX_RING_SIZE; i++) {
+ 	EIPVT(ei)->rx_ring[i].base =
+ 		(EIPVT(ei)->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
+ 	EIPVT(ei)->rx_ring[i].buf_length = -PKT_BUF_SZ;
+   }
+ 
+   /*
+    * The Tx buffer address is filled in as needed, but we do
+    * need to clear the upper ownership bit.
+    */
+   for (i = 0; i < TX_RING_SIZE; i++) {
+ 	EIPVT(ei)->tx_ring[i].base = 0;
+   }
+ 
+   EIPVT(ei)->init_block.mode = 0x0000;
+ 
+   /* Set the LANCE phys address. */
+   memcpy(EIPVT(ei)->init_block.phys_addr,
+ 	((struct sockaddr *) ei->dev->mac_aget(ei->dev))->sa_data, ETH_ALEN);
+ 
+   EIPVT(ei)->init_block.filter[0] = 0x00000000;
+   EIPVT(ei)->init_block.filter[1] = 0x00000000;
+   EIPVT(ei)->init_block.rx_ring = (int)EIPVT(ei)->rx_ring | RX_RING_LEN_BITS;
+   EIPVT(ei)->init_block.tx_ring = (int)EIPVT(ei)->tx_ring | TX_RING_LEN_BITS;
+ }
+ 
+ 
+ /* We have a good packet(s), get it/them out of the buffers. */
+ static void
+ lance_receive(struct ei *ei)
+ {
+   int entry = EIPVT(ei)->cur_rx & RX_RING_MOD_MASK;
+ 
+   /* If we own the next entry, it's a new packet. Send it up. */
+   while (EIPVT(ei)->rx_ring[entry].base >= 0) {
+ 	int status = EIPVT(ei)->rx_ring[entry].base >> 24;
+ 
+ 	if (status != 0x03) {	/* There was an error. */
+ 		/*
+ 		 * There is a tricky error noted by John Murphy,
+ 		 * <murf@perftech.com>, to Russ Nelson: even with full-sized
+ 		 * buffers it's possible for a jabber packet to use two
+ 		 * buffers, with only the last correctly noting the error.
+ 		 */
+ 		if (status & 0x01) /* only count a general error at the end... */
+ 			ei->dev->if_stats->rx_errors++;	/* ... of a packet */
+ 
+ 		if (status & 0x20)
+ 			ei->dev->if_stats->rx_frame++;
+ 		if (status & 0x10)
+ 			ei->dev->if_stats->rx_overrun++;
+ 		if (status & 0x08)
+ 			ei->dev->if_stats->rx_crc++;
+ 		if (status & 0x04)
+ 			ei->dev->if_stats->rx_fifo++;
+ 	} else {
+ 		/* Malloc up new buffer. */
+ 		short pkt_len = EIPVT(ei)->rx_ring[entry].msg_length;
+ 		struct sk_buff *skb;
+ 
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ 			printk("%s: memory squeeze, deferring packet.\n",
+ 							ei->dev->if_name);
+ 			ei->dev->if_stats->rx_dropped++;/* really, deferred */
+ 			break;
+ 		}
+ 		skb->len = pkt_len;
+ 		skb->dev = ei->dev;
+ 
+ 		memcpy(skb->raw,
+ 		       (unsigned char *)
+ 				(EIPVT(ei)->rx_ring[entry].base & 0x00ffffff),
+ 			pkt_len);
+ 
+ 		if (dev_rint(ei->dev, skb, pkt_len) != 0) {
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		ei->dev->if_stats->rx_packets++;
+ 
+ 		EIPVT(ei)->rx_ring[entry].base |= 0x80000000;
+ 		entry = (++EIPVT(ei)->cur_rx) & RX_RING_MOD_MASK;
+ 	}
+ 
+ 	/*
+ 	 * We should check that at least two ring entries are free.
+ 	 * If not, we should free one and mark stats->rx_dropped++.
+ 	 */
+   }
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the network interface interrupts.
+  */
+ static void
+ lance_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = lance_irq2ei_map[irq];
+   int ioaddr, csr0;
+ 
+   if (ei == NULL) {
+ 	printk("at1500: interrupt: IRQ%d for unknown device\n", irq);
+ 	return;
+   }
+   ioaddr = ei->ioaddr;
+ 
+   if (ei->interrupt)
+ 	printk("%s: re-entering the interrupt handler\n", ei->dev->if_name);
+   ei->interrupt = 1;
+ 
+   outw(0x00, ioaddr + LANCE_ADDR);
+   csr0 = inw(ioaddr + LANCE_DATA);
+ 
+   /* Acknowledge all of the current interrupt sources ASAP. */
+   outw(csr0 & ~0x004F, ioaddr + LANCE_DATA);
+ 
+   DPRINTF((DBG_AT1500, "%s: interrupt  csr0=0x%2.2X new csr=0x%2.2X\n",
+ 			ei->dev->if_name, csr0, inw(ioaddr + LANCE_DATA)));
+ 
+   if (csr0 & 0x0400)		/* RX interrupt */
+ 	lance_receive(ei);
+ 
+   if (csr0 & 0x0200) {	/* TX-done interrupt */
+ 	int dirty_tx = EIPVT(ei)->dirty_tx;
+ 
+ 	while (dirty_tx < EIPVT(ei)->cur_tx) {
+ 		int entry = dirty_tx & TX_RING_MOD_MASK;
+ 		int status = EIPVT(ei)->tx_ring[entry].base;
+ 	    
+ 		if (status < 0) break;		/* it still hasn't been TXed */
+ 
+ 		EIPVT(ei)->tx_ring[entry].base = 0;
+ 
+ 		if (status & 0x40000000) {
+ 			/* There was a major error, log it. */
+ 			int err_status = EIPVT(ei)->tx_ring[entry].misc;
+ 
+ 			ei->dev->if_stats->tx_errors++;
+ 			if (err_status & 0x0400)
+ 				ei->dev->if_stats->tx_aborted++;
+ 			if (err_status & 0x0800)
+ 				ei->dev->if_stats->tx_carrier++;
+ 			if (err_status & 0x1000)
+ 				ei->dev->if_stats->tx_window++;
+ 			if (err_status & 0x4000)
+ 				ei->dev->if_stats->tx_fifo++;
+ 			/* Perhaps we should re-init() after the FIFO error. */
+ 		} else {
+ 			if (status & 0x18000000)
+ 				ei->dev->if_stats->tx_collision++;
+ 			ei->dev->if_stats->tx_packets++;
+ 		}
+ 		dirty_tx++;
+ 	}
+ 
+ #ifndef final_version
+ 	if (EIPVT(ei)->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ 		printk("out-of-sync dirty pointer, %d vs. %d.\n",
+ 					dirty_tx, EIPVT(ei)->cur_tx);
+ 		dirty_tx += TX_RING_SIZE;
+ 	}
+ #endif
+ 
+ 	if (ei->dev->if_tbusy && dirty_tx >
+ 	    EIPVT(ei)->cur_tx - TX_RING_SIZE + 2) {
+ 		/* The ring is no longer full, clear tbusy. */
+ 		ei->dev->if_tbusy = 0;
+ 		mark_bh(NET_BH);
+ 	}
+ 	EIPVT(ei)->dirty_tx = dirty_tx;
+   }
+ 
+   if (csr0 & 0x8000) {
+ 	if (csr0 & 0x4000) ei->dev->if_stats->tx_errors++;
+ 	if (csr0 & 0x1000) ei->dev->if_stats->rx_errors++;
+   }
+ 
+   /* Clear the interrupts we've handled. */
+   outw(0x0000, ioaddr + LANCE_ADDR);
+   outw(0x7f40, ioaddr + LANCE_DATA);
+ 
+   DPRINTF((DBG_AT1500, "%s: exiting interrupt, csr%d=0x%4.4X\n",
+ 	ei->dev->if_name, inw(ioaddr + LANCE_ADDR), inw(ioaddr + LANCE_DATA)));
+ 
+   ei->interrupt = 0;
+ }
+ 
+ 
+ /*
+  * Open/initialize the board.
+  * This is called (in the current kernel) sometime after booting
+  * when the 'ifconfig' program is run.
+  */
+ static int
+ lance_open(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+   int i;
+ 
+   if (request_irq(ei->irq, lance_interrupt)) return(-EAGAIN);
+   if (request_dma(ei->dma)) {
+ 	free_irq(ei->irq);
+ 	return(-EAGAIN);
+   }
+   lance_irq2ei_map[ei->irq] = ei;
+ 
+   /* Reset the LANCE */
+   inw(ioaddr+LANCE_RESET);
+ 
+   /* The DMA controller is used as a no-operation slave, "cascade mode". */
+   enable_dma(ei->dma);
+   set_dma_mode(ei->dma, DMA_MODE_CASCADE);
+ 
+   /* Un-Reset the LANCE, needed only for the NE2100. */
+   if (EIPVT(ei)->old_lance) outw(0, ioaddr + LANCE_RESET);
+ 
+   if (! EIPVT(ei)->old_lance) {
+ 	/* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
+ 	outw(0x0002, ioaddr + LANCE_ADDR);
+ 	outw(0x0002, ioaddr + LANCE_BUS_IF);
+   }
+ 
+   DPRINTF((DBG_AT1500,
+ 	"%s: open: IRQ%d DMA%d tx/rx rings 0x%X/0x%X init 0x%X\n",
+ 		ei->dev->if_name, ei->irq, ei->dma,
+ 		(int) EIPVT(ei)->tx_ring, (int) EIPVT(ei)->rx_ring,
+ 		(int) &EIPVT(ei)->init_block));
+ 
+   lance_init_ring(ei);
+ 
+   /* Re-initialize the LANCE, and start it when done. */
+   outw(0x0001, ioaddr + LANCE_ADDR);
+   outw((short) (int) &EIPVT(ei)->init_block, ioaddr + LANCE_DATA);
+   outw(0x0002, ioaddr + LANCE_ADDR);
+   outw(((int)&EIPVT(ei)->init_block) >> 16, ioaddr + LANCE_DATA);
+ 
+   outw(0x0004, ioaddr + LANCE_ADDR);
+   outw(0x0d15, ioaddr + LANCE_DATA);
+ 
+   outw(0x0000, ioaddr + LANCE_ADDR);
+   outw(0x0001, ioaddr + LANCE_DATA);
+ 
+   ei->dev->if_tbusy = 0;
+   ei->interrupt = 0;
+   ei->start = 1;
+   i = 0;
+   while (i++ < 100) {
+ 	if (inw(ioaddr + LANCE_DATA) & 0x0100) break;
+   }
+   outw(0x0142, ioaddr + LANCE_DATA);
+ 
+   DPRINTF((DBG_AT1500,
+ 	"%s: LANCE open after %d ticks, init block 0x%X csr0 0x%4.4X\n",
+ 		ei->dev->if_name, i, (int) &EIPVT(ei)->init_block,
+ 		inw(ioaddr + LANCE_DATA)));
+ 
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to lance_open(). */
+ static int
+ lance_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   outw(112, ioaddr+LANCE_ADDR);
+   ei->dev->if_stats->rx_missed = inw(ioaddr + LANCE_DATA);
+ 
+   outw(0, ioaddr + LANCE_ADDR);
+ 
+   DPRINTF((DBG_AT1500, "%s: shutting down ethercard, status was 0x%2.2X\n",
+ 				ei->dev->if_name, inw(ioaddr + LANCE_DATA)));
+ 
+   /* We stop the LANCE here -- it occasionally polls memory if we don't. */
+   outw(0x0004, ioaddr + LANCE_DATA);
+ 
+   disable_dma(ei->dma);
+ 
+   free_irq(ei->irq);
+   free_dma(ei->dma);
+ 
+   lance_irq2ei_map[ei->irq] = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ lance_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int ioaddr, entry;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in lance_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+ 
+   /* Transmitter timeout, serious problems. */
+   if (dev->if_tbusy) {
+ 	/*
+ 	 * If we get here, some higher level has decided we are broken.
+ 	 * There should really be a "kick me" function call instead.
+ 	 */
+ 	int tickssofar = (jiffies - dev->if_txtimer);
+ 
+ 	if (tickssofar < 10) return(1);
+ 	outw(0, ioaddr + LANCE_ADDR);
+ 	printk("%s: transmit timed out, status 0x%4.4X, resetting\n",
+ 					dev->if_name, inw(ioaddr+LANCE_DATA));
+ 	outw(0x0001, ioaddr + LANCE_DATA);
+ 	dev->if_stats->tx_errors++;
+ 
+ #ifndef final_version
+ 	{
+ 	    int i;
+ 
+ 	    printk(" Ring data dump: dirty_tx %d cur_tx %d cur_rx %d.",
+ 		   EIPVT(ei)->dirty_tx, EIPVT(ei)->cur_tx, EIPVT(ei)->cur_rx);
+ 	    for (i = 0 ; i < RX_RING_SIZE; i++)
+ 		printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ 			EIPVT(ei)->rx_ring[i].base,
+ 			-EIPVT(ei)->rx_ring[i].buf_length,
+ 			EIPVT(ei)->rx_ring[i].msg_length);
+ 	    for (i = 0 ; i < TX_RING_SIZE; i++)
+ 		printk(" %s%08x %04x %04x", i & 0x3 ? "" : "\n ",
+ 			EIPVT(ei)->tx_ring[i].base,
+ 			-EIPVT(ei)->tx_ring[i].length,
+ 			EIPVT(ei)->tx_ring[i].misc);
+ 		printk("\n");
+ 	}
+ #endif
+ 	lance_init_ring(ei);
+ 	outw(0x0043, ioaddr + LANCE_DATA);
+ 
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+ 	return(0);
+   }
+ 
+   /*
+    * If some higher layer thinks we've missed an tx-done interrupt
+    * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+    * itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   if (skb->len <= 0) return(0);
+ 
+   /*
+    * Block a timer-based transmit from overlapping.  This could
+    * better be done with atomic_swap(1, dev->tbusy), but set_bit()
+    * works as well.
+    */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 		printk("%s: transmitter access conflict\n", dev->if_name);
+     else {
+ 	/* Fill in a Tx ring entry */
+ 
+ 	/* Mask to ring buffer boundary. */
+ 	entry = EIPVT(ei)->cur_tx & TX_RING_MOD_MASK;
+ 
+ 	/*
+ 	 * Caution: the write order is important here, set the
+ 	 * base address with the "ownership" bits last.
+ 	 */
+ 
+ 	/*
+ 	 * The old LANCE chips doesn't automatically pad buffers
+ 	 * to min. size.
+ 	 */
+ 	if (EIPVT(ei)->old_lance) {
+ 		EIPVT(ei)->tx_ring[entry].length =
+ 			-(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+ 	} else {
+ 		EIPVT(ei)->tx_ring[entry].length = -skb->len;
+ 	}
+ 
+ 	EIPVT(ei)->tx_ring[entry].misc = 0x0000;
+ 
+ 	/*
+ 	 * If any part of this buffer is >16M we must copy it
+ 	 * to a low-memory buffer.
+ 	 */
+ 	if ((int)skb->raw + skb->len > 0x01000000) {
+ 		DPRINTF((DBG_AT1500,
+ 			"%s: bouncing a high-memory packet (0x%lX).\n",
+ 						dev->if_name, (int)skb->raw));
+ 		memcpy(&EIPVT(ei)->tx_bounce_buffs[entry], skb->raw, skb->len);
+ 		EIPVT(ei)->tx_ring[entry].base =
+ 			(int)(EIPVT(ei)->tx_bounce_buffs + entry) | 0x83000000;
+ 	} else {
+ 		EIPVT(ei)->tx_ring[entry].base = (int)skb->raw | 0x83000000;
+ 	}
+ 
+ 	EIPVT(ei)->cur_tx++;
+ 
+ 	/* Trigger an immediate send poll. */
+ 	outw(0x0000, ioaddr+LANCE_ADDR);
+ 	outw(0x0048, ioaddr+LANCE_DATA);
+ 
+ 	dev->if_txtimer = jiffies;
+ 
+ 	if (EIPVT(ei)->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+ 							dev->if_tbusy = 0;
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ lance_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   /* We take the simple way out and always enable promiscuous mode. */
+   outw(0, ioaddr + LANCE_ADDR);
+   outw(0x0004, ioaddr + LANCE_DATA); /* temporarily stop the LANCE */
+ 
+   outw(15, ioaddr + LANCE_ADDR);
+   if (num_addrs >= 0) {
+ 	short multicast_table[4];
+ 	int i;
+ 
+ 	/*
+ 	 * We don't use the multicast table, but rely on
+ 	 * upper-layer filtering.
+ 	 */
+ 	memset(multicast_table, (num_addrs == 0)
+ 		? 0
+ 		: -1,
+ 		sizeof(multicast_table));
+ 
+ 	for (i = 0; i < 4; i++) {
+ 	    outw(8 + i, ioaddr + LANCE_ADDR);
+ 	    outw(multicast_table[i], ioaddr + LANCE_DATA);
+ 	}
+ 	outw(0x0000, ioaddr + LANCE_DATA); /* unset promiscuous mode */
+   } else {
+ 	outw(0x8000, ioaddr + LANCE_DATA); /* set promiscuous mode */
+   }
+ 
+   outw(0, ioaddr + LANCE_ADDR);
+   outw(0x0142, ioaddr + LANCE_DATA); /* resume normal operation */
+ }
+ 
+ 
+ static int
+ lance_probe1(short ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int hpJ2405A = 0;
+   int i, reset_val;
+ 
+   DPRINTF((DBG_AT1500, "%s: LANCE probe at 0x%X,", ei->dev->if_name, ioaddr));
+ 
+   hpJ2405A = ((inb(ioaddr) == 0x08) &&
+ 	      (inb(ioaddr+1) == 0x00) &&
+ 	      (inb(ioaddr+2) == 0x09));
+ 
+   /* Reset the LANCE. */
+   reset_val = inw(ioaddr+LANCE_RESET);
+ 
+   /*
+    * The Un-Reset needed is only needed for the real NE2100,
+    * and will confuse the HP board.
+    */
+   if (! hpJ2405A) outw(reset_val, ioaddr+LANCE_RESET);
+ 
+   outw(0x0000, ioaddr+LANCE_ADDR); /* wwitch to window 0 */
+ 
+   if (inw(ioaddr+LANCE_DATA) != 0x0004) return(-ENODEV);
+ 
+ #if 0
+   /*
+    * See below as well.  This piece of code allocates the memory
+    * block needed by the entire LANCE chip, including control
+    * blocks and buffers.  The routine will make sure the memory
+    * is going to be allocated in the lower 16M of RAM.
+    */
+   dev = init_etherdev(0, sizeof(struct lance) +
+ 			 PKT_BUF_SZ*(RX_RING_SIZE + TX_RING_SIZE), &mem_start);
+ #endif
+ 
+   /*
+    * There is a 16 byte station address PROM at the base address.
+    * The first six bytes are the station address.
+    */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(ioaddr + i);
+ 	DPRINTF((DBG_AT1500, " %02.2X", sa.sa_data[i]));
+   }
+ 
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+   snarf_region(ioaddr, LANCE_TOTAL_SIZE);
+ 
+   /*
+    * See above too.
+    * Allocate a control block and make certain the
+    * data structures used by the LANCE are aligned.
+    */
+   i = sizeof(struct lance) + (PKT_BUF_SZ * (RX_RING_SIZE + TX_RING_SIZE));
+ #if 1
+   EIPVT(ei) = (struct lance *) kmalloc(i, GFP_KERNEL);
+ #else
+   EIPVT(ei) = (struct lance *) vmalloc(i);
+ #endif
+   if (EIPVT(ei) == NULL) {
+ 	DPRINTF((DBG_AT1500, "No memory for LANCE control block !\n"));
+ 	return(-ENOMEM);
+   }
+   memset(EIPVT(ei), 0, sizeof(struct lance));
+   EIPVT(ei) = (void *)(((int) EIPVT(ei) + 7) & ~7);
+   EIPVT(ei)->rx_buffs = (long)EIPVT(ei) + sizeof(struct lance);
+   EIPVT(ei)->tx_bounce_buffs = (char (*)[PKT_BUF_SZ])
+ 			   (EIPVT(ei)->rx_buffs + PKT_BUF_SZ*RX_RING_SIZE);
+ 
+ #ifndef final_version
+   /* This should never happen. */
+   if ((int)(EIPVT(ei)->rx_ring) & 0x07) {
+ 	printk(" **ERROR** LANCE Rx and Tx rings not on even boundary.\n");
+ 	return(-ENODEV);
+   }
+ #endif
+ 
+   outw(88, ioaddr+LANCE_ADDR);
+   EIPVT(ei)->old_lance = (inw(ioaddr+LANCE_DATA) != 0x3003);
+ 
+ #ifdef notdef
+   printk(EIPVT(ei)->old_lance
+ 		? " original LANCE (%04x)"
+ 		: " PCnet-ISA LANCE (%04x)", inw(ioaddr+LANCE_DATA)));
+ #endif
+ 
+   EIPVT(ei)->init_block.mode = 0x0003;	/* Disable Rx and Tx. */
+ 
+   /* Set LANCE phys address. */
+   memcpy(EIPVT(ei)->init_block.phys_addr,
+ 	((struct sockaddr *) ei->dev->mac_aget(ei->dev))->sa_data, ETH_ALEN);
+ 
+   EIPVT(ei)->init_block.filter[0] = 0x00000000;
+   EIPVT(ei)->init_block.filter[1] = 0x00000000;
+   EIPVT(ei)->init_block.rx_ring = (int)EIPVT(ei)->rx_ring | RX_RING_LEN_BITS;
+   EIPVT(ei)->init_block.tx_ring = (int)EIPVT(ei)->tx_ring | TX_RING_LEN_BITS;
+ 
+   outw(0x0001, ioaddr+LANCE_ADDR);
+   outw((short) (int) &EIPVT(ei)->init_block, ioaddr+LANCE_DATA);
+   outw(0x0002, ioaddr+LANCE_ADDR);
+   outw(((int)&EIPVT(ei)->init_block) >> 16, ioaddr+LANCE_DATA);
+   outw(0x0000, ioaddr+LANCE_ADDR);
+ 
+   if (hpJ2405A) {
+ 	char dma_tbl[4] = { 3, 5, 6, 7 };
+ 	char irq_tbl[8] = { 3, 4, 5, 9, 10, 11, 12, 15 };
+ 	short reset_val = inw(ioaddr+LANCE_RESET);
+ 
+ 	ei->dma = dma_tbl[(reset_val >> 2) & 3];
+ 	ei->irq = irq_tbl[(reset_val >> 4) & 7];
+ 	DPRINTF((DBG_AT1500, " HP J2405A IRQ %d DMA %d.\n", ei->irq, ei->dma));
+     } else {
+ 	/*
+ 	 * To auto-IRQ we enable the initialization-done and DMA
+ 	 * err, interrupts. For now we will always get a DMA error.
+ 	 */
+ 	if (ei->irq < 2) {
+ 		autoirq_setup(0);
+ 
+ 		/* Trigger an initialization just for the interrupt. */
+ 		outw(0x0041, ioaddr+LANCE_DATA);
+ 
+ 		ei->irq = autoirq_report(1);
+ 		if (ei->irq) DPRINTF((DBG_AT1500,
+ 		    ", probed IRQ %d, fixed at DMA %d.\n", ei->irq, ei->dma));
+ 		  else {
+ 			DPRINTF((DBG_AT1500, ", failed to detect IRQ line.\n"));
+ 			return(-ENODEV);
+ 		}
+ 	} else {
+ 		DPRINTF((DBG_AT1500, " assigned IRQ %d DMA %d.\n",
+ 							ei->irq, ei->dma));
+ 	}
+   }
+ 
+   if (! EIPVT(ei)->old_lance) {
+ 	/*
+ 	 * Turn on auto-select of media (10baseT or BNC) so that
+ 	 * the user can watch the LEDs even if the board isn't
+ 	 * opened.
+ 	 */
+ 	outw(0x0002, ioaddr + LANCE_ADDR);
+ 	outw(0x0002, ioaddr + LANCE_BUS_IF);
+   }
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = lance_xmit;
+   ei->chip_s_multi = lance_set_multicast;
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = NULL;
+   ei->card_open = lance_open;
+   ei->card_close = lance_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /* Check for a network adaptor of this type, and return '0' iff one exists. */
+ int
+ at1500_probe(struct ei *ei)
+ {
+   static int lance_card_nr = 0;
+   int *port, ports[] = { 0x300, 0x320, 0x340, 0x360, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "LANCE";
+   sprintf(ei->dev->if_name, "la%d", lance_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(lance_probe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	int ioaddr = *port;
+ 
+ 	if (check_region(ioaddr, LANCE_TOTAL_SIZE)) continue;
+ 
+ 	if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57))
+ 								continue;
+ 	if (lance_probe1(ioaddr, ei) == 0) return(0);
+   }
+   return(-ENODEV);
+ }
*** original/net/drv/becker/at1700.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/at1700.c	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,641 ----
+ /* at1700.c: A network device driver for  the Allied Telesis AT1700.
+ 
+    Written 1993 by Donald Becker.  This is a alpha test limited release.
+    This version may only be used and distributed according to the terms of the
+    GNU Public License, incorporated herein by reference.
+ 
+    The author may be reached as becker@super.org or
+    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+    This is a device driver for the Allied Telesis AT1700, which is a
+    straight-foward Fujitsu MB86965 implementation.
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/fcntl.h>
+ #include <linux/malloc.h>
+ #include <linux/string.h>
+ #include <linux/interrupt.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include <errno.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"at1700.c:v0.06 3/3/94  Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ /*
+   Sources:
+     The Fujitsu MB86695 datasheet.
+ 
+ 	After this driver was written, ATI provided their EEPROM configuration
+ 	code header file.  Thanks to Gerry Sockins of ATI.
+ */
+ 
+ typedef unsigned char uchar;
+ 
+ 
+ /* Information that need to be kept for each board. */
+ struct at1700 {
+   long		open_time;		/* Useless example local info. */
+   uint		tx_started:1;		/* Number of packet on the Tx queue. */
+   uchar		tx_queue;		/* Number of packet on the Tx queue. */
+   ushort	tx_queue_len;		/* Current length of the Tx queue. */
+ };
+ #define EIPVT(ei)	((struct at1700 *) (ei)->pvt)
+ 
+ 
+ /* Offsets from the base address. */
+ #define STATUS			0
+ #define TX_STATUS		0
+ #define RX_STATUS		1
+ #define TX_INTR			2	/* Bit-mapped interrupt enable registers. */
+ #define RX_INTR			3
+ #define TX_MODE			4
+ #define RX_MODE			5
+ #define CONFIG_0		6	/* Misc. configuration settings. */
+ #define CONFIG_1		7
+ 
+ /* Run-time register bank 2 definitions. */
+ #define DATAPORT		8	/* Word-wide DMA or programmed-I/O dataport. */
+ #define TX_START		10
+ #define MODE13			13
+ #define EEPROM_Ctrl		16
+ #define EEPROM_Data		17
+ 
+ /* EEPROM_Ctrl bits. */
+ #define EE_SHIFT_CLK		0x40	/* EEPROM shift clock, in reg. 16. */
+ #define EE_CS			0x20	/* EEPROM chip select, in reg. 16. */
+ #define EE_DATA_WRITE		0x80	/* EEPROM chip data in, in reg. 17. */
+ #define EE_DATA_READ		0x80	/* EEPROM chip data out, in reg. 17. */
+ 
+ /* Delay between EEPROM clock transitions. */
+ #define eeprom_delay()	do \
+   { \
+ 	int _i = 40; \
+ 	while (--_i > 0) { \
+ 		__SLOW_DOWN_IO; \
+ 	} \
+   } while (0)
+ 
+ /* The EEPROM commands include the alway-set leading bit. */
+ #define EE_WRITE_CMD		(5 << 6)
+ #define EE_READ_CMD		(6 << 6)
+ #define EE_ERASE_CMD		(7 << 6)
+ 
+ 
+ static struct ei *at1700_irq2ei_map[16];
+ 
+ 
+ static int
+ read_eeprom(int ioaddr, int location)
+ {
+   int i;
+   unsigned short retval = 0;
+   short ee_addr = ioaddr + EEPROM_Ctrl;
+   short ee_daddr = ioaddr + EEPROM_Data;
+   int read_cmd = location | EE_READ_CMD;
+   short ctrl_val = EE_CS;
+ 	
+   outb(ctrl_val, ee_addr);
+ 	
+   /* Shift the read command bits out. */
+   for (i = 9; i >= 0; i--) {
+ 	short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ 	outb(dataval, ee_daddr);
+ 	outb(EE_CS | EE_SHIFT_CLK, ee_addr);	/* EEPROM clock tick. */
+ 	eeprom_delay();
+ 	outb(EE_CS, ee_addr);	/* Finish EEPROM a clock tick. */
+ 	eeprom_delay();
+   }
+   outb(EE_CS, ee_addr);
+ 	
+   for (i = 16; i > 0; i--) {
+ 	outb(EE_CS | EE_SHIFT_CLK, ee_addr);
+ 	eeprom_delay();
+ 	retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
+ 	outb(EE_CS, ee_addr);
+ 	eeprom_delay();
+   }
+ 
+   /* Terminate the EEPROM access. */
+   ctrl_val &= ~EE_CS;
+   outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+   eeprom_delay();
+   outb(ctrl_val, ee_addr);
+   eeprom_delay();
+   return(retval);
+ }
+ 
+ 
+ /* We have a good packet(s), get it/them out of the buffers. */
+ static void
+ at1700_receive(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+   int boguscount = 5;
+ 
+   while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
+ 	ushort status = inw(ioaddr + DATAPORT);
+ 
+ 	DPRINTF((DBG_AT1700, "%s: RXing packet mode 0x%02X status 0x%04X\n",
+ 		ei->dev->if_name, inb(ioaddr + RX_MODE), status));
+ #ifndef final_version
+ 	if (status == 0) {
+ 		outb(0x05, ioaddr + 14);
+ 		break;
+ 	}
+ #endif
+ 
+ 	if ((status & 0xF0) != 0x20) {	/* There was an error. */
+ 		ei->dev->if_stats->rx_errors++;
+ 		if (status & 0x08)
+ 			ei->dev->if_stats->rx_length++;
+ 		if (status & 0x04)
+ 			ei->dev->if_stats->rx_frame++;
+ 		if (status & 0x02)
+ 			ei->dev->if_stats->rx_crc++;
+ 		if (status & 0x01)
+ 			ei->dev->if_stats->rx_overrun++;
+ 	} else {
+ 		/* Malloc up new buffer. */
+ 		ushort pkt_len = inw(ioaddr + DATAPORT);
+ 		struct sk_buff *skb;
+ 
+ 		if (pkt_len > 1550) {
+ 			printk("%s: AT1700 claimed very large packet (%u)\n",
+ 				ei->dev->if_name, pkt_len);
+ 			outb(0x05, ioaddr + 14);
+ 			ei->dev->if_stats->rx_errors++;
+ 			break;
+ 		}
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ 			printk("%s: memory squeeze, dropping packet (len %u)\n",
+ 				ei->dev->if_name, pkt_len);
+ 			outb(0x05, ioaddr + 14);
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		skb->len = pkt_len;
+ 		skb->dev = ei->dev;
+ 
+ 		/* 'skb->raw' points to the start of sk_buff data area. */
+ 		insw(ioaddr + DATAPORT, (void *)skb->raw, (pkt_len + 1) >> 1);
+ 
+ 		if (dev_rint(ei->dev, skb, pkt_len) != 0) {
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		ei->dev->if_stats->rx_packets++;
+ 		if (--boguscount <= 0) break;
+ 	}
+   }
+ 
+   /*
+    * If any worth-while packets have been received, dev_rint()
+    * has done a mark_bh(NET_BH) for us and will work on them
+    * when we get to the bottom-half routine.
+    */
+   {
+ 	int i;
+ 
+ 	for (i = 0; i < 20; i++) {
+ 		if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40) break;
+ 		outb(0x05, ioaddr + 14);
+ 	}
+ 
+ 	DPRINTF((DBG_AT1700,
+ 		"%s: Exint RX packet with mode 0x%02X after %d ticks\n", 
+ 			ei->dev->if_name, inb(ioaddr + RX_MODE), i));
+   }
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the network interface interrupts.
+  */
+ static void
+ at1700_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = at1700_irq2ei_map[irq];
+   int ioaddr, status;
+ 
+   if (ei == NULL) {
+ 	printk("AT1700: interrupt: IRQ%d for unknown device\n", irq);
+ 	return;
+   }
+   ei->interrupt = 1;
+   ioaddr = ei->ioaddr;
+ 
+   status = inw(ioaddr + TX_STATUS);
+   outw(status, ioaddr + TX_STATUS);
+ 
+   DPRINTF((DBG_AT1700, "%s: interrupt status=0x%02.2X\n",
+ 				ei->dev->if_name, status));
+ 
+   if ((status & 0xff00) || (inb(ioaddr + RX_MODE) & 0x40) == 0) {			/* Got a packet(s). */
+ 	at1700_receive(ei);
+   }
+ 
+   if (status & 0x00ff) {
+ 	if (status & 0x80) {
+ 		ei->dev->if_stats->tx_packets++;
+ 		if (EIPVT(ei)->tx_queue) {
+ 			outb(0x80 | EIPVT(ei)->tx_queue, ioaddr + TX_START);
+ 			EIPVT(ei)->tx_queue = 0;
+ 			EIPVT(ei)->tx_queue_len = 0;
+ 			ei->dev->if_txtimer = jiffies;
+ 			ei->dev->if_tbusy = 0;
+ 			mark_bh(NET_BH);
+ 		} else {
+ 			EIPVT(ei)->tx_started = 0;
+ 
+ 			/* Turn on Tx interrupts off. */
+ 			outb(0x00, ioaddr + TX_INTR);
+ 			ei->dev->if_tbusy = 0;
+ 		}
+ 	}
+   }
+ 
+   ei->interrupt = 0;
+ }
+ 
+ 
+ /*
+  * Open/initialize the board.
+  * This is called (in the current kernel) sometime after booting
+  * when the 'ifconfig' program is run.
+  */
+ static int
+ at1700_open(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+   unsigned char *ptr;
+   int i;
+ 
+   /* Powerup the chip, initialize config register 1, and select bank 0. */
+   outb(0xe0, ioaddr + CONFIG_1);
+ 
+   /* Set the station address in bank zero. */
+   ptr = ((struct sockaddr *) ei->dev->mac_aget(ei->dev))->sa_data;
+   for (i = 0; i < ETH_ALEN; i++)
+ 	outb(ptr[i], ioaddr + 8 + i);
+ 
+   /* Switch to bank 1 and set the multicast table to accept none. */
+   outb(0xe4, ioaddr + 7);
+   for (i = 0; i < 8; i++) outb(0x00, ioaddr + 8 + i);
+ 
+   /*
+    * Set the configuration register 0 to 32K 100ns. byte-wide
+    * memory, 16 bit bus access, and two 4K Tx queues.
+    */
+   outb(0xda, ioaddr + CONFIG_0);
+ 
+   /* Same config 0, except enable the Rx and Tx. */
+   outb(0x5a, ioaddr + CONFIG_0);
+ 
+   /* Switch to register bank 2 for the run-time registers. */
+   outb(0xe8, ioaddr + CONFIG_1);
+ 
+   /* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
+   outb(0x00, ioaddr + TX_INTR);
+   outb(0x81, ioaddr + RX_INTR);
+ 
+   EIPVT(ei)->open_time = jiffies;
+ 
+   ei->dev->if_tbusy = 0;
+   ei->interrupt = 0;
+   ei->start = 1;
+ 
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to lance_open(). */
+ static int
+ at1700_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   DPRINTF((DBG_AT1700, "%s: shutting down ethercard\n", ei->dev->if_name));
+ 
+   EIPVT(ei)->open_time = 0;
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   /* Set configuration register 0 to disable Tx and Rx. */
+   outb(0xda, ioaddr + CONFIG_0);
+ 
+   /* Update the statistics -- ToDo. */
+ 
+   /* Power-down the chip. Green, green, green! */
+   outb(0x00, ioaddr + CONFIG_1);
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ at1700_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int ioaddr;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in at1700_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+ 
+   /* Transmitter timeout, serious problems. */
+   if (dev->if_tbusy) {
+ 	/*
+ 	 * If we get here, some higher level has decided we are broken.
+ 	 * There should really be a "kick me" function call instead.
+ 	 */
+ 	int tickssofar = (jiffies - dev->if_txtimer);
+ 
+ 	if (tickssofar < 10) return(1);
+ 	printk("%s: transmit timed out with status 0x%04X, %s?\n",
+ 		dev->if_name, inw(ioaddr + STATUS),
+ 		inb(ioaddr + TX_STATUS) & 0x80
+ 			? "IRQ conflict"
+ 			: "network cable problem");
+ 	printk("%s: timeout registers: %04X %04X %04X %04X %04X %04X %04X %04X\n",
+ 		dev->if_name, inw(ioaddr + 0), inw(ioaddr + 2),
+ 		inw(ioaddr + 4), inw(ioaddr + 6), inw(ioaddr + 8),
+ 		inw(ioaddr + 10), inw(ioaddr + 12), inw(ioaddr + 14));
+ 	dev->if_stats->tx_errors++;
+ 
+ 	/* ToDo: We should try to restart the adaptor... */
+ 	outw(0xffff, ioaddr + 24);
+ 	outw(0xffff, ioaddr + TX_STATUS);
+ 	outw(0xe85a, ioaddr + CONFIG_0);
+ 	outw(0x8100, ioaddr + TX_INTR);
+ 
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+ 	return(0);
+   }
+ 
+   /*
+    * If some higher layer thinks we've missed an tx-done interrupt
+    * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+    * itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   if (skb->len <= 0) return(0);
+ 
+   /*
+    * Block a timer-based transmit from overlapping.  This could
+    * better be done with atomic_swap(1, dev->tbusy), but set_bit()
+    * works as well.
+    */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 		printk("%s: transmitter access conflict\n", dev->if_name);
+     else {
+ 	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+  
+ 	/* Turn off the possible TX interrupts. */
+ 	outb(0x00, ioaddr + TX_INTR);
+ 		
+ 	outw(length, ioaddr + DATAPORT);
+ 	outsw(ioaddr + DATAPORT, skb->raw, (length + 1) >> 1);
+ 
+ 	EIPVT(ei)->tx_queue++;
+ 	EIPVT(ei)->tx_queue_len += (length + 2);
+ 
+ 	if (EIPVT(ei)->tx_started == 0) {
+ 		/* If the Tx is idle, always trigger a transmit. */
+ 		outb(0x80 | EIPVT(ei)->tx_queue, ioaddr + TX_START);
+ 		EIPVT(ei)->tx_queue = 0;
+ 		EIPVT(ei)->tx_queue_len = 0;
+ 		dev->if_txtimer = jiffies;
+ 		EIPVT(ei)->tx_started = 1;
+ 	} else if (EIPVT(ei)->tx_queue_len < 4096 - 1502)
+ 		/* Room for one more packet? */
+ 		dev->if_tbusy = 0;
+ 
+ 	/* Turn on Tx interrupts back on. */
+ 	outb(0x82, ioaddr + TX_INTR);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ at1700_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   if (num_addrs) {
+ 	outw(3, ioaddr + RX_MODE);	/* enable promiscuous mode */
+   } else {
+ 	outw(2, ioaddr + RX_MODE);	/* disable promiscuous, use normal mode */
+   }
+ }
+ 
+ 
+ /*
+  * The Fujitsu datasheet suggests that the NIC be probed for by
+  * checking its "signature", the default bit pattern after a reset.
+  * This *doesn't* work -- there is no way to reset the bus interface
+  * without a complete power-cycle!
+  *
+  * It turns out that ATI came to the same conclusion I did: the only
+  * thing that can be done is checking a few bits and then diving right
+  * into an EEPROM read.
+  */
+ static int
+ at1700_probe1(short ioaddr, struct ei *ei)
+ {
+   unsigned short signature[4] = { 0xffff, 0xffff, 0x7ff7, 0xff5f };
+   unsigned short signature_invalid[4] = { 0xffff, 0xffff, 0x7ff7, 0xdf0f };
+   char irqmap[8] = { 3, 4, 5, 9, 10, 11, 14, 15 };
+   unsigned int i, irq;
+   struct sockaddr sa;
+ 
+   DPRINTF((DBG_AT1700, "%s: AT1700 probe at 0x%3X,",
+ 				ei->dev->if_name, ioaddr));
+ 
+   /*
+    * Resetting the chip doesn't reset the ISA interface, so don't bother.
+    * That means we have to be careful with the register values we probe for.
+    */
+   for (i = 0; i < 4; i++) {
+ 	if ((inw(ioaddr + 2*i) | signature_invalid[i]) != signature[i]) {
+ 		DPRINTF((DBG_AT1700,
+ 			"signature match failed at %d (0x%04X vs. 0x%04X)\n",
+ 					i, inw(ioaddr + 2*i), signature[i]));
+ 		return(-ENODEV);
+ 	}
+   }
+ 
+   if ((read_eeprom(ioaddr, 4) != 0x0000) ||
+       (read_eeprom(ioaddr, 5) & 0x00FF != 0x00F4)) return(-ENODEV);
+ 
+   /*
+    * Grab the region so that we can find another board
+    * if the IRQ request fails.
+    */
+   snarf_region(ioaddr, 32);
+ 
+   irq = irqmap[(read_eeprom(ioaddr, 12)&0x04) | (read_eeprom(ioaddr, 0)>>14)];
+ 
+   /* Snarf the interrupt vector now. */
+   if (request_irq(irq, at1700_interrupt)) {
+ 	DPRINTF((DBG_AT1700,
+ 		"AT1700 found at 0x%3X, but it's unusable due "
+ 		"to a conflict on IRQ%d.\n", ioaddr, irq));
+ 	return(-EAGAIN);
+   }
+ 
+   DPRINTF((DBG_AT1700, "%s: AT1700 found at 0x%3X, IRQ%d, address ",
+ 					ei->dev->if_name, ioaddr, irq));
+ 
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+   ei->irq = irq;
+   at1700_irq2ei_map[irq] = ei;
+ 
+   /* Read the physical address. */
+   {
+ 	unsigned short *ptr = (unsigned short *) sa.sa_data;
+ 
+ 	for(i = 0; i < 3; i++) {
+ 		unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
+ 
+ 		*ptr++ = eeprom_val;
+ 		DPRINTF((DBG_AT1700, " %04.4X", eeprom_val));
+ 	}
+   }
+ 
+   /*
+    * The EEPROM word 12 bit 0x0400 means use regular 100 ohm
+    * 10baseT signals, rather than 150 ohm shielded twisted pair
+    * compensation.
+    * 0x0000 == auto-sense the interface
+    * 0x0800 == use TP interface
+    * 0x1800 == use coax interface
+    */
+   {
+ #ifdef BECKER_DEBUG
+ 	char *porttype[] = { "auto-sense", "10baseT", "auto-sense", "10base2" };
+ #endif
+ 	ushort setup_value = read_eeprom(ioaddr, 12);
+ 
+ 	ei->interface_num = setup_value >> 8;
+ 	DPRINTF((DBG_AT1700, " %s interface (%04X).\n",
+ 			porttype[(ei->interface_num >> 3) & 3], setup_value));
+   }
+ 
+   /* Set the station address in bank zero. */
+   outb(0xe0, ioaddr + 7);
+   for (i = 0; i < ETH_ALEN; i++)
+ 	outb(sa.sa_data[i], ioaddr + 8 + i);
+ 
+   /* Switch to bank 1 and set the multicast table to accept none. */
+   outb(0xe4, ioaddr + 7);
+   for (i = 0; i < 8; i++)
+ 	outb(0x00, ioaddr + 8 + i);
+ 
+   /*
+    * Set the configuration register 0 to 32K 100ns. byte-wide
+    * memory, 16 bit bus access, two 4K Tx queues, and disabled
+    * Tx and Rx.
+    */
+   outb(0xda, ioaddr + CONFIG_0);
+ 
+   /* Switch to bank 2 and lock our I/O address. */
+   outb(0xe8, ioaddr + 7);
+   outb(ei->interface_num, MODE13);
+ 
+   /* Power-down the chip.  Aren't we green! */
+   outb(0x00, ioaddr + CONFIG_1);
+ 
+   /* Allocate a control block */
+   EIPVT(ei) = (struct at1700 *) kmalloc(sizeof(struct at1700), GFP_KERNEL);
+   if (EIPVT(ei) == NULL) {
+ 	DPRINTF((DBG_AT1700, "No memory for AT1700 control block !\n"));
+ 	return(-ENOMEM);
+   }
+   memset(EIPVT(ei), 0, sizeof(struct at1700));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = at1700_xmit;
+   ei->chip_s_multi = at1700_set_multicast;
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = NULL;
+   ei->card_open = at1700_open;
+   ei->card_close = at1700_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /* Check for a network adaptor of this type, and return '0' iff one exists. */
+ int
+ at1700_probe(struct ei *ei)
+ {
+   static int at1700_card_nr = 0;
+   short *port;
+   short ports[] = { 0x300, 0x280, 0x380, 0x320, 0x340, 0x260, 0x2a0, 0x240, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "AT1700";
+   sprintf(ei->dev->if_name, "at%d", at1700_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(at1700_probe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	int ioaddr = *port;
+ 
+ 	if (check_region(ioaddr, 32)) continue;
+ 
+ 	if (at1700_probe1(ioaddr, ei) == 0) return(0);
+   }
+   return(-ENODEV);
+ }
*** original/net/drv/becker/atp.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/atp.c	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,1033 ----
+ /* atp.c: Attached (pocket) ethernet adaptor driver for linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 	Copyright 1993 United States Government as represented by the Director,
+ 	National Security Agency.  This software may only be used and distributed
+ 	according to the terms of the GNU Public License as modified by SRC,
+ 	incorported herein by reference.
+ 
+ 	The author may be reached as becker@super.org or
+ 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+ */
+ 
+ /*
+ 	This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
+ 	ethernet adaptor.  This is a common low-cost OEM pocket ethernet
+ 	adaptor, sold under many names.
+ 
+   Sources:
+ 	This driver was written from the packet driver assembly code provided by
+ 	Vincent Bono of AT-Lan-Tec.	 Ever try to figure out how a complicated
+ 	device works just from the assembly code?  It ain't pretty.  The following
+ 	description is written based on guesses and writing lots of special-purpose
+ 	code to test my theorized operation.
+ 
+ 					Theory of Operation
+ 	
+ 	The RTL8002 adaptor seems to be built around a custom spin of the SEEQ
+ 	controller core.  It probably has a 16K or 64K internal packet buffer, of
+ 	which the first 4K is devoted to transmit and the rest to receive.
+ 	The controller maintains the queue of received packet and the packet buffer
+ 	access pointer internally, with only 'reset to beginning' and 'skip to next
+ 	packet' commands visible.  The transmit packet queue holds two (or more?)
+ 	packets: both 'retransmit this packet' (due to collision) and 'transmit next
+ 	packet' commands must be started by hand.
+ 
+ 	The station address is stored in a standard bit-serial EEPROM which must be
+ 	read (ughh) by the device driver.  (Provisions have been made for
+ 	substituting a 74S288 PROM, but I haven't gotten reports of any models
+ 	using it.)  Unlike built-in devices, a pocket adaptor can temporarily lose
+ 	power without indication to the device driver.  The major effect is that
+ 	the station address, receive filter (promiscuous, etc.) and transceiver
+ 	must be reset.
+ 
+ 	The controller itself has 16 registers, some of which use only the lower
+ 	bits.  The registers are read and written 4 bits at a time.  The four bit
+ 	register address is presented on the data lines along with a few additional
+ 	timing and control bits.  The data is then read from status port or written
+ 	to the data port.
+ 
+ 	Since the bulk data transfer of the actual packets through the slow
+ 	parallel port dominates the driver's running time, four distinct data
+ 	(non-register) transfer modes are provided by the adaptor, two in each
+ 	direction.  In the first mode timing for the nibble transfers is
+ 	provided through the data port.  In the second mode the same timing is
+ 	provided through the control port.  In either case the data is read from
+ 	the status port and written to the data port, just as it is accessing
+ 	registers.
+ 
+ 	In addition to the basic data transfer methods, several more are modes are
+ 	created by adding some delay by doing multiple reads of the data to allow
+ 	it to stabilize.  This delay seems to be needed on most machines.
+ 
+ 	The data transfer mode is stored in the 'ei->interface_num' field.
+ 	Its default value is '4'.  It may be overriden at boot-time using the
+ 	third parameter to the "ether=..." initialization.
+ 
+ 	The header file <atp.h> provides inline functions that encapsulate the
+ 	register and data access methods.  These functions are hand-tuned to
+ 	generate reasonable object code.  This header file also documents my
+ 	interpretations of the device registers.
+ */
+ 
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/fcntl.h>
+ #include <linux/malloc.h>
+ #include <linux/string.h>
+ #include <linux/interrupt.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include <errno.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"atp.c:v0.04 2/5/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ struct rx_header {
+   ushort pad;			/* The first read is always corrupted. */
+   ushort rx_count;
+   ushort rx_status;		/* Unknown bit assignments :-<.  */
+   ushort cur_addr;		/* Apparently the current buffer address(?) */
+ };
+ 
+ #define PAR_DATA	0
+ #define PAR_STATUS	1
+ #define PAR_CONTROL	2
+ 
+ #define Ctrl_LNibRead	0x08	/* LP_PSELECP */
+ #define Ctrl_HNibRead	0
+ #define Ctrl_LNibWrite	0x08	/* LP_PSELECP */
+ #define Ctrl_HNibWrite	0
+ #define Ctrl_SelData	0x04	/* LP_PINITP */
+ #define Ctrl_IRQEN	0x10	/* LP_PINTEN */
+ 
+ #define EOW		0xE0
+ #define EOC		0xE0
+ #define WrAddr		0x40	/* Set address of EPLC read, write register. */
+ #define RdAddr		0xC0
+ #define HNib		0x10
+ 
+ enum page0_regs {
+   /* The first six registers hold the ethernet physical station address. */
+   PAR0 = 0, PAR1 = 1,
+   PAR2 = 2, PAR3 = 3,
+   PAR4 = 4, PAR5 = 5,
+   TxCNT0 = 6, TxCNT1 = 7,		/* The transmit byte count. */
+   TxSTAT = 8, RxSTAT = 9,		/* Tx and Rx status. */
+   ISR = 10, IMR = 11,			/* Interrupt status and mask. */
+   CMR1 = 12,				/* Command register 1. */
+   CMR2 = 13,				/* Command register 2. */
+   MAR = 14,				/* Memory address register. */
+   CMR2_h = 0x1D
+ };
+ 
+ enum eepage_regs {
+   PROM_CMD = 6,
+   PROM_DATA = 7
+ };				/* Note that PROM_CMD is in the "high" bits. */
+ 
+ 
+ #define ISR_TxOK	0x01
+ #define ISR_RxOK	0x04
+ #define ISR_TxErr	0x02
+ #define ISRh_RxErr	0x11	/* ISR, high nibble */
+ 
+ #define CMR1h_RESET	0x04	/* Reset. */
+ #define CMR1h_RxENABLE	0x02	/* Rx unit enable.  */
+ #define CMR1h_TxENABLE	0x01	/* Tx unit enable.  */
+ #define CMR1h_TxRxOFF	0x00
+ #define CMR1_ReXmit	0x08	/* Trigger a retransmit. */
+ #define CMR1_Xmit	0x04	/* Trigger a transmit. */
+ #define	CMR1_IRQ	0x02	/* Interrupt active. */
+ #define	CMR1_BufEnb	0x01	/* Enable the buffer(?). */
+ #define	CMR1_NextPkt	0x01	/* Enable the buffer(?). */
+ 
+ #define CMR2_NULL	8
+ #define CMR2_IRQOUT	9
+ #define CMR2_RAMTEST	10
+ #define CMR2_EEPROM	12	/* Set to page 1, for reading the EEPROM. */
+ 
+ #define CMR2h_OFF	0	/* No accept mode. */
+ #define CMR2h_Physical	1	/* Accept a physical address match only. */
+ #define CMR2h_Normal	2	/* Accept physical and broadcast address. */
+ #define CMR2h_PROMISC	3	/* Promiscuous mode. */
+ 
+ 
+ /* The number of low I/O ports used by the ethercard. */
+ #define ETHERCARD_TOTAL_SIZE	3
+ 
+ 
+ static struct ei *atp_irq2ei_map[16] = { NULL };
+ 
+ 
+ /*
+  * An inline function used below: it differs from inb() by
+  * explicitly return an unsigned char, saving a truncation.
+  */
+ static inline unsigned char
+ inbyte(unsigned short port)
+ {
+   unsigned char _v;
+ 
+   __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
+   return(_v);
+ }
+ 
+ 
+ /*
+  * Read register OFFSET.
+  * This command should aways be terminated with read_end().
+  */
+ static inline unsigned char
+ read_nibble(short port, unsigned char offset)
+ {
+   unsigned char retval;
+ 
+   outb(EOC+offset, port + PAR_DATA);
+   outb(RdAddr+offset, port + PAR_DATA);
+   inbyte(port + PAR_STATUS);		/* Settling time delay */
+   retval = inbyte(port + PAR_STATUS);
+   outb(EOC+offset, port + PAR_DATA);
+   return(retval);
+ }
+ 
+ 
+ /*
+  * Functions for bulk data read.  The interrupt line is always disabled.
+  * Get a byte using read mode 0, reading data from the control lines.
+  */
+ static inline unsigned char
+ read_byte_mode0(short ioaddr)
+ {
+   unsigned char low_nib;
+ 
+   outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+   inbyte(ioaddr + PAR_STATUS);
+   low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+   outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+   inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */
+   inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */
+   return(low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0));
+ }
+ 
+ 
+ /* The same as read_byte_mode0(), but does multiple inb()s for stability. */
+ static inline unsigned char
+ read_byte_mode2(short ioaddr)
+ {
+   unsigned char low_nib;
+ 
+   outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+   inbyte(ioaddr + PAR_STATUS);
+   low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+   outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+   inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */
+   return(low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0));
+ }
+ 
+ 
+ /* Read a byte through the data register. */
+ static inline unsigned char
+ read_byte_mode4(short ioaddr)
+ {
+   unsigned char low_nib;
+ 
+   outb(RdAddr | MAR, ioaddr + PAR_DATA);
+   low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+   outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+   return(low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0));
+ }
+ 
+ 
+ /* Read a byte through the data register, double reading to allow settling. */
+ static inline unsigned char
+ read_byte_mode6(short ioaddr)
+ {
+   unsigned char low_nib;
+ 
+   outb(RdAddr | MAR, ioaddr + PAR_DATA);
+   inbyte(ioaddr + PAR_STATUS);
+   low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+   outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+   inbyte(ioaddr + PAR_STATUS);
+   return(low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0));
+ }
+ 
+ 
+ static inline void
+ write_reg(short port, unsigned char reg, unsigned char value)
+ {
+   unsigned char outval;
+ 
+   outb(EOC | reg, port + PAR_DATA);
+   outval = WrAddr | reg;
+   outb(outval, port + PAR_DATA);
+   outb(outval, port + PAR_DATA);	/* Double write for PS/2. */
+ 
+   outval &= 0xf0;
+   outval |= value;
+   outb(outval, port + PAR_DATA);
+   outval &= 0x1f;
+   outb(outval, port + PAR_DATA);
+   outb(outval, port + PAR_DATA);
+ 
+   outb(EOC | outval, port + PAR_DATA);
+ }
+ 
+ 
+ static inline void
+ write_reg_high(short port, unsigned char reg, unsigned char value)
+ {
+   unsigned char outval = EOC | HNib | reg;
+ 
+   outb(outval, port + PAR_DATA);
+   outval &= WrAddr | HNib | 0x0f;
+   outb(outval, port + PAR_DATA);
+   outb(outval, port + PAR_DATA);	/* Double write for PS/2. */
+ 
+   outval = WrAddr | HNib | value;
+   outb(outval, port + PAR_DATA);
+   outval &= HNib | 0x0f;		/* HNib | value */
+   outb(outval, port + PAR_DATA);
+   outb(outval, port + PAR_DATA);
+ 
+   outb(EOC | HNib | outval, port + PAR_DATA);
+ }
+ 
+ 
+ /* Write a byte out using nibble mode.  The low nibble is written first. */
+ static inline void
+ write_reg_byte(short port, unsigned char reg, unsigned char value)
+ {
+   unsigned char outval;
+ 
+   outb(EOC | reg, port + PAR_DATA); 	/* Reset the address register. */
+   outval = WrAddr | reg;
+   outb(outval, port + PAR_DATA);
+   outb(outval, port + PAR_DATA);	/* Double write for PS/2. */
+ 
+   outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
+   outb(value & 0x0f, port + PAR_DATA);
+   value >>= 4;
+   outb(value, port + PAR_DATA);
+   outb(0x10 | value, port + PAR_DATA);
+   outb(0x10 | value, port + PAR_DATA);
+ 
+   outb(EOC  | value, port + PAR_DATA); 	/* Reset the address register. */
+ }
+ 
+ 
+ /*
+  * Bulk data writes to the packet buffer.  The interrupt line remains enabled.
+  * The first, faster method uses only the dataport (data modes 0, 2 & 4).
+  * The second (backup) method uses data and control regs (modes 1, 3 & 5).
+  * It should only be needed when there is skew between the individual data
+  * lines.
+  */
+ static inline void
+ write_byte_mode0(short ioaddr, unsigned char value)
+ {
+   outb(value & 0x0f, ioaddr + PAR_DATA);
+   outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+ }
+ 
+ 
+ static inline void
+ write_byte_mode1(short ioaddr, unsigned char value)
+ {
+   outb(value & 0x0f, ioaddr + PAR_DATA);
+   outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
+   outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+   outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
+ }
+ 
+ 
+ /* Write 16bit VALUE to the packet buffer: the same as above just doubled. */
+ static inline void
+ write_word_mode0(short ioaddr, unsigned short value)
+ {
+   outb(value & 0x0f, ioaddr + PAR_DATA);
+   value >>= 4;
+   outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+   value >>= 4;
+   outb(value & 0x0f, ioaddr + PAR_DATA);
+   value >>= 4;
+   outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+ }
+ 
+ 
+ /*  EEPROM_Ctrl bits. */
+ #define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */
+ #define EE_CS		0x02	/* EEPROM chip select. */
+ #define EE_CLK_HIGH	0x12
+ #define EE_CLK_LOW	0x16
+ #define EE_DATA_WRITE	0x01	/* EEPROM chip data in. */
+ #define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+ 
+ /* Delay between EEPROM clock transitions. */
+ #define eeprom_delay(ticks) \
+ 	do { \
+ 		int _i = 40; \
+ 		while (--_i > 0) { \
+ 			__SLOW_DOWN_IO; \
+ 		} \
+ 	} while (0)
+ 
+ /* The EEPROM commands include the alway-set leading bit. */
+ #define EE_WRITE_CMD(offset)	(((5 << 6) + (offset)) << 17)
+ #define EE_READ(offset) 	(((6 << 6) + (offset)) << 17)
+ #define EE_ERASE(offset)	(((7 << 6) + (offset)) << 17)
+ #define EE_CMD_SIZE	27	/* The command+address+data size. */
+ 
+ 
+ /*
+  * An EEPROM read command starts by shifting out 0x60+address,
+  * and then shifting in the serial data. See the NatSemi databook
+  * for details.
+  *		   ________________
+  * CS : __|
+  *			   ___	   ___
+  * CLK: ______|	  |___|	  |
+  *		 __ _______ _______
+  * DI :	 __X_______X_______X
+  * DO :	 _________X_______X
+  */
+ static unsigned short
+ eeprom_op(short ioaddr, unsigned int cmd)
+ {
+   unsigned eedata_out = 0;
+   int num_bits = EE_CMD_SIZE;
+ 	
+   while (--num_bits >= 0) {
+ 	char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
+ 
+ 	write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
+ 	eeprom_delay(5);
+ 	write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
+ 	eedata_out <<= 1;
+ 	if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ)
+ 		eedata_out++;
+ 	eeprom_delay(5);
+   }
+   write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS);
+   return(eedata_out);
+ }
+ 
+ 
+ /*
+  * This routine resets the hardware.  We initialize everything,
+  * assuming that the hardware may have been temporarily detached.
+  */
+ static void
+ atp_card_init(struct ei *ei)
+ {
+   unsigned char *ptr;
+   int ioaddr = ei->ioaddr;
+   int i;
+ 
+   write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ 	
+   ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+   for(i = 0; i < ETH_ALEN; i++) {
+ 	write_reg_byte(ioaddr, PAR0 + i, ptr[i]);
+   }
+ 
+   write_reg_high(ioaddr, CMR2, ei->addr_mode);
+ 
+   DPRINTF((DBG_ATP, "%s: reset: current RX mode %d\n", ei->dev->if_name,
+ 			(read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f));
+ 
+   write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+   write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+ 
+   /* Enable the interrupt line from the serial port. */
+   outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ 
+   /* Unmask the interesting interrupts. */
+   write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+   write_reg_high(ioaddr, IMR, ISRh_RxErr);
+ 
+   ei->tx_unit_busy = 0;
+   ei->pac_cnt_in_tx_buf = 0;
+   ei->saved_tx_size = 0;
+ 
+   ei->dev->if_tbusy = 0;
+   ei->interrupt = 0;
+ }
+ 
+ 
+ static void
+ trigger_send(short ioaddr, int length)
+ {
+   write_reg_byte(ioaddr, TxCNT0, length & 0xff);
+   write_reg(ioaddr, TxCNT1, length >> 8);
+   write_reg(ioaddr, CMR1, CMR1_Xmit);
+ }
+ 
+ 
+ static void
+ write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
+ {
+   length = (length + 1) & ~1;		/* round up to word length */
+   outb(EOC+MAR, ioaddr + PAR_DATA);
+   if ((data_mode & 1) == 0) {
+ 	/* Write the packet out, starting with the write addr. */
+ 	outb(WrAddr+MAR, ioaddr + PAR_DATA);
+ 	do {
+ 		write_byte_mode0(ioaddr, *packet++);
+ 	} while(--length > 0);
+   } else {
+ 	/* Write the packet out in slow mode. */
+ 	unsigned char outbyte = *packet++;
+ 
+ 	outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ 	outb(WrAddr+MAR, ioaddr + PAR_DATA);
+ 
+ 	outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA);
+ 	outb(outbyte & 0x0f, ioaddr + PAR_DATA);
+ 	outbyte >>= 4;
+ 	outb(outbyte & 0x0f, ioaddr + PAR_DATA);
+ 	outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ 	while (--length > 0)
+ 		write_byte_mode1(ioaddr, *packet++);
+   }
+ 
+   /* Terminate the TX frame. End of write: ECB. */
+   outb(0xff, ioaddr + PAR_DATA);
+   outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ }
+ 
+ 
+ static void
+ read_packet(short ioaddr, int length, unsigned char *p, int data_mode)
+ {
+   if (data_mode <= 3) { /* mode 0 or 1 */
+ 	outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ 	outb(length == 8  ?  RdAddr | HNib | MAR  :  RdAddr | MAR,
+ 		 ioaddr + PAR_DATA);
+ 	if (data_mode <= 1) { /* mode 0 or 1 */
+ 		do  *p++ = read_byte_mode0(ioaddr);  while (--length > 0);
+ 	} else	/* Mode 2 or 3 */
+ 		do  *p++ = read_byte_mode2(ioaddr);  while (--length > 0);
+   } else if (data_mode <= 5)
+ 	do      *p++ = read_byte_mode4(ioaddr);  while (--length > 0);
+     else
+ 	do      *p++ = read_byte_mode6(ioaddr);  while (--length > 0);
+ 
+   outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+   outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
+ }
+ 
+ 
+ /* We have a good packet(s), get it/them out of the buffers. */
+ static void
+ atp_receive(struct ei *ei)
+ {
+   struct rx_header rx_head;
+   int ioaddr = ei->ioaddr;
+ 
+   /* Process the received packet. */
+   outb(EOC+MAR, ioaddr + PAR_DATA);
+   read_packet(ioaddr, 8, (unsigned char*)&rx_head, ei->interface_num);
+ 
+   DPRINTF((DBG_ATP, " rx_count 0x%04X 0x%04X 0x%04X 0x%04X",
+ 	rx_head.pad, rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr));
+ 
+   if ((rx_head.rx_status & 0x77) != 0x01) {
+ 	ei->dev->if_stats->rx_errors++;
+ 
+ 	/*
+ 	 * Ackkk!  I don't have any documentation on what the error
+ 	 * bits mean!  The best I can do is slap the device around
+ 	 * a bit.
+ 	 */
+ 	DPRINTF((DBG_ATP, "%s: unknown ATP RX error 0x%04X\n",
+ 			ei->dev->if_name, rx_head.rx_status));
+ 	atp_card_init(ei);
+ 	return;
+   } else {
+ 	/* Malloc up new buffer. */
+ 	int pkt_len = (rx_head.rx_count & 0x7ff) - 4;
+ 			/* the "-4" omits the FCS (CRC) */
+ 	struct sk_buff *skb;
+ 		
+ 	skb = skb_create(pkt_len);
+ 	if (skb == NULL) {
+ 		printk("%s: memory squeeze, dropping packet\n",
+ 						ei->dev->if_name);
+ 		ei->dev->if_stats->rx_dropped++;
+ 		goto done;
+ 	}
+ 	skb->len = pkt_len;
+ 	skb->dev = ei->dev;
+ 		
+ 	/* 'skb->raw' points to the start of sk_buff data area. */
+ 	read_packet(ioaddr, pkt_len, skb->raw, ei->interface_num);
+ 
+ 	if (dev_rint(ei->dev, skb, pkt_len) != 0) {
+ 		ei->dev->if_stats->rx_dropped++;
+ 	} else {
+ 		ei->dev->if_stats->rx_packets++;
+ 	}
+   }
+ done:
+   write_reg(ioaddr, CMR1, CMR1_NextPkt);
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the network interface interrupts.
+  */
+ static void
+ atp_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = atp_irq2ei_map[irq];
+   int ioaddr, status, boguscount = 20;
+   static int num_tx_since_rx = 0;
+ 
+   if (ei == NULL) {
+ 	printk("ATP: interrupt: IRQ%d for unknown device\n", irq);
+ 	return;
+   }
+   ei->interrupt = 1;
+   ioaddr = ei->ioaddr;
+ 
+   /* Disable additional spurious interrupts. */
+   outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
+ 
+   /* The adaptor's output is currently the IRQ line, switch it to data. */
+   write_reg(ioaddr, CMR2, CMR2_NULL);
+   write_reg(ioaddr, IMR, 0);
+ 
+   DPRINTF((DBG_ATP, "%s: in interrupt ", ei->dev->if_name));
+   while (--boguscount > 0) {
+ 	status = read_nibble(ioaddr, ISR);
+ 	DPRINTF((DBG_ATP, "loop status 0x%02X..", status));
+ 
+ 	if (status & (ISR_RxOK<<3)) {
+ 		write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
+ 		do {
+ 			int read_status = read_nibble(ioaddr, CMR1);
+ 
+ 			DPRINTF((DBG_ATP,
+ 				"handling RX packet 0x%02X..", read_status));
+ 
+ 			/*
+ 			 * We acknowledged the normal Rx interrupt, so if
+ 			 * the interrupt is still outstanding we must have
+ 			 * a Rx error.
+ 			 */
+ 			if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */
+ 				ei->dev->if_stats->rx_overrun++;
+ 
+ 				/*
+ 				 * Set to no-accept mode long enough to
+ 				 * remove a packet.
+ 				 */
+ 				write_reg_high(ioaddr, CMR2, CMR2h_OFF);
+ 				atp_receive(ei);
+ 
+ 				/*
+ 				 * Clear the interrupt and return to normal
+ 				 * Rx mode.
+ 				 */
+ 				write_reg_high(ioaddr, ISR, ISRh_RxErr);
+ 				write_reg_high(ioaddr, CMR2, ei->addr_mode);
+ 			} else if ((read_status & (CMR1_BufEnb << 3)) == 0) {
+ 				atp_receive(ei);
+ 				ei->dev->if_rxtimer = jiffies;
+ 				num_tx_since_rx = 0;
+ 			} else {
+ 				break;
+ 			}
+ 		} while (--boguscount > 0);
+ 	} else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
+ 		DPRINTF((DBG_ATP, "handling TX done.."));
+ 
+ 		/*
+ 		 * Clear the Tx interrupt.  We should check for too many
+ 		 * failures and reinitialize the adaptor.
+ 		 */
+ 		write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK);
+ 		if (status & (ISR_TxErr<<3)) {
+ 			ei->dev->if_stats->tx_collision++;
+ 			if (++ei->re_tx > 15) {
+ 				ei->dev->if_stats->tx_aborted++;
+ 				atp_card_init(ei);
+ 				break;
+ 			}
+ 
+ 			/* Attempt to retransmit. */
+ 			DPRINTF((DBG_ATP, "attempting to ReTX"));
+ 			write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
+ 		} else {
+ 			/* Finish up the transmit. */
+ 			ei->dev->if_stats->tx_packets++;
+ 			ei->pac_cnt_in_tx_buf--;
+ 			if (ei->saved_tx_size) {
+ 				trigger_send(ioaddr, ei->saved_tx_size);
+ 				ei->saved_tx_size = 0;
+ 				ei->re_tx = 0;
+ 			} else {
+ 				ei->tx_unit_busy = 0;
+ 			}
+ 			ei->dev->if_tbusy = 0;
+ 			mark_bh(NET_BH);
+ 		}
+ 		num_tx_since_rx++;
+ 	} else if (num_tx_since_rx > 8 && jiffies > ei->dev->if_rxtimer + 100) {
+ 		DPRINTF((DBG_ATP,
+ 			"%s: missed packet? No RX after %d TX and %ld jiffies"
+ 			" status 0x%02X  CMR1 0x%02X\n", ei->dev->if_name,
+ 				num_tx_since_rx, jiffies - ei->dev->if_rxtimer,
+ 				status,
+ 				(read_nibble(ioaddr, CMR1) >> 3) & 15));
+ 			ei->dev->if_stats->rx_missed++;
+ 			atp_card_init(ei);
+ 			num_tx_since_rx = 0;
+ 			break;
+ 	} else {
+ 		break;
+ 	}
+   }
+ 
+   /*
+    * Re-write the ATP HW address to the card.
+    * This fixes a rare (and difficult to track down)
+    * problem where the ATP forgets its ethernet address.
+    */
+   {
+     unsigned char *ptr;
+     int i;
+ 
+     ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+     for(i = 0; i < ETH_ALEN; i++) {
+ 	write_reg_byte(ioaddr, PAR0 + i, ptr[i]);
+     }
+   }
+ 
+   /* Tell the adaptor that it can go back to using the output line as IRQ. */
+   write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ 
+   /*
+    * Enable the physical interrupt line, which is sure to
+    * be low until..
+    */
+   outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ 
+   /* .. we enable the interrupt sources. */
+   write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+   write_reg_high(ioaddr, IMR, ISRh_RxErr);  /* hmmm, really needed? */
+ 
+   DPRINTF((DBG_ATP, "exiting interrupt\n"));
+   ei->interrupt = 0;
+ }
+ 
+ 
+ /*
+  * Open/initialize the board.
+  * This is called (in the current kernel) sometime after booting
+  * when the 'ifconfig' program is run.
+  *
+  * This routine sets everything up anew at each open, even
+  * registers that "should" only need to be set once at boot, so that
+  * there is non-reboot way to recover if something goes wrong.
+  *
+  * This is an attachable device: if there is no dev->priv entry then
+  * it wasn't probed for at boot-time, and we need to probe for it again.
+  */
+ static int
+ atp_open(struct ei *ei)
+ {
+   /*
+    * The interrupt line is turned off (tri-stated) when the device
+    * isn't in use.  That's especially important for "attached"
+    * interfaces where the port or interrupt may be shared.
+    */
+   if ((atp_irq2ei_map[ei->irq] != 0) ||
+       ((atp_irq2ei_map[ei->irq] = ei) == NULL) ||
+       request_irq(ei->irq, atp_interrupt)) return(-EAGAIN);
+ 
+   atp_card_init(ei);
+   ei->start = 1;
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to atp_open(). */
+ static int
+ atp_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   /* Flush the Tx and disable Rx here. */
+   ei->addr_mode = CMR2h_OFF;
+   write_reg_high(ioaddr, CMR2, CMR2h_OFF);
+ 
+   /* Free the IRQ line. */
+   outb(0x00, ioaddr + PAR_CONTROL);
+   free_irq(ei->irq);
+   atp_irq2ei_map[ei->irq] = NULL;
+ 
+   /* Leave the hardware in a reset state. */
+   write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ atp_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int ioaddr;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in atp_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+ 
+   if (dev->if_tbusy) {
+ 	/*
+ 	 * If we get here, some higher level has decided we are broken.
+ 	 * There should really be a "kick me" function call instead.
+ 	 */
+ 	int tickssofar = (jiffies - dev->if_txtimer);
+ 
+ 	if (tickssofar < 5) return(1);
+ 	printk("%s: transmit timed out, %s?\n",
+ 		dev->if_name, inb(ioaddr + PAR_CONTROL) & 0x10
+ 			? "network cable problem"
+ 			: "IRQ conflict");
+ 	dev->if_stats->tx_errors++;
+ 
+ 	/* Try to restart the adaptor. */
+ 	atp_card_init(ei);
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+   }
+ 
+   /*
+    * If some higher layer thinks we've missed an tx-done interrupt
+    * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+    * itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   /*
+    * Block a timer-based transmit from overlapping.  This could
+    * better be done with atomic_swap(1, dev->tbusy), but set_bit()
+    * works as well.
+    */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 		printk("%s: transmitter access conflict\n", dev->if_name);
+     else {
+ 	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ 	unsigned char *buf = skb->raw;
+ 	int flags;
+ 
+ 	/*
+ 	 * Disable interrupts by writing 0x00 to the Interrupt Mask
+ 	 * Register.  This sequence must not be interrupted by an
+ 	 * incoming packet.
+ 	 */
+ 	save_flags(flags);
+ 	cli();
+ 	write_reg(ioaddr, IMR, 0);
+ 	write_reg_high(ioaddr, IMR, 0);
+ 	restore_flags(flags);
+ 
+ 	write_packet(ioaddr, length, buf, ei->interface_num);
+ 
+ 	ei->pac_cnt_in_tx_buf++;
+ 	if (ei->tx_unit_busy == 0) {
+ 		trigger_send(ioaddr, length);
+ 		ei->saved_tx_size = 0; 		/* redundant */
+ 		ei->re_tx = 0;
+ 		ei->tx_unit_busy = 1;
+ 	} else {
+ 		ei->saved_tx_size = length;
+ 	}
+ 	dev->if_txtimer = jiffies;
+ 
+ 	/* Re-enable the LPT interrupts. */
+ 	write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ 	write_reg_high(ioaddr, IMR, ISRh_RxErr);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ atp_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   ei->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+   write_reg_high(ioaddr, CMR2, ei->addr_mode);
+ }
+ 
+ 
+ /* Read the station address PROM, usually a word-wide EEPROM. */
+ static void
+ get_node_ID(struct ei *ei, struct sockaddr *sap)
+ {
+   short ioaddr = ei->ioaddr;
+   int sa_offset = 0;
+   int i;
+ 	
+   /* Point to the EEPROM control registers. */
+   write_reg(ioaddr, CMR2, CMR2_EEPROM);
+ 	
+   /*
+    * Some adaptors have the station address at offset 15 instead
+    * of offset zero.  Check for it, and fix it if needed.
+    */
+   if (eeprom_op(ioaddr, EE_READ(0)) == 0xFFFF) sa_offset = 15;
+ 	
+   for (i = 0; i < 3; i++) {
+ 	(*((unsigned short *) &sap->sa_data[i])) =
+ 		ntohs(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
+   }
+ 
+   write_reg(ioaddr, CMR2, CMR2_NULL);
+ }
+ 
+ 
+ static int
+ atp_probe1(short ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int saved_ctrl_reg, status;
+ 
+   DPRINTF((DBG_ATP, "%s: ATP probe at 0x%3X,",
+ 			ei->dev->if_name, ioaddr));
+ 
+   outb(0xff, ioaddr + PAR_DATA);
+ 
+   /*
+    * Save the original value of the Control register,
+    * in case we guessed wrong.
+    */
+   saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
+ 
+   /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
+   outb(0x04, ioaddr + PAR_CONTROL);
+   write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+   eeprom_delay(2048);
+   status = read_nibble(ioaddr, CMR1);
+ 
+   if ((status & 0x78) != 0x08) {
+ 	/* The pocket adaptor probe failed, restore the control register. */
+ 	outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
+ 	return(-ENODEV);
+   }
+   status = read_nibble(ioaddr, CMR2_h);
+   if ((status & 0x78) != 0x10) {
+ 	outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
+ 	return(-ENODEV);
+   }
+ 
+   /* Find the IRQ used by triggering an interrupt. */
+   write_reg_byte(ioaddr, CMR2, 0x01);		/* no accept mode, IRQ out */
+   write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+ 						/* enable TX and RX */
+ 
+   /* Omit autoIRQ routine for now. Use "table lookup" instead.  Uhgggh. */
+   if (ioaddr == 0x378) ei->irq = 7;
+     else ei->irq = 5;
+   write_reg_high(ioaddr, CMR1, CMR1h_TxRxOFF); /* diable TX and RX units */
+   write_reg(ioaddr, CMR2, CMR2_NULL);
+ 
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+   ei->addr_mode = CMR2h_Normal;
+ 
+   /* Read the station address PROM. */
+   get_node_ID(ei, &sa);
+ 
+   /* Leave the hardware in a reset state. */
+   write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = atp_xmit;
+   ei->chip_s_multi = atp_set_multicast;
+ 
+   DPRINTF((DBG_ATP, "%s: ATP at 0x%3X, using IRQ%d\n",
+ 			ei->dev->if_name, ei->ioaddr, ei->irq));
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = atp_card_init;
+   ei->card_open = atp_open;
+   ei->card_close = atp_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /* Check for a network adaptor of this type, and return '0' iff one exists. */
+ int
+ atp_probe(struct ei *ei)
+ {
+   static int atp_card_nr = 0;
+   int *port, ports[] = { 0x378, 0x278, 0x3bc, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "ATP";
+   sprintf(ei->dev->if_name, "atp%d", atp_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(atp_probe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	short ioaddr = *port;
+ 	outb(0x57, ioaddr + PAR_DATA);
+ 	if (inb(ioaddr + PAR_DATA) != 0x57) continue;
+ 	if (atp_probe1(ioaddr, ei) == 0) return(0);
+   }
+   return(-ENODEV);
+ }
*** original/net/drv/becker/auto_irq.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/auto_irq.c	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,112 ----
+ /* auto_irq.c: Auto-configure IRQ lines for linux. */
+ /*
+     Written 1993 by Donald Becker.
+ 
+     The Author may be reached as becker@super.org or
+     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+     This code is a general-purpose IRQ line detector for devices with
+     jumpered IRQ lines.  If you can make the device raise an IRQ (and
+     that IRQ line isn't already being used), these routines will tell
+     you what IRQ line it's using -- perfect for those oh-so-cool boot-time
+     device probes!
+ 
+     To use this, first call autoirq_setup(timeout). TIMEOUT is how many
+     'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
+     and can usually be zero at boot.  'autoirq_setup()' returns the bit
+     vector of nominally-available IRQ lines (lines may be physically in-use,
+     but not yet registered to a device).
+     Next, set up your device to trigger an interrupt.
+     Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
+     most recently active.  The TIMEOUT should usually be zero, but may
+     be set to the number of jiffies to wait for a slow device to raise an IRQ.
+ 
+     The idea of using the setup timeout to filter out bogus IRQs came from
+     the serial driver.
+ */
+ 
+ 
+ #ifdef version
+ static char *version="auto_irq.c:v0.02 1993 Donald Becker (becker@super.org)";
+ #endif
+ 
+ /*#include <linux/config.h>*/
+ /*#include <linux/kernel.h>*/
+ #include <linux/sched.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ /*#include <asm/system.h>*/
+ 
+ int irqs_busy = 0x01;		/* The set of fixed IRQs always enabled */
+ int irqs_used = 0x01;		/* The set of fixed IRQs sometimes enabled. */
+ int irqs_reserved = 0x00;	/* An advisory "reserved" table. */
+ int irqs_shared = 0x00;		/* IRQ lines "shared" among conforming cards.*/
+ 
+ static volatile int irq_number;	/* The latest irq number we actually found. */
+ static volatile int irq_bitmap; /* The irqs we actually found. */
+ static int irq_handled;		/* The irq lines we have a handler on. */
+ 
+ 
+ static void
+ autoirq_probe(int irq)
+ {
+   irq_number = irq;
+   set_bit(irq, (void *)&irq_bitmap);	/* irq_bitmap |= 1 << irq; */
+ }
+ 
+ 
+ struct sigaction autoirq_sigaction = {
+   autoirq_probe, 0, SA_INTERRUPT, NULL
+ };
+ 
+ 
+ int
+ autoirq_setup(int waittime)
+ {
+   int i, mask;
+   int timeout = jiffies+waittime;
+ 
+   irq_number = 0;
+   irq_bitmap = 0;
+   irq_handled = 0;
+   for (i = 0; i < 16; i++) {
+ 	if (!irqaction(i, &autoirq_sigaction))
+ 	    set_bit(i, (void *)&irq_handled);	/* irq_handled |= 1 << i;*/
+   }
+ 
+   /* Update our USED lists. */
+   irqs_used |= ~irq_handled;
+ 
+   /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
+   while (timeout > jiffies)
+ 
+   for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
+ 	if (irq_bitmap & irq_handled & mask) {
+ 		irq_handled &= ~mask;
+ #ifdef notdef
+ 		printk(" Spurious interrupt on IRQ %d\n", i);
+ #endif
+ 		free_irq(i);
+ 	}
+   }
+   return(irq_handled);
+ }
+ 
+ 
+ int
+ autoirq_report(int waittime)
+ {
+   int i;
+   int timeout = jiffies+waittime;
+ 
+   /* Hang out at least <waittime> jiffies waiting for the IRQ. */
+   while (timeout > jiffies)
+ 	if (irq_number) break;
+ 
+   /* Retract the irq handlers that we installed. */
+   for (i = 0; i < 16; i++) {
+ 	if (test_bit(i, (void *)&irq_handled))
+ 	    free_irq(i);
+   }
+   return(irq_number);
+ }
*** original/net/drv/becker/becker.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/becker.h	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,85 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Constant definitions for the BECKER device driver kit.
+  *
+  * Version:	@(#)becker.h	1.3.0	03/01/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #define BECKER_MAJOR	20		/* VFS major device		*/
+ #define BECKER_MAX_DEV	64		/* should be enough for now	*/
+ 
+ 
+ /* Feature selection. */
+ #define HAVE_PINGPONG		1	/* allow ping-pong buffering	*/
+ 
+ 
+ /*
+  * BECKER feature selection.
+  *
+  * Note: for most old WD cards, the AutoProbe doesn't work.  You have
+  *	 to force those cards into operation, by specifying the I/O add-
+  *	 ress, the IRQ and the address of the shared memory in the Space.c
+  *	 file entry.  All other supported cards behave like they should,
+  *	 you can leave the values to 0. -FvK
+  */
+ 
+ #define EL2_NONSHARED_MEM	1	/* probe for non-sharedmem card	*/
+ #define EL2_MEMTEST		0	/* check the assigned sh-memory	*/
+ 
+ #define NE_SHOW_ALL_SAPROM	0	/* show all SAPROM contents	*/
+ #define NE_RW_BUGFIX		0	/* use the NEx000 8390 bugfix	*/
+ 
+ #define HP_RW_BUGFIX		0	/* use the Crynwr fix		*/
+ 
+ 
+ /* Has the master NETDBG flag been set? */
+ #undef	BECKER_DEBUG
+ #ifdef	NETDBG
+ #   undef  BECKER_DEBUG
+ #   define BECKER_DEBUG
+ #endif
+ 
+ #ifdef	BECKER_DEBUG
+ #   define DPRINTF(x)	becker_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ /* Define which major classes of Ethernet cards we support. */
+ #define BECARD_ANY	0		/* probe for any card		*/
+ #define BECARD_ULTRA	1		/* probe for an SMC Ultra card	*/
+ #define BECARD_WD	2		/* probe for a WD or SMC card	*/
+ #define BECARD_NE	3		/* probe for an NE-x000 card	*/
+ #define BECARD_HP	4		/* probe for an HP Lan card	*/
+ #define BECARD_EL1	5		/* probe for a 3com 3C501 card	*/
+ #define BECARD_EL2	6		/* probe for a 3com 3C503 card	*/
+ #define BECARD_EL3	7		/* probe for a 3com 3C509 card	*/
+ #define BECARD_EL16	8		/* probe for a 3com 3C507 card	*/
+ #define BECARD_PLUS	9		/* probe for a 3com 3C5?? card	*/
+ #define BECARD_AT15	10		/* probe for AT1500 card	*/
+ #define BECARD_AT17	11		/* probe for AT1700 card	*/
+ #define BECARD_ZNET	12		/* probe for ZNET card		*/
+ #define BECARD_EEXP	13		/* probe for EtherExpress card	*/
+ #define BECARD_AC3200	14		/* probe for AC3200 card	*/
+ #define BECARD_E2100	15		/* probe for E2100 card		*/
+ #define BECARD_ATP	16		/* probe for ATP card		*/
+ #define BECARD_ZNOTE	17		/* probe for Z-Note card	*/
+ 
+ 
+ extern int		becker_init(struct ddi *ddi);
+ 
+ 
+ /* End of BECKER.H */
*** original/net/drv/becker/dummy.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/dummy.c	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,52 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Temporary hack while porting the drivers, to prevent us
+  *		from having to re-configure the kernel each time.
+  *
+  * Version:	@(#)dummy.c	1.3.0	03/01/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/fs.h>
+ #include <linux/tty.h>
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <asm/io.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ int
+ elplus_probe(struct ei *ei)
+ {
+   printk("3Com 3C505 (EtherLink Plus) not available.\n");
+   return(-ENODEV);
+ }
+ 
+ 
+ int
+ ac3200_probe(struct ei *ei)
+ {
+   printk("Ansel Communications AC3200 not available.\n");
+   return(-ENODEV);
+ }
+ 
+ 
+ int
+ e2100_probe(struct ei *ei)
+ {
+   printk("Cabletron E2100 not available.\n");
+   return(-ENODEV);
+ }
*** original/net/drv/becker/eexpress.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/eexpress.c	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,1004 ----
+ /* eexpress.c: Intel EtherExpress device driver for Linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 	Copyright 1993 United States Government as represented by the Director,
+ 	National Security Agency.  This software may only be used and distributed
+ 	according to the terms of the GNU Public License as modified by SRC,
+ 	incorported herein by reference.
+ 
+ 	The author may be reached as becker@super.org or
+ 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+ 	Things remaining to do:
+ 	Check that the 586 and ASIC are reset/unreset at the right times.
+ 	Check tx and rx buffer setup.
+ 	The current Tx is single-buffer-only.
+ 	Move the theory of operation and memory map documentation.
+ 	Rework the board error reset
+ 	The statistics need to be updated correctly.
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/malloc.h>
+ #include <linux/string.h>
+ #include <linux/interrupt.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"eexpress.c:v0.07 1/19/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ /*
+   Sources:
+ 	This driver wouldn't have been written with the availability of the
+ 	Crynwr driver source code.	It provided a known-working implementation
+ 	that filled in the gaping holes of the Intel documention.  Three cheers
+ 	for Russ Nelson.
+ 
+ 	Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ 	info that the casual reader might think that it documents the i82586.
+ */
+ 
+ /*
+   			Details of the i82586.
+ 
+    You'll really need the databook to understand the details of this part,
+    but the outline is that the i82586 has two seperate processing units.
+ 
+    The Rx unit uses a list of frame descriptors and a list of data buffer
+    descriptors.  We use full-sized (1518 byte) data buffers, so there is
+    a one-to-one pairing of frame descriptors to buffer descriptors.
+ 
+    The Tx ("command") unit executes a list of commands that look like:
+ 		Status word		Written by the 82586 when the command is done.
+ 		Command word	Command in lower 3 bits, post-command action in upper 3
+ 		Link word		The address of the next command.
+ 		Parameters		(as needed).
+ 
+ 	Some definitions related to the Command Word are:
+  */
+ #define CMD_EOL		0x8000			/* The last command of the list, stop. */
+ #define CMD_SUSP	0x4000			/* Suspend after doing cmd. */
+ #define CMD_INTR	0x2000			/* Interrupt after doing cmd. */
+ 
+ enum commands {
+ 	CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ 	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+ 
+ /*
+   		Details of the EtherExpress Implementation
+   The EtherExpress takes an unusual approach to host access to packet buffer
+   memory.  The host can use either the Dataport, with independent
+   autoincrementing read and write pointers, or it can I/O map 32 bytes of the
+   memory using the "Shadow Memory Pointer" (SMB) as follows:
+ 			ioaddr						Normal EtherExpress registers
+ 			ioaddr+0x4000...0x400f		Buffer Memory at SMB...SMB+15
+ 			ioaddr+0x8000...0x800f		Buffer Memory at SMB+16...SMB+31
+ 			ioaddr+0xC000...0xC007		"" SMB+16...SMB+23 (hardware flaw?)
+ 			ioaddr+0xC008...0xC00f		Buffer Memory at 0x0008...0x000f
+   The last I/O map set is useful if you put the i82586 System Command Block
+   (the command mailbox) exactly at 0x0008.  (There seems to be some
+   undocumented init structure at 0x0000-7, so I had to use the Crywnr memory
+   setup verbatim for those four words anyway.)
+ 
+   A problem with using either one of these mechanisms is that you must run
+   single-threaded, or the interrupt handler must restore a changed value of
+   the read, write, or SMB pointers.
+ 
+   Unlike the Crynwr driver, my driver mostly ignores the I/O mapped "feature"
+   and relies heavily on the dataport for buffer memory access.  To minimize
+   switching, the read_pointer is dedicated to the Rx interrupt handler, and
+   the write_pointer is used by the send_packet() routine (it's carefully saved
+   and restored when it's needed by the interrupt handler).
+   */
+ 
+ /* Offsets from the base I/O address. */
+ #define DATAPORT	0	/* Data Transfer Register. */
+ #define WRITE_PTR	2	/* Write Address Pointer. */
+ #define READ_PTR	4	/* Read Address Pointer. */
+ #define SIGNAL_CA	6	/* Frob the 82586 Channel Attention line. */
+ #define SET_IRQ		7	/* IRQ Select. */
+ #define SHADOW_PTR	8	/* Shadow Memory Bank Pointer. */
+ #define MEM_Ctrl	11
+ #define MEM_Page_Ctrl	12
+ #define Config		13
+ #define EEPROM_Ctrl	14
+ #define ID_PORT		15
+ 
+ /* EEPROM_Ctrl bits. */
+ #define EE_SHIFT_CLK	0x01	/* EEPROM shift clock. */
+ #define EE_CS		0x02	/* EEPROM chip select. */
+ #define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+ #define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+ #define EE_CTRL_BITS	(EE_SHIFT_CLK | EE_CS | EE_DATA_WRITE | EE_DATA_READ)
+ #define ASIC_RESET	0x40
+ #define _586_RESET	0x80
+ 
+ /* Offsets to elements of the System Control Block structure. */
+ #define SCB_STATUS	0xc008
+ #define SCB_CMD		0xc00A
+ #define	 CUC_START	0x0100
+ #define	 CUC_RESUME	0x0200
+ #define	 CUC_SUSPEND	0x0300
+ #define	 RX_START	0x0010
+ #define	 RX_RESUME	0x0020
+ #define	 RX_SUSPEND	0x0030
+ #define SCB_CBL		0xc00C	/* Command BLock offset. */
+ #define SCB_RFA		0xc00E	/* Rx Frame Area offset. */
+ 
+ /*
+   What follows in 'init_words[]' is the "program" that is downloaded to the
+   82586 memory.	 It's mostly tables and command blocks, and starts at the
+   reset address 0xfffff6.
+ 
+   Even with the additional "don't care" values, doing it this way takes less
+   program space than initializing the individual tables, and I feel it's much
+   cleaner.
+ 
+   The databook is particularly useless for the first two structures; they are
+   completely undocumented.  I had to use the Crynwr driver as an example.
+ 
+    The memory setup is as follows:
+    */
+ 
+ #define CONFIG_CMD	0x0018
+ #define SET_SA_CMD	0x0024
+ #define SA_OFFSET	0x002A
+ #define IDLELOOP	0x30
+ #define TDR_CMD		0x38
+ #define TDR_TIME	0x3C
+ #define DUMP_CMD	0x40
+ #define DIAG_CMD	0x48
+ #define SET_MC_CMD	0x4E
+ #define DUMP_DATA	0x56	/* A 170 byte buffer for dump and Set-MC into. */
+ 
+ #define TX_BUF_START	0x0100
+ #define NUM_TX_BUFS 	4
+ #define TX_BUF_SIZE	0x0680	/* packet+header+TBD+extra (1518+14+20+16) */
+ #define TX_BUF_END	0x2000
+ 
+ #define RX_BUF_START	0x2000
+ #define RX_BUF_SIZE 	(0x640)	/* packet+header+RBD+extra */
+ #define RX_BUF_END	0x4000
+ 
+ /*
+   That's it: only 86 bytes to set up the beast, including every extra
+   command available.  The 170 byte buffer at DUMP_DATA is shared between the
+   Dump command (called only by the diagnostic program) and the SetMulticastList
+   command.
+ 
+   To complete the memory setup you only have to write the station address at
+   SA_OFFSET and create the Tx & Rx buffer lists.
+ 
+   The Tx command chain and buffer list is setup as follows:
+   A Tx command table, with the data buffer pointing to...
+   A Tx data buffer descriptor.  The packet is in a single buffer, rather than
+      chaining together several smaller buffers.
+   A NoOp command, which initially points to itself,
+   And the packet data.
+ 
+   A transmit is done by filling in the Tx command table and data buffer,
+   re-writing the NoOp command, and finally changing the offset of the last
+   command to point to the current Tx command.  When the Tx command is finished,
+   it jumps to the NoOp, when it loops until the next Tx command changes the
+   "link offset" in the NoOp.  This way the 82586 never has to go through the
+   slow restart sequence.
+ 
+   The Rx buffer list is set up in the obvious ring structure.  We have enough
+   memory (and low enough interrupt latency) that we can avoid the complicated
+   Rx buffer linked lists by alway associating a full-size Rx data buffer with
+   each Rx data frame.
+ 
+   I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+   use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+ 
+   */
+ 
+ static short init_words[] = {
+   0x0000,				/* Set bus size to 16 bits. */
+   0x0000,0x0000,			/* Set control mailbox (SCB) addr. */
+   0,0,					/* pad to 0x000000. */
+   0x0001,				/* Status word that's cleared when init is done. */
+   0x0008,0,0,				/* SCB offset, (skip, skip) */
+ 
+   0,0xf000|RX_START|CUC_START,	/* SCB status and cmd. */
+   CONFIG_CMD,				/* Command list pointer, points to Configure. */
+   RX_BUF_START,				/* Rx block list. */
+   0,0,0,0,				/* Error count: CRC, align, buffer, overrun. */
+ 
+   /* 0x0018: Configure command. Change to put MAC data with packet. */
+   0, CmdConfigure,			/* Status, command. */
+   SET_SA_CMD,				/* Next command is Set Station Addr. */
+   0x0804,				/* "4" bytes of config data, 8 byte FIFO. */
+   0x2e40,				/* Magic values, including MAC data location. */
+   0,					/* Unused pad word. */
+ 
+   /* 0x0024: Setup station address command. */
+   0, CmdSASetup,
+   SET_MC_CMD,				/* Next command. */
+   0xaa00,0xb000,0x0bad,			/* Station address (to be filled in) */
+ 
+   /* 0x0030: NOP, looping back to itself.	 Point to first Tx buffer to Tx. */
+   0, CmdNOp, IDLELOOP, 0		/* pad */,
+ 
+   /* 0x0038: A unused Time-Domain Reflectometer command. */
+   0, CmdTDR, IDLELOOP, 0,
+ 
+   /* 0x0040: An unused Dump State command. */
+   0, CmdDump, IDLELOOP, DUMP_DATA,
+ 
+   /* 0x0048: An unused Diagnose command. */
+   0, CmdDiagnose, IDLELOOP,
+ 
+   /* 0x004E: An empty set-multicast-list command. */
+ #ifdef initial_text_tx
+   0, CmdMulticastList, DUMP_DATA, 0,
+ #else
+   0, CmdMulticastList, IDLELOOP, 0,
+ #endif
+ 
+   /* 0x0056: A continuous transmit command, only here for testing. */
+   0, CmdTx, DUMP_DATA, DUMP_DATA+8, 0x83ff, -1, DUMP_DATA, 0,
+ };
+ 
+ 
+ /* Reverse IRQ map: the value to put in the SET_IRQ reg. for IRQ<index>. */
+ static char irqrmap[]={0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0};
+ static struct ei *eexp_irq2ei_map[16] = { NULL };
+ 
+ 
+ /*
+  * The horrible routine to read a word from the serial EEPROM.
+  * The delay between EEPROM clock transitions.
+  */
+ #define eeprom_delay() \
+   { \
+ 	int _i = 40; \
+ 	while (--_i > 0) { \
+ 		__SLOW_DOWN_IO; \
+ 	} \
+   }
+ #define EE_READ_CMD (6 << 6)
+ 
+ 
+ static int
+ read_eeprom(int ioaddr, int location)
+ {
+   short ee_addr = ioaddr + EEPROM_Ctrl;
+   int read_cmd = location | EE_READ_CMD;
+   short ctrl_val = EE_CS | _586_RESET;
+   unsigned short retval = 0;
+   int i;
+ 	
+   outb(ctrl_val, ee_addr);
+ 	
+   /* Shift the read command bits out. */
+   for (i = 8; i >= 0; i--) {
+ 	short outval = (read_cmd & (1 << i))
+ 		? ctrl_val | EE_DATA_WRITE
+ 		: ctrl_val;
+ 	outb(outval, ee_addr);
+ 	outb(outval | EE_SHIFT_CLK, ee_addr);	/* EEPROM clock tick */
+ 	eeprom_delay();
+ 	outb(outval, ee_addr);	/* finish EEPROM a clock tick */
+ 	eeprom_delay();
+   }
+   outb(ctrl_val, ee_addr);
+ 	
+   for (i = 16; i > 0; i--) {
+ 	outb(ctrl_val | EE_SHIFT_CLK, ee_addr);	 eeprom_delay();
+ 	retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ 	outb(ctrl_val, ee_addr);  eeprom_delay();
+   }
+ 
+   /* Terminate the EEPROM access. */
+   ctrl_val &= ~EE_CS;
+   outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+   eeprom_delay();
+   outb(ctrl_val, ee_addr);
+   eeprom_delay();
+   return(retval);
+ }
+ 
+ 
+ /* Initialize the Rx-block list. */
+ static void
+ init_rx_bufs(struct ei *ei)
+ {
+   short ioaddr = ei->ioaddr;
+   int cur_rxbuf = ei->rx_head = RX_BUF_START;
+ 	
+   /* Initialize each Rx frame + data buffer. */
+   do {	/* While there is room for one more. */
+ 	outw(cur_rxbuf, ioaddr + WRITE_PTR);
+ 	outw(0x0000, ioaddr); 			/* status */
+ 	outw(0x0000, ioaddr);			/* command */
+ 	outw(cur_rxbuf + RX_BUF_SIZE, ioaddr);	/* link */
+ 	outw(cur_rxbuf + 22, ioaddr);		/* buffer offset */
+ 	outw(0xFeed, ioaddr); 			/* pad for dest addr */
+ 	outw(0xF00d, ioaddr);
+ 	outw(0xF001, ioaddr);
+ 	outw(0x0505, ioaddr); 			/* pad for source addr */
+ 	outw(0x2424, ioaddr);
+ 	outw(0x6565, ioaddr);
+ 	outw(0xdeaf, ioaddr);			/* pad for protocol */
+ 
+ 	outw(0x0000, ioaddr);			/* buffer: Actual count */
+ 	outw(-1, ioaddr);			/* buffer: Next (none) */
+ 	outw(cur_rxbuf + 0x20, ioaddr);		/* buffer: Address low */
+ 	outw(0x0000, ioaddr);
+ 
+ 	/* Finally, the number of bytes in the buffer. */
+ 	outw(0x8000 + RX_BUF_SIZE-0x20, ioaddr);
+ 		
+ 	ei->rx_tail = cur_rxbuf;
+ 	cur_rxbuf += RX_BUF_SIZE;
+   } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+ 	
+   /*
+    * Terminate the list by setting the EOL bit, and wrap
+    * the pointer to make the list a ring.
+    */
+   outw(ei->rx_tail + 2, ioaddr + WRITE_PTR);
+   outw(0xC000, ioaddr);				/* command, mark as last */
+   outw(ei->rx_head, ioaddr);			/* link */
+ }
+ 
+ 
+ static void
+ init_82586_mem(struct ei *ei)
+ {
+   short ioaddr = ei->ioaddr;
+   unsigned short *ptr;
+ 
+   /*
+    * Enable loopback to protect the wire while starting up.
+    * This is Superstition From Crynwr.
+    */
+   outb(inb(ioaddr + Config) | 0x02, ioaddr + Config);
+ 
+   /* Hold the 586 in reset during the memory initialization. */
+   outb(_586_RESET, ioaddr + EEPROM_Ctrl);
+ 
+   /* Place the write pointer at 0xfff6 (address-aliased to 0xfffff6). */
+   outw(0xfff6, ioaddr + WRITE_PTR);
+   outsw(ioaddr, init_words, sizeof(init_words)>>1);
+ 
+   /* Fill in the station address. */
+   outw(SA_OFFSET, ioaddr + WRITE_PTR);
+   ptr = (unsigned short *) ei->dev->mac_aget(ei->dev);
+   outsw(ioaddr, ptr, 3);
+ 
+   /* The Tx-block list is written as needed.  We just set up the values. */
+ #ifdef initial_text_tx
+   ei->tx_cmd_link = DUMP_DATA + 4;
+ #else
+   ei->tx_cmd_link = IDLELOOP + 4;
+ #endif
+   ei->tx_head = ei->tx_reap = TX_BUF_START;
+ 
+   init_rx_bufs(ei);
+ 
+   /* Start the 586 by releasing the reset line. */
+   outb(0x00, ioaddr + EEPROM_Ctrl);
+ 
+   /*
+    * This was time consuming to track down: you need to give two channel
+    * attention signals to reliably start up the i82586.
+    */
+   outb(0, ioaddr + SIGNAL_CA);
+ 
+   {
+ 	int boguscnt = 50;
+ 	while (inw(ioaddr + SCB_STATUS) == 0) {
+ 		if (--boguscnt == 0) {
+ 			printk("%s: i82586 initialization timed out with status 0x%04X, cmd 0x%04X\n",
+ 				ei->dev->if_name,
+ 				inw(ioaddr + SCB_STATUS),
+ 				inw(ioaddr + SCB_CMD));
+ 			break;
+ 		}
+ 	}
+ 
+ 	/* Issue channel-attn -- the 82586 won't start without it. */
+ 	outb(0, ioaddr + SIGNAL_CA);
+   }
+ 
+   /* Disable loopback. */
+   outb(inb(ioaddr + Config) & ~0x02, ioaddr + Config);
+ 
+   DPRINTF((DBG_EEXP, "%s: initialized 82586, status 0x%04X\n",
+ 			ei->dev->if_name, inw(ioaddr + SCB_STATUS)));
+ }
+ 
+ 
+ static void
+ eexp_send_packet(struct ei *ei, void *buf, short length)
+ {
+   short ioaddr = ei->ioaddr;
+   short tx_block = ei->tx_head;
+ 
+   /* Set the write pointer to the Tx block, and put out the header. */
+   outw(tx_block, ioaddr + WRITE_PTR);
+   outw(0x0000, ioaddr);			/* TX status */
+   outw(CMD_INTR|CmdTx, ioaddr);		/* TX command */
+   outw(tx_block+16, ioaddr);		/* next command is a NoOp */
+   outw(tx_block+8, ioaddr);		/* data Buffer offset */
+ 
+   /* Output the data buffer descriptor. */
+   outw(length | 0x8000, ioaddr);	/* byte count parameter */
+   outw(-1, ioaddr);			/* no next data buffer */
+   outw(tx_block+22, ioaddr);	/* buffer follows the NoOp command */
+   outw(0x0000, ioaddr);		/* buffer address high bits (always zero) */
+ 
+   /* Output the Loop-back NoOp command. */
+   outw(0x0000, ioaddr);			/* TX status */
+   outw(CmdNOp, ioaddr);			/* TX command */
+   outw(tx_block+16, ioaddr);		/* next is myself */
+ 
+   /*
+    * Output the packet using the write pointer.
+    * Hmmm, it feels a little like a 3c501!
+    */
+   outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+ 
+   /* Set the old command link pointing to this send packet. */
+   outw(ei->tx_cmd_link, ioaddr + WRITE_PTR);
+   outw(tx_block, ioaddr);
+   ei->tx_cmd_link = tx_block + 20;
+ 
+   /* Set the next free tx region. */
+   ei->tx_head = tx_block + TX_BUF_SIZE;
+   if (ei->tx_head > TX_BUF_END - TX_BUF_SIZE)
+ 			ei->tx_head = TX_BUF_START;
+ 
+   DPRINTF((DBG_EEXP,
+ 	"%s: EExp @%X send length=%d, tx_block %3X, next %3X, "
+ 	"reap %4X status %4.4X\n", ei->dev->if_name, ioaddr, length,
+ 		tx_block, ei->tx_head, ei->tx_reap, inw(ioaddr + SCB_STATUS)));
+ 
+   if (ei->tx_head != ei->tx_reap) ei->dev->if_tbusy = 0;
+ }
+ 
+ 
+ /* We have a good packet(s), get it/them out of the buffers. */
+ static void
+ eexp_receive(struct ei *ei)
+ {
+   short ioaddr = ei->ioaddr;
+   short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+   short rx_head = ei->rx_head;
+   short rx_tail = ei->rx_tail;
+   short boguscount = 10;
+   short frame_status;
+ 
+   /* Set the read pointer to the Rx frame. */
+   outw(rx_head, ioaddr + READ_PTR);
+   while ((frame_status = inw(ioaddr)) < 0) {	/* command complete */
+ 	short rfd_cmd = inw(ioaddr);
+ 	short next_rx_frame = inw(ioaddr);
+ 	short data_buffer_addr = inw(ioaddr);
+ 	short pkt_len;
+ 
+ 	/* Set the read pointer the data buffer. */
+ 	outw(data_buffer_addr, ioaddr + READ_PTR);
+ 	pkt_len = inw(ioaddr);
+ 
+ 	if ((rfd_cmd != 0) || (data_buffer_addr != rx_head + 22) ||
+ 	    (pkt_len & 0xC000 != 0xC000)) {
+ 		printk("%s: RX frame at 0x%X corrupted, status %04X cmd %04X"
+ 		       "next %04X data-buf @%04X %04X\n",
+ 			ei->dev->if_name, rx_head, frame_status, rfd_cmd,
+ 			next_rx_frame, data_buffer_addr, pkt_len);
+ 	} else if ((frame_status & 0x2000) == 0) {
+ 		/* Frame received, but with error. */
+ 		ei->dev->if_stats->rx_errors++;
+ 		if (frame_status & 0x0800)
+ 			ei->dev->if_stats->rx_crc++;
+ 		if (frame_status & 0x0400)
+ 			ei->dev->if_stats->rx_frame++;
+ 		if (frame_status & 0x0200)
+ 			ei->dev->if_stats->rx_fifo++;
+ 		if (frame_status & 0x0100)
+ 			ei->dev->if_stats->rx_overrun++;
+ 		if (frame_status & 0x0080)
+ 			ei->dev->if_stats->rx_length++;
+ 	} else {
+ 		/* Malloc up new buffer. */
+ 		struct sk_buff *skb;
+ 
+ 		pkt_len &= 0x3fff;
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ 			printk("%s: memory squeeze, dropping packet\n",
+ 						ei->dev->if_name);
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		skb->len = pkt_len;
+ 		skb->dev = ei->dev;
+ 
+ 		outw(data_buffer_addr + 10, ioaddr + READ_PTR);
+ 
+ 		insw(ioaddr, (void *)skb->raw, (pkt_len + 1) >> 1);
+ 		
+ 		if (dev_rint(ei->dev, skb, pkt_len) != 0) {
+ 			ei->dev->if_stats->rx_dropped++;
+ 		} else {
+ 			ei->dev->if_stats->rx_packets++;
+ 		}
+ 
+ 	}
+ 
+ 	/* Clear the status word and set End-of-List on the rx frame. */
+ 	outw(rx_head, ioaddr + WRITE_PTR);
+ 	outw(0x0000, ioaddr);
+ 	outw(0xC000, ioaddr);
+ 
+ #ifndef final_version
+ 	if (next_rx_frame != rx_head + RX_BUF_SIZE
+ 		&& next_rx_frame != RX_BUF_START) {
+ 		printk("%s: Rx next frame at %#x is %#x instead of %#x.\n",
+ 			ei->dev->if_name, rx_head, next_rx_frame,
+ 			rx_head + RX_BUF_SIZE);
+ 		next_rx_frame = rx_head + RX_BUF_SIZE;
+ 		if (next_rx_frame >= RX_BUF_END - RX_BUF_SIZE)
+ 			next_rx_frame = RX_BUF_START;
+ 	}
+ #endif
+ 	outw(rx_tail+2, ioaddr + WRITE_PTR);
+ 	outw(0x0000, ioaddr);	/* clear the end-of-list on the prev. RFD. */
+ 
+ #ifndef final_version
+ 	outw(rx_tail+4, ioaddr + READ_PTR);
+ 	if (inw(ioaddr) != rx_head) {
+ 		printk("%s: RX buf link mismatch, at %04X link %04X instead of %04X\n",
+ 			ei->dev->if_name, rx_tail,
+ 			(outw(rx_tail+4, ioaddr + READ_PTR), inw(ioaddr)),
+ 			rx_head);
+ 		outw(rx_head, ioaddr);
+ 	}
+ #endif
+ 
+ 	rx_tail = rx_head;
+ 	rx_head = next_rx_frame;
+ 	if (--boguscount == 0) break;
+ 	outw(rx_head, ioaddr + READ_PTR);
+   }
+ 	
+   ei->rx_head = rx_head;
+   ei->rx_tail = rx_tail;
+ 	
+   /* Restore the original write pointer. */
+   outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the network interface interrupts.
+  */
+ static void
+ eexp_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = eexp_irq2ei_map[irq];
+   int ioaddr, status, boguscount = 0;
+   short ack_cmd;
+ 
+   if (ei == NULL) {
+ 	printk("EExp: interrupt: IRQ%d for unknown device ??\n", irq);
+ 	return;
+   }
+   ei->interrupt = 1;
+   ioaddr = ei->ioaddr;
+ 	
+   status = inw(ioaddr + SCB_STATUS);
+   DPRINTF((DBG_EEXP, "%s: interrupt, status %4.4X\n",
+ 				ei->dev->if_name, status));
+ 
+   /* Disable the 82586's input to the interrupt line. */
+   outb(irqrmap[ei->irq], ioaddr + SET_IRQ);
+ 
+   /* Reap the Tx packet buffers. */
+   while (ei->tx_reap != ei->tx_head) { 	/* if (status & 0x8000) */
+ 	unsigned short tx_status;
+ 
+ 	outw(ei->tx_reap, ioaddr + READ_PTR);
+ 	tx_status = inw(ioaddr);
+ 	if (tx_status == 0) {
+ 		DPRINTF((DBG_EEXP, "Couldn't reap 0x%X\n", ei->tx_reap));
+ 		break;
+ 	}
+ 	if (tx_status & 0x2000) {
+ 		ei->dev->if_stats->tx_packets++;
+ 		ei->dev->if_stats->tx_collision += tx_status & 0xf;
+ 		ei->dev->if_tbusy = 0;
+ 		mark_bh(NET_BH);
+ 	} else {
+ 		ei->dev->if_stats->tx_errors++;
+ 		if (tx_status & 0x0600)
+ 			ei->dev->if_stats->tx_carrier++;
+ 		if (tx_status & 0x0100)
+ 			ei->dev->if_stats->tx_fifo++;
+ 		if (!(tx_status & 0x0040))
+ 			ei->dev->if_stats->tx_heartbeat++;
+ 		if (tx_status & 0x0020)
+ 			ei->dev->if_stats->tx_aborted++;
+ 	}
+ 
+ 	DPRINTF((DBG_EEXP, "Reaped %X, TX status %04X\n",
+ 					ei->tx_reap, tx_status));
+ 	ei->tx_reap += TX_BUF_SIZE;
+ 	if (ei->tx_reap > TX_BUF_END - TX_BUF_SIZE) ei->tx_reap = TX_BUF_START;
+ 	if (++boguscount > 4) break;
+   }
+ 
+   if (status & 0x4000) { /* packet received */
+ 	DPRINTF((DBG_EEXP, "received packet, rx_head %04X\n", ei->rx_head));
+ 	eexp_receive(ei);
+   }
+ 
+   /* Acknowledge the interrupt sources. */
+   ack_cmd = status & 0xf000;
+ 
+   if ((status & 0x0700) != 0x0200 && ei->start) {
+ 	short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ 
+ 	DPRINTF((DBG_EEXP,
+ 		"%s: command unit stopped, status %04X, restarting\n",
+ 			   ei->dev->if_name, status));
+ 
+ 	/*
+ 	 * If this ever occurs we must re-write the idle loop,
+ 	 * reset the TX list, and do a complete restart of the
+ 	 * command unit.
+ 	 */
+ 	outw(IDLELOOP, ioaddr + WRITE_PTR);
+ 	outw(0, ioaddr);
+ 	outw(CmdNOp, ioaddr);
+ 	outw(IDLELOOP, ioaddr);
+ 	outw(IDLELOOP, SCB_CBL);
+ 	ei->tx_cmd_link = IDLELOOP + 4;
+ 	ei->tx_head = ei->tx_reap = TX_BUF_START;
+ 
+ 	/* Restore the saved write pointer. */
+ 	outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ 	ack_cmd |= CUC_START;
+   }
+ 
+   if ((status & 0x0070) != 0x0040 && ei->start) {
+ 	short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ 
+ 	/*
+ 	 * The Rx unit is not ready, it must be hung.  Restart the
+ 	 * receiver by initializing the rx buffers, and issuing an
+ 	 * Rx start command.
+ 	 */
+ 	ei->dev->if_stats->rx_errors++;
+ 	DPRINTF((DBG_EEXP,
+ 		"%s: RX unit stopped status %04X RX head %04X tail %04X\n",
+ 			ei->dev->if_name, status, ei->rx_head, ei->rx_tail));
+ 	init_rx_bufs(ei);
+ 	outw(RX_BUF_START, SCB_RFA);
+ 	outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ 	ack_cmd |= RX_START;
+   }
+ 
+   outw(ack_cmd, ioaddr + SCB_CMD);
+   outb(0, ioaddr + SIGNAL_CA);
+ 
+   DPRINTF((DBG_EEXP, "%s: exiting interrupt, status %4.4X\n",
+ 		ei->dev->if_name, inw(ioaddr + SCB_CMD)));
+ 
+   /* Enable the 82586's input to the interrupt line. */
+   outb(irqrmap[ei->irq] | 0x08, ioaddr + SET_IRQ);
+ }
+ 
+ 
+ /*
+  * Open/initialize the board.
+  * This is called (in the current kernel) sometime after booting
+  * when the 'ifconfig' program is run.
+  */
+ static int
+ eexp_open(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   if (ei->irq == 0 || irqrmap[ei->irq] == 0) return(-ENXIO);
+ 
+   /* This is always true, but avoid the false IRQ. */
+   if ((eexp_irq2ei_map[ei->irq] != 0) ||
+       ((eexp_irq2ei_map[ei->irq] = ei) == NULL) ||
+       request_irq(ei->irq, eexp_interrupt)) return(-EAGAIN);
+ 
+   /* Initialize the 82586 memory and start it. */
+   init_82586_mem(ei);
+ 
+   /* Enable the interrupt line. */
+   outb(irqrmap[ei->irq] | 0x08, ioaddr + SET_IRQ);
+ 
+   ei->dev->if_tbusy = 0;
+   ei->interrupt = 0;
+   ei->start = 1;
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to eexp_open(). */
+ static int
+ eexp_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   /* Flush the Tx and disable Rx. */
+   outw(RX_SUSPEND | CUC_SUSPEND, ioaddr + SCB_CMD);
+   outb(0, ioaddr + SIGNAL_CA);
+ 
+   /* Disable the physical interrupt line. */
+   outb(0, ioaddr + SET_IRQ);
+ 
+   free_irq(ei->irq);
+ 
+   eexp_irq2ei_map[ei->irq] = NULL;
+ 
+   /* Update the statistics here. */
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ eexp_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   int ioaddr;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in eexp_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+ 
+   if (dev->if_tbusy) {
+ 	/*
+ 	 * If we get here, some higher level has decided we are broken.
+ 	 * There should really be a "kick me" function call instead.
+ 	 */
+ 	int tickssofar = (jiffies - dev->if_txtimer);
+ 
+ 	if (tickssofar < 5) return(1);
+ 
+ 	printk("%s: transmit timed out, %s?  ", dev->if_name,
+ 		inw(ioaddr+SCB_STATUS) & 0x8000
+ 			? "IRQ conflict" :
+ 			"network cable problem");
+ 	dev->if_stats->tx_errors++;
+ 
+ 	/* Try to restart the adaptor. */
+ 	if (ei->last_restart == ei->dev->if_stats->tx_packets) {
+ 		DPRINTF((DBG_EEXP, "resetting board.\n"));
+ 		/* Completely reset the adaptor. */
+ 		init_82586_mem(ei);
+ 	} else {
+ 		/* Issue the channel attention signal and hope it "gets better". */
+ 		DPRINTF((DBG_EEXP, "kicking board\n"));
+ 		outw(0xf000|CUC_START|RX_START, ioaddr + SCB_CMD);
+ 		outb(0, ioaddr + SIGNAL_CA);
+ 		ei->last_restart = dev->if_stats->tx_packets;
+ 	}
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+   }
+ 
+   /*
+    * If some higher layer thinks we've missed an tx-done interrupt
+    * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+    * itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   /* Block a timer-based transmit from overlapping. */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 	printk("%s: transmitter access conflict\n", dev->if_name);
+     else {
+ 	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ 	unsigned char *buf = skb->raw;
+ 
+ 	/* Disable the 82586's input to the interrupt line. */
+ 	outb(irqrmap[ei->irq], ioaddr + SET_IRQ);
+ 	eexp_send_packet(ei, buf, length);
+ 	dev->if_txtimer = jiffies;
+ 
+ 	/* Enable the 82586 interrupt input. */
+ 	outb(0x08 | irqrmap[ei->irq], ioaddr + SET_IRQ);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ eexp_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   if (num_addrs < 0) {
+ 	/*
+ 	 * Not written yet, this requires expanding the
+ 	 * init_words config cmd.
+ 	 */
+   } else if (num_addrs > 0) {
+ 	/*
+ 	 * Fill in the SET_MC_CMD with the number of address
+ 	 * bytes, followed by the list of multicast addresses
+ 	 * to be accepted.
+ 	 */
+ 	outw(SET_MC_CMD + 6, ioaddr + WRITE_PTR);
+ 	outw(num_addrs * 6, ioaddr);
+ 	outsw(ioaddr, addrs, num_addrs*3);	/* 3 = addr len in words */
+ 	/* We must trigger a whole 586 reset due to a bug. */
+   } else {
+ 	/*
+ 	 * Not written yet, this requires expanding the
+ 	 * init_words config cmd.
+ 	 */
+ 	outw(99, ioaddr);	/* disable promiscuous mode, use normal mode */
+   }
+ }
+ 
+ 
+ static int
+ eexp_probe1(short ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   unsigned short station_addr[3];
+   int i;
+ 
+   DPRINTF((DBG_EEXP, "%s: EtherExpress probe at 0x%3X,",
+ 				ei->dev->if_name, ioaddr));
+ 
+   /*
+    * The station address is stored !backwards! in the EEPROM,
+    * reverse after reading.  (Hmmm, a little brain-damage there
+    * at Intel, eh?)
+    */
+   station_addr[0] = read_eeprom(ioaddr, 2);
+   station_addr[1] = read_eeprom(ioaddr, 3);
+   station_addr[2] = read_eeprom(ioaddr, 4);
+ 
+   /* Check the first three octets of the S.A. for the manufactor's code. */
+   if (station_addr[2] != 0x00AA || (station_addr[1] & 0xFF00) != 0x0000) {
+ 	DPRINTF((DBG_EEXP, " rejected (invalid address %04X%04X%04X).\n",
+ 		   station_addr[2], station_addr[1], station_addr[0]));
+ 	return(-ENODEV);
+   }
+ 
+   /* We've committed to using the board, and can start filling in. */
+   snarf_region(ioaddr, 16);
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+ 
+   /* Fetch the station address. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = ((unsigned char *)station_addr)[5 - i];
+   }
+ 
+   /*
+    * There is no reason for the driver to care, but I print
+    * out the interface to minimize bogus bug reports.
+    */
+   {
+ 	char irqmap[] = { 0, 9, 3, 4, 5, 10, 11, 0 };
+ #ifdef BECKER_DEBUG
+ 	char *ifmap[] = { "AUI", "BNC", "10baseT" };
+ #endif
+ 	enum iftype { AUI=0, BNC=1, TP=2 };
+ 	unsigned short setupval = read_eeprom(ioaddr, 0);
+ 
+ 	ei->irq = irqmap[setupval >> 13];
+ 	ei->interface_num = (setupval & 0x1000) == 0
+ 		? AUI
+ 		: read_eeprom(ioaddr, 5) & 0x1
+ 			? TP
+ 			: BNC;
+ 	DPRINTF((DBG_EEXP, ", IRQ%d, Interface %s.\n",
+ 				ei->irq, ifmap[ei->interface_num]));
+ 
+ 	/*
+ 	 * Release the IRQ line so that it can be shared if we
+ 	 * don't use the ethercard.
+ 	 */
+ 	outb(0x00, ioaddr + SET_IRQ);
+   }
+ 
+   /* It's now OK to leave the board in reset, pending the open(). */
+   outb(ASIC_RESET, ioaddr + EEPROM_Ctrl);
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = eexp_xmit;
+   ei->chip_s_multi = eexp_set_multicast;
+ 
+   DPRINTF((DBG_EEXP, "%s: EtherExpress at 0x%3X, using IRQ%d\n",
+ 			ei->dev->if_name, ei->ioaddr, ei->irq));
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = NULL;
+   ei->card_open = eexp_open;
+   ei->card_close = eexp_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Check for a network adaptor of this type, and return '0' iff one exists.
+  * Don't probe all settable addresses, 0x[23][0-7]0, just common ones.
+  */
+ int
+ express_probe(struct ei *ei)
+ {
+   static int eexp_card_nr = 0;
+   int *port, ports[] = { 0x300, 0x270, 0x320, 0x340, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "EtherExpress";
+   sprintf(ei->dev->if_name, "eex%d", eexp_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(eexp_probe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	short id_addr = *port + ID_PORT;
+ 	unsigned short sum = 0;
+ 	int i;
+ 
+ 	for (i = 4; i > 0; i--) {
+ 		short id_val = inb(id_addr);
+ 		sum |= (id_val >> 4) << ((id_val & 3) << 2);
+ 	}
+ 	if ((sum == 0xBABA) && eexp_probe1(*port, ei) == 0) return(0);
+   }
+   return(-ENODEV);
+ }
*** original/net/drv/becker/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/global.h	Mon May  9 13:13:01 1994
***************
*** 0 ****
--- 1,134 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Global definitions for the BECKER device driver kit.
+  *
+  * Version:	@(#)global.h	1.3.0	03/01/94
+  *
+  * Authors:	Donald J. Becker, <becker@super.org>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This file is part of Donald Becker's driver kit, and is
+  *		distributed under the same license.  Some of these names
+  *		and comments are from the Crynwr packet drivers.
+  */
+ 
+ 
+ /* Debug levels. */
+ #define DBG_DRV		1		/* general driver debugging	*/
+ #define DBG_8390	2		/* NS8390 chipset debugging	*/
+ #define DBG_ULTRA	4		/* SMC Ultra card		*/
+ #define DBG_WD		5		/* SMC/WD 80x3 card		*/
+ #define DBG_NE		6		/* NEx000 card			*/
+ #define DBG_HP		7		/* HPLAN card			*/
+ #define DBG_EL1		8		/* 3c501 card			*/
+ #define DBG_EL2		9		/* 3c503 card			*/
+ #define DBG_EL3		10		/* 3c509 card			*/
+ #define DBG_EL16	11		/* 3c507 card			*/
+ #define DBG_ATP		12		/* ATP card			*/
+ #define DBG_EEXP	13		/* EtherExpress card		*/
+ #define DBG_ZNOTE	14		/* Zenith Z-Note card		*/
+ #define DBG_AT1500	15		/* AT1500 (LANCE) based cards	*/
+ #define DBG_AT1700	16		/* AT1700 cards			*/
+ 
+ /* The per-board driver control block. */
+ struct ei {
+   char		*name;			/* card name			*/
+   unsigned int	ioaddr;			/* I/O address where card is	*/
+   unsigned int	irq;			/* IRQ number			*/
+   unsigned int	dma;			/* DMA channel			*/
+   unsigned long	membase;		/* shared memory address	*/
+   unsigned int	memsize,		/* shared memory size		*/
+   		interface_num;		/* port (AUI, 10bT.) to use	*/
+   unsigned long	mem_start,		/* shared memory pointers	*/
+ 		mem_end,
+ 		rmem_start,
+ 		rmem_end;
+   unsigned int	iobase;			/* I/O address where NIC is	*/
+   struct ifnet	*dev;			/* back pointer to NET iface	*/
+   struct sigaction *sigaction;		/* ptr to card-spec. SIGACTION	*/
+   int		open:1,
+   		start:1,
+   		word16:1,		/* Use 16-bit (vs 8-bit) access	*/
+   		txing:1,		/* Transmit Active		*/
+   		dmaing:2,		/* Remote DMA Active		*/
+   		irqlock:1,		/* 8390 ints disabled when '1'	*/
+   		pingpong:1,		/* Using the ping-pong driver	*/
+ 		interrupt:1;		/* an interrupt occurred	*/
+ 
+   /*
+    * The following block of data is really dependent on the
+    * chipset and ether card in use.  When all drivers are
+    * ported, we should rewrite the stuff into a "local" data
+    * block, with a pointer to it here. -FvK
+    */
+   unsigned char tx_start_page,
+   		rx_start_page,
+   		stop_page;
+   unsigned char current_page,		/* Read pointer in buffer	*/
+   		txqueue,		/* Tx Packet buff queue length	*/
+   		in_interrupt;
+   short		tx1, tx2;		/* Packet lengths for ping-pong tx */
+   short		lasttx;			/* Alpha version consistency check */
+   short		reg0, reg5;		/* for WD8013 REG0 and REG5 regs */
+   short		port;			/* for 3c509 */
+   int		last_restart;		/* for 3c507 and EExpress */
+   ushort	rx_head;
+   ushort	rx_tail;
+   ushort	tx_head;
+   ushort	tx_cmd_link;
+   ushort	tx_reap;
+   ushort	saved_tx_size;		/* for ATP */
+   unsigned char re_tx,			/* Number of packet retransmissions. */
+ 		tx_unit_busy,
+ 		addr_mode,		/* Current Rx filter e.g. promiscuous, etc. */
+ 		pac_cnt_in_tx_buf;
+   /* End of data block. */
+ 
+   void		*pvt;			/* ptr to private data area */
+ 
+   /* Card-specific service functions. */
+   void		(*chip_init)	(struct ei *, int startp);
+   void		(*chip_open)	(struct ei *);
+   void		(*chip_s_multi)	(struct ei *, int num, void *);
+   void		(*card_init)	(struct ei *);
+   int		(*card_open)	(struct ei *);
+   int		(*card_close)	(struct ei *);
+   int		(*card_input)	(struct ei *, int, char *, int);
+   void		(*card_output)	(struct ei *, int, char *, int);
+ };
+ 
+ 
+ extern int		becker_debug;	/* debug flags */
+ extern struct ei	*becker_ctrl[];	/* BECKER control blocks	*/
+ 
+ 
+ /* From autoirq.c */
+ extern void		autoirq_setup(int waittime);
+ extern int		autoirq_report(int waittime);
+ 
+ /* Card probing routines. */
+ extern int		ac3200_probe(struct ei *ei);
+ extern int		at1500_probe(struct ei *ei);
+ extern int		at1700_probe(struct ei *ei);
+ extern int		atp_probe(struct ei *ei);
+ extern int		e2100_probe(struct ei *ei);
+ extern int		el1_probe(struct ei *);
+ extern int		el2_probe(struct ei *);
+ extern int		el3_probe(struct ei *);
+ extern int		el16_probe(struct ei *ei);
+ extern int		elplus_probe(struct ei *ei);
+ extern int		express_probe(struct ei *ei);
+ extern int		hp_probe(struct ei *);
+ extern int		ne_probe(struct ei *);
+ extern int		ultra_probe(struct ei *);
+ extern int		wd_probe(struct ei *);
+ extern int		znote_probe(struct ei *ei);
+ 
+ extern int		becker_auto_probe(struct ei *ei, int card);
+ extern int		becker_ioctl(struct inode *inode, struct file *file,
+ 				     unsigned int cmd, unsigned long arg);
+ extern void		becker_dprintf(int level, char *fmt, ...);
+ extern int		becker_dbg_ioctl(unsigned long arg, int level);
+ 
+ /* End of GLOBAL.H */
*** original/net/drv/becker/hp.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/hp.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,367 ----
+ /* hp.c: A HP LAN ethernet driver for linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 	Copyright 1993 United States Government as represented by the
+ 	Director, National Security Agency.	 This software may be used and
+ 	distributed according to the terms of the GNU Public License,
+ 	incorporated herein by reference.
+ 
+ 	This is a driver for the HP LAN adaptors.
+ 
+ 	The Author may be reached as becker@super.org or
+ 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/ioport.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ 
+ #define HP_IO_EXTENT	32
+ 
+ #define HP_DATAPORT	0x0c		/* "Remote DMA" data port. */
+ #define HP_ID		0x07
+ #define HP_CONFIGURE	0x08		/* Configuration register. */
+ #define	 HP_RUN		0x01		/* 1 == Run, 0 == reset. */
+ #define	 HP_IRQ		0x0E		/* Mask for software-configured IRQ line. */
+ #define	 HP_DATAON	0x10		/* Turn on dataport */
+ #define NIC_OFFSET	0x10		/* Offset the 8390 registers. */
+ 
+ #define HP_START_PG	0x00		/* First page of TX buffer */
+ #define HP_8BSTOP_PG	0x80		/* Last page +1 of RX ring */
+ #define HP_16BSTOP_PG	0xFF		/* Same, for 16 bit cards. */
+ 
+ 
+ #if 0
+ static char *version =
+ 	"hp.c:v0.99.15k 3/3/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ /* The map from IRQ number to HP_CONFIGURE register setting. */
+ /* My default is IRQ5	   0  1	 2  3  4  5  6	7  8  9 10 11 */
+ static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
+ 
+ 
+ /*
+  * Hard reset the card.  This used to pause for the same
+  * period that a 8390 reset command required, but that
+  * shouldn't be necessary.
+  */
+ static void
+ hp_reset(struct ei *ei)
+ {
+   int hp_base = ei->ioaddr;
+   int saved_config = inb_p(hp_base + HP_CONFIGURE);
+ 
+   DPRINTF((DBG_HP, "resetting the 8390 t=%ld...", jiffies));
+ 
+   outb_p(0x00, hp_base + HP_CONFIGURE);
+   ei->txing = 0;
+ 
+   /* Pause just a few cycles for the hardware reset to take place. */
+   SLOW_DOWN_IO ; SLOW_DOWN_IO;
+ 
+   outb_p(saved_config, hp_base + HP_CONFIGURE);
+ 
+   SLOW_DOWN_IO ; SLOW_DOWN_IO;
+ 
+   if ((inb_p(hp_base + NIC_OFFSET + EN0_ISR) & ENISR_RESET) == 0)
+ 	DPRINTF((DBG_HP, "%s: 8390 reset did not complete\n",
+ 						ei->dev->if_name));
+ 
+   DPRINTF((DBG_HP, "8390 reset done (%d)", jiffies));
+ }
+ 
+ 
+ /* This function resets the ethercard if something screws up. */
+ static void
+ hp_init_card(struct ei *ei)
+ {
+   int irq = ei->irq;
+ 
+   ei->chip_init(ei, 0);
+   outb_p(irqmap[irq & 0x0f] | HP_RUN, ei->ioaddr + HP_CONFIGURE);
+ }
+ 
+ 
+ /*
+  * Block input and output, similar to the Crynwr packet driver.
+  * If you are porting to a new ethercard look at the packet driver
+  * source for hints.  The HP LAN doesn't use shared memory -- we
+  * put the packet out through the "remote DMA" dataport.
+  */
+ static int
+ hp_input(struct ei *ei, int count, char *buf, int ring_offset)
+ {
+   int nic_base = ei->iobase;
+   int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+   int xfer_count = count;
+ 
+   outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+   outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+   outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+   outb_p(count >> 8, nic_base + EN0_RCNTHI);
+   outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+   outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+   outb_p(E8390_RREAD+E8390_START, nic_base);
+ 
+   if (ei->word16) {
+ 	insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
+ 	if (count & 0x01)
+ 		buf[count-1] =
+ 			inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
+   } else {
+ 	insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+   }
+ 
+   /* This is for the ALPHA version only, remove for later releases. */
+ #ifdef BECKER_DEBUG
+   if (becker_debug & (1 << DBG_HP)) {	/* DMA termination address check... */
+ 	int high = inb_p(nic_base + EN0_RSARHI);
+ 	int low = inb_p(nic_base + EN0_RSARLO);
+ 	int addr = (high << 8) + low;
+ 
+ 	/* Check only the lower 8 bits so we can ignore ring wrap. */
+ 	if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
+ 	DPRINTF((DBG_HP,
+ 	"%s: RX transfer address mismatch, 0x%4.4X vs. 0x%4.4X (actual)\n",
+ 		ei->dev->if_name, ring_offset + xfer_count, addr));
+   }
+ #endif
+   outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+   return(ring_offset + count);
+ }
+ 
+ 
+ static void
+ hp_output(struct ei *ei, int count, char *buf, int start_page)
+ {
+   int nic_base = ei->iobase;
+   int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+ 
+   outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ 
+   /*
+    * Round the count up for word writes.  Do we need to do this?
+    * What effect will an odd byte count have on the 8390?
+    * I should check someday.
+    */
+   if (ei->word16 && (count & 0x01)) count++;
+ 
+   /* We should already be in page 0, but to be safe... */
+   outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
+ 
+ #if HP_RW_BUGFIX
+   /*
+    * Handle the read-before-write bug the same way as the
+    * Crynwr packet driver -- the NatSemi method doesn't work.
+    */
+   outb_p(0x42, nic_base + EN0_RCNTLO);
+   outb_p(0,   nic_base + EN0_RCNTHI);
+   outb_p(0xff, nic_base + EN0_RSARLO);
+   outb_p(0x00, nic_base + EN0_RSARHI);
+   outb_p(E8390_RREAD+E8390_START, EN_CMD);
+ 
+   /* Make certain that the dummy read has occured. */
+   inb_p(0x61);
+   inb_p(0x61);
+ #endif
+ 
+   outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+   outb_p(count >> 8, nic_base + EN0_RCNTHI);
+   outb_p(0x00, nic_base + EN0_RSARLO);
+   outb_p(start_page, nic_base + EN0_RSARHI);
+ 
+   outb_p(E8390_RWRITE+E8390_START, nic_base);
+   if (ei->word16) {
+ 	/* Use the 'rep' sequence for 16 bit boards. */
+ 	outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
+   } else {
+ 	outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+   }
+ 
+   /*
+    * This is for the ALPHA version only, remove for later releases.
+    * DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken!
+    */
+ #ifdef BECKER_DEBUG
+   if (becker_debug & (1 << DBG_HP)) {	/* DMA termination address check... */
+ 	int high = inb_p(nic_base + EN0_RSARHI);
+ 	int low  = inb_p(nic_base + EN0_RSARLO);
+ 	int addr = (high << 8) + low;
+ 
+ 	if ((start_page << 8) + count != addr) {
+ 		DPRINTF((DBG_HP,
+ 			"%s: TX Transfer address mismatch, 0x%4.4X vs. 0x%4.4X\n",
+ 			ei->dev->if_name, (start_page << 8) + count, addr));
+ 	}
+   }
+ #endif
+   outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+ }
+ 
+ 
+ static int
+ hp_open(struct ei *ei)
+ {
+   ei->chip_open(ei);
+   return(0);
+ }
+ 
+ 
+ static int
+ hp_close(struct ei *ei)
+ {
+   DPRINTF((DBG_HP, "%s: shutting down ethercard\n", ei->dev->if_name));
+   ei->chip_init(ei, 0);
+ #if 0
+   NS8390_irq2ei_map[ei->irq] = NULL;
+ #endif
+ 
+   return(0);
+ }
+ 
+ 
+ int
+ hpprobe1(int ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int i, board_id;
+ 
+   /*
+    * Check for the HP physical address, 08 00 09 xx xx xx.
+    * This really isn't good enough: we may pick up HP LANCE
+    * boards also!  Avoid the lance 0x5757 signature.
+    */
+   if (inb(ioaddr) != 0x08 ||
+       inb(ioaddr+1) != 0x00 ||
+       inb(ioaddr+2) != 0x09 || inb(ioaddr + 14) == 0x57) return(-ENODEV);
+ 
+   /*
+    * Set up the parameters based on the board ID.
+    * If you have additional mappings, please mail them to
+    * becker@super.org (Donald Becker).
+    */
+   ei->tx_start_page = HP_START_PG;
+   ei->rx_start_page = HP_START_PG + TX_PAGES;
+   if ((board_id = inb(ioaddr + HP_ID)) & 0x80) {
+ 	ei->name = "HP27247";
+ 	ei->word16 = 1;
+ 	ei->stop_page = HP_16BSTOP_PG; /* safe (if small) value */
+   } else {
+ 	ei->name = "HP27250";
+ 	ei->word16 = 0;
+ 	ei->stop_page = HP_8BSTOP_PG;
+   }
+ 
+   DPRINTF((DBG_HP, "%s: %s (ID 0x%02X) at 0x%3X,",
+ 		ei->dev->if_name, ei->name, board_id, ioaddr));
+ 
+   /* Fetch the hardware address. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(ioaddr + i);
+ 	DPRINTF((DBG_HP, " %02.2X", sa.sa_data[i]));
+   }
+ 
+   /* Set the base address to point to the NIC, not the "real" base! */
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr + NIC_OFFSET;
+ 
+   /* Set up the chip-specific stuff. */
+   (void) NS8390_setup(ei);
+ 
+   /* Snarf the interrupt now.  Someday this could be moved to open(). */
+   if (ei->irq < 2) {
+       int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0 };
+       int irq_8list[] = { 7, 5, 3, 4, 9, 0 };
+       int *irqp = ei->word16 ? irq_16list : irq_8list;
+ 
+       do {
+ 		if (request_irq(ei->irq = *irqp, NULL) != -EBUSY) {
+ 			autoirq_setup(0);
+ 
+ 			/* Twinkle the interrupt, and check if it's seen. */
+ 			outb_p(irqmap[ei->irq] | HP_RUN, ioaddr + HP_CONFIGURE);
+ 			outb_p(0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
+ 
+ 			/* It's a good IRQ line! */
+ 			if (ei->irq == autoirq_report(0) &&
+ 			    request_irq(ei->irq, NULL) == 0) {
+ 				DPRINTF((DBG_HP, " selecting IRQ%d\n",
+ 								ei->irq));
+ 				break;
+ 			}
+ 		}
+ 	} while (*++irqp);
+ 
+ 	if (*irqp == 0) {
+ 		DPRINTF((DBG_HP, " no free IRQ lines\n"));
+ 		return(-EAGAIN);
+ 	}
+   } else {
+ 	if (ei->irq == 2) ei->irq = 9;
+ 	if (irqaction(ei->irq, ei->sigaction)) {
+ 		printk(" unable to get IRQ%d\n", ei->irq);
+ 		return(-EAGAIN);
+ 	}
+   }
+ 
+   snarf_region(ioaddr, HP_IO_EXTENT);
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = hp_reset;
+   ei->card_open = hp_open;
+   ei->card_close = hp_close;
+   ei->card_input = hp_input;
+   ei->card_output = hp_output;
+ 
+   /* Initialize the card. */
+   hp_init_card(ei);
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Probe for an HP LAN adaptor.
+  * Also initialize the card and fill in STATION_ADDR with
+  * the station address.
+  */
+ int
+ hp_probe(struct ei *ei)
+ {
+   static int hp_card_nr = 0;
+   int *port, ports[] = { 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "HP-LAN";
+   sprintf(ei->dev->if_name, "hp%d", hp_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(hpprobe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	if (check_region(*port, HP_IO_EXTENT)) continue;
+ 
+ 	/* Check if a card is present. */
+ 	if (hpprobe1(*port, ei) == 0) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/becker/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/ioctl.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,127 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		I/O control request handling functions.
+  *
+  * Version:	@(#)ioctl.c	1.3.1	04/05/94
+  *
+  * Authors:	Donald J. Becker, <becker@super.org>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		See the file called `LICENSE' about the legal status of
+  *		this software package.  This is important...
+  */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/fs.h>
+ #include <linux/tty.h>
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <asm/io.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ /* Check if the new MTU setting is OK. */
+ static int
+ becker_check_mtu(struct ifnet *dev, int new_mtu)
+ {
+   if ((new_mtu < ETH_ZLEN) || (new_mtu >= ETH_FRAME_LEN)) return(-EINVAL);
+   dev->if_mtu = new_mtu;
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ becker_check_probe(struct ei *ei, struct ei *newei)
+ {
+   struct ei eidev;
+   int i;
+ 
+   eidev = *ei;
+   *ei = *newei;
+ 
+   if ((i = becker_auto_probe(ei, BECARD_ANY)) == 0) return(0);
+   *ei = eidev;
+   return(i);
+ }
+ 
+ 
+ int
+ becker_ioctl(struct inode *inode, struct file *file,
+ 	     unsigned int cmd, unsigned long arg)
+ {
+   struct ddi tmp_ddi;
+   struct ifnet *dev = NULL;
+   struct ei *ei = NULL;
+   struct ei new_ei;
+   int err;
+ 
+   DPRINTF((DBG_DRV, "BECKER: ioctl(0x%X, 0x%X, 0x%X)\n", inode, cmd, arg));
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(becker_dbg_ioctl(arg, 1));
+ 
+ 	case DDIOCGCONF:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memset(&tmp_ddi, 0, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= BECKER_MAX_DEV) return(-ENODEV);
+ 		ei = becker_ctrl[dev->if_unit];
+ 		if (ei == NULL) return(-ENODEV);
+ 
+ 		tmp_ddi.io = ei->ioaddr;
+ 		tmp_ddi.irq = ei->irq;
+ 		tmp_ddi.memaddr = ei->membase;
+ 		tmp_ddi.memsize = ei->memsize;
+ 		tmp_ddi.auxio = ei->interface_num;
+ 		memcpy_tofs((void *)arg, &tmp_ddi, sizeof(struct ddi));
+ 		return(0);
+ 
+ 	case DDIOCSCONF:
+ 		err = verify_area(VERIFY_READ, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&tmp_ddi, (void *)arg, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= BECKER_MAX_DEV) return(-ENODEV);
+ 		ei = becker_ctrl[dev->if_unit];
+ 		if (ei == NULL) return(-ENODEV);
+ 
+ 		new_ei = *ei;
+ 		new_ei.ioaddr = tmp_ddi.io;
+ 		new_ei.irq = tmp_ddi.irq;
+ 		new_ei.membase = tmp_ddi.memaddr;
+ 		new_ei.memsize = tmp_ddi.memsize;
+ 		new_ei.interface_num = (unsigned char) tmp_ddi.auxio;
+ 		return(becker_check_probe(ei, &new_ei));
+ 		return(0);
+ 
+ 	case SIOCSIFMTU:
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= BECKER_MAX_DEV) return(-ENODEV);
+ 		if (becker_ctrl[dev->if_unit] == NULL) return(-ENODEV);
+ 		return(becker_check_mtu(dev, (int) arg));
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
*** original/net/drv/becker/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/main.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,367 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		BECKER Ethernet Driver Kit main program.
+  *
+  * Version:	@(#)main.c	1.3.0	03/30/94
+  *
+  * Authors:	Donald J. Becker, <becker@super.org>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		See the file called `LICENSE' about the legal status of
+  *		this software package.  This is important...
+  */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/fs.h>
+ #include <linux/tty.h>
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/in.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <asm/io.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #define VERSION		"1.3.0"
+ 
+ 
+ struct ei *becker_ctrl[BECKER_MAX_DEV];
+ 
+ 
+ static int
+ becker_fopen(struct inode *inode, struct file *file)
+ {
+   int minor;
+   struct ei *ei;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= BECKER_MAX_DEV) return(-ENODEV);
+   ei = becker_ctrl[minor];
+   if (ei == NULL) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ becker_fclose(struct inode *inode, struct file *file)
+ {
+   int minor;
+   struct ei *ei;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= BECKER_MAX_DEV) return;
+   ei = becker_ctrl[minor];
+   if (ei == NULL) return;
+ }
+ 
+ 
+ static struct file_operations becker_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   becker_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   becker_fopen,	/* OPEN		*/
+   becker_fclose	/* CLOSE	*/
+ };
+ 
+ 
+ static int
+ becker_open(struct ifnet *dev)
+ {
+   struct ei *ei;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	DPRINTF((DBG_DRV, "BECKER: %s: open: no control block !\n",
+ 							dev->if_name));
+ 	return(-ENODEV);
+   }
+   return(ei->card_open(ei));
+ }
+ 
+ 
+ static int
+ becker_close(struct ifnet *dev)
+ {
+   struct ei *ei;
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	DPRINTF((DBG_DRV, "BECKER: %s: close: no control block !\n",
+ 							dev->if_name));
+ 	return(-ENODEV);
+   }
+   return(ei->card_close(ei));
+ }
+ 
+ 
+ static int
+ becker_set_multi(struct ifnet *dev, int multi, void *args)
+ {
+   struct ei *ei;
+ 
+   DPRINTF((DBG_DRV, "%s: set_multi(%d, 0x%lX)\n",
+ 			dev->if_name, multi, (int) args));
+ 
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	DPRINTF((DBG_DRV, "BECKER: %s: set_multi: no control block !\n",
+ 							dev->if_name));
+ 	return(-ENODEV);
+   }
+ 
+   /* Does the driver support promisc modes? */
+   if (ei->chip_s_multi == NULL) return(-EINVAL);
+ 
+   /* Yes.  Tell it to go into the right mode. */
+   ei->chip_s_multi(ei, multi, args);
+ 
+   return(0);
+ }
+ 
+ 
+ int
+ becker_auto_probe(struct ei *ei, int card)
+ {
+   int found = 0;
+ 
+   /*
+    * If we need to probe for a single type of card.
+    * Perhaps we should make a compile-time table of
+    * this stuff, and simply walk the table? -FvK
+    */
+   if (card != BECARD_ANY) {
+ 	switch(card) {
+ #ifdef CONFIG_ULTRA	/* SMC Ultra */
+ 		case BECARD_ULTRA:
+ 			found = ultra_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_WD80x3	/* WD80x3 and SMC Elite */
+ 		case BECARD_WD:
+ 			found = wd_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_NE2000	/* Novell x000 and clones */
+ 		case BECARD_NE:
+ 			found = ne_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_HPLAN	/* HP HPLAN */
+ 		case BECARD_HP:
+ 			found = hp_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_EL1	/* 3Com EtherLink I (3c501) */
+ 		case BECARD_EL1:
+ 			found = el1_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_EL2	/* 3Com EtherLink II (3c503) */
+ 		case BECARD_EL2:
+ 			found = el2_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_EL3	/* 3Com EtherLink III (3c509) */
+ 		case BECARD_EL3:
+ 			found = el3_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_EL16	/* 3Com EtherLink 16 (3c507) */
+ 		case BECARD_EL16:
+ 			found = el16_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_ELPLUS	/* 3Com EtherLink Plus (3c505) */
+ 		case BECARD_ELPLUS:
+ 			found = elplus_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_AT1500
+ 		case BECARD_AT15:
+ 			found = at1500_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_AT1700
+ 		case BECARD_AT17:
+ 			found = at1700_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_ZNOTE	/* Zenith Z-Note and some IBM Thinkpads. */
+ 		case BECARD_ZNOTE:
+ 			found = znote_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_EEXPRESS	/* Intel EtherExpress */
+ 		case BECARD_EEXP:
+ 			found = express_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_AC3200	/* Ansel Communications EISA 3200. */
+ 		case BECARD_AC3200:
+ 			found = ac3200_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_E2100	/* Cabletron E21xx series. */
+ 		case BECARD_E2100:
+ 			found = e2100_probe(ei);
+ 			break;
+ #endif
+ #ifdef CONFIG_ATP
+ 		case BECARD_ATP:
+ 			found = atp_probe(ei);
+ 			break;
+ #endif
+ 		default:
+ 			printk("unknown card type %d, cannot probe\n", card);
+ 	}
+   } else {
+ 	/* Probe for the various types of supported cards. */
+ 	found = 0;
+ #ifdef CONFIG_ULTRA
+ 	found = ultra_probe(ei);
+ #endif
+ #ifdef CONFIG_WD80x3
+ 	if (found < 0) found = wd_probe(ei);
+ #endif
+ #ifdef CONFIG_NE2000
+ 	if (found < 0) found = ne_probe(ei);
+ #endif
+ #ifdef CONFIG_HPLAN
+ 	if (found < 0) found = hp_probe(ei);
+ #endif
+ #ifdef CONFIG_EL1
+ 	if (found < 0) found = el1_probe(ei);
+ #endif
+ #ifdef CONFIG_EL2
+ 	if (found < 0) found = el2_probe(ei);
+ #endif
+ #ifdef CONFIG_EL3
+ 	if (found < 0) found = el3_probe(ei);
+ #endif
+ #ifdef CONFIG_EL16
+ 	if (found < 0) found = el16_probe(ei);
+ #endif
+ #ifdef CONFIG_ELPLUS
+ 	if (found < 0) found = elplus_probe(ei);
+ #endif
+ #ifdef CONFIG_AT1500
+ 	if (found < 0) found = at1500_probe(ei);
+ #endif
+ #ifdef CONFIG_AT1700
+ 	if (found < 0) found = at1700_probe(ei);
+ #endif
+ #ifdef CONFIG_ZNOTE
+ 	if (found < 0) found = znote_probe(ei);
+ #endif
+ #ifdef CONFIG_EEXPRESS
+ 	if (found < 0) found = express_probe(ei);
+ #endif
+ #ifdef CONFIG_AC3200
+ 	if (found < 0) found = ac3200_probe(ei);
+ #endif
+ #ifdef CONFIG_E2100
+ 	if (found < 0) found = e2100_probe(ei);
+ #endif
+ #ifdef CONFIG_ATP
+ 	if (found < 0) found = atp_probe(ei);
+ #endif
+   }
+ 
+   /* Return the result... */
+   return(found);
+ }
+ 
+ 
+ /* This is the main entry point of this driver. */
+ int
+ becker_init(struct ddi *ddi)
+ {
+   static int unit_nr = 0;
+   static int becker_set_up = 0;
+   struct ei *ei;
+   struct ifnet *dev;
+   int i;
+ 
+   /* Initialize the driver if this is the first call. */
+   if (becker_set_up++ == 0) {
+ 	for(i = 0; i < BECKER_MAX_DEV; i++) becker_ctrl[i] = NULL;
+ 	printk("%s: version %s: ", ddi->name, VERSION);
+ 	if (ddi->major != 0) {
+ 		if (register_chrdev(ddi->major, ddi->name, &becker_fops) < 0) {
+ 			printk("cannot register device %d!\n", ddi->major);
+ 			return(-EINVAL);
+ 		}
+ 		printk("device %d/%d: ", ddi->major, ddi->minor);
+ 	}
+ 	printk("OK\n");
+   }
+ 
+   /* Allocate and initialize a Control Block. */
+   ei = (struct ei *) kmalloc(sizeof(struct ei), GFP_KERNEL);
+   if (ei == NULL) {
+ 	printk("%s: no memory for control block !\n", ddi->name);
+ 	return(-ENOMEM);
+   }
+   memset(ei, 0, sizeof(struct ei));
+   ei->name = ddi->name;
+   ei->ioaddr = ddi->io;
+   ei->irq = ddi->irq;
+   ei->membase = (unsigned long) ddi->memaddr;
+   ei->memsize = (unsigned int) ddi->memsize;
+   ei->interface_num = (unsigned char) ddi->auxio;
+   becker_ctrl[unit_nr] = ei;
+ 
+   /* Allocate and initialize a NET interface. */
+   if ((dev = dev_create(NULL)) == NULL) {
+ 	printk("%s: no memory for interface descriptor!\n", ddi->name);
+ 	kfree_s(ei, sizeof(struct ei));
+ 	return(-ENOMEM);
+   }
+   dev->if_unit = unit_nr;
+   ei->dev = dev;
+ 
+   dev->open = becker_open;
+   dev->close = becker_close;
+   dev->ioctl = becker_ioctl;
+   dev->set_multi = becker_set_multi;
+ 
+   /*
+    * Try to detect the various cards here.  This should result in
+    * setting up an interface name, and, where needed, setting up
+    * the real I/O address and such, as found by the auto-probing
+    * functions.
+    */
+   if ((i = becker_auto_probe(ei, ddi->nunits)) < 0) {
+ 	(void) dev_free(dev);
+ 	return(i);
+   }
+ 
+   /* We found a device, or it was not yet set. */
+   if (ei->ioaddr == DDI_IOANY) {
+ 	printk("%s: %s [NOT SET]\n", dev->if_name, ei->name);
+   } else {
+ 	printk("%s: %s (IO=0x%X IRQ=%d MEM=0x%X ADDR=%s)\n",
+ 		dev->if_name, ei->name, ei->ioaddr,
+ 		ei->irq, ei->membase, dev->mac_sprint(dev->mac_aget(dev)));
+   }
+ 
+   /* This interface is busy. */
+   unit_nr++;
+ 
+   /* All done... */
+   return(0);
+ }
*** original/net/drv/becker/ne.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/ne.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,487 ----
+ /* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+ /*
+     Written 1992,1993 by Donald Becker.
+ 
+     Copyright 1993 United States Government as represented by the
+     Director, National Security Agency.  This software may be used and
+     distributed according to the terms of the GNU Public License,
+     incorporated herein by reference.
+ 
+     This driver should work with many 8390-based ethernet boards.  Currently
+     it support the NE1000, NE2000, clones, and some Cabletron products.
+ 
+     The Author may be reached as becker@super.org or
+     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+ 
+ /* Routines for the NatSemi-based designs (NE[12]000). */
+ 
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/ioport.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ 
+ #define NE_BASE		(ei->iobase)
+ #define NE_CMD	 	0x00
+ #define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+ #define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+ 
+ #define NE1SM_START_PG	0x20	/* First page of TX buffer */
+ #define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
+ #define NESM_START_PG	0x40	/* First page of TX buffer */
+ #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+ 
+ 
+ #if 0
+ static char *version =
+ 	"ne.c:v0.99-15k 3/3/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ /*
+  * Hard reset the card.  This used to pause for the same
+  * period that a 8390 reset command required, but that
+  * shouldn't be necessary.
+  */
+ static void
+ ne_reset(struct ei *ei)
+ {
+   int tmp = inb_p(NE_BASE + NE_RESET);
+   int reset_start_time = jiffies;
+ 
+   DPRINTF((DBG_NE, "resetting the 8390 t=%ld...", jiffies));
+   ei->txing = 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) {
+ 		DPRINTF((DBG_NE, "%s: ne_reset() did not complete\n",
+ 							ei->dev->if_name));
+ 		break;
+ 	}
+   }
+ }
+ 
+ 
+ /*
+  * Block input and output, similar to the Crynwr packet driver.
+  * If you are porting to a new ethercard look at the packet driver
+  * source for hints.  The NEx000 doesn't share it on-board packet
+  * memory -- you have to put the packet out through the "remote DMA"
+  * dataport using outb.
+  */
+ static int
+ ne_input(struct ei *ei, int count, char *buf, int ring_offset)
+ {
+   int xfer_count = count;
+   int nic_base = ei->iobase;
+ 
+   if (ei->dmaing) {
+ 	DPRINTF((DBG_NE, "%s: DMAing conflict in ne_block_input."
+ 		   "[DMAstat:%1X][irqlock:%1X]\n",
+ 		   ei->dev->if_name, ei->dmaing, ei->irqlock));
+ 	return(0);
+   }
+ 
+   ei->dmaing |= 0x01;
+   outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + NE_CMD);
+   outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+   outb_p(count >> 8, nic_base + EN0_RCNTHI);
+   outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+   outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+   outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+   if (ei->word16) {
+ 	insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ 	if (count & 0x01) {
+ 		buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+ 		xfer_count++;
+ 	}
+   } else {
+ 	insb(NE_BASE + NE_DATAPORT, buf, count);
+   }
+ 
+   /*
+    * This was for the ALPHA version only, but enough people have
+    * encountering problems that it is still here.  If you see
+    * this message you either 1) have an slightly imcompatible clone
+    * or 2) have noise/speed problems with your bus.
+    */
+ #ifdef BECKER_DEBUG
+   if (becker_debug & (1 << DBG_NE)) {	/* DMA termination address check... */
+ 	int addr, tries = 20;
+ 	do {
+ 		/*
+ 		 * DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ 		 * -- it's broken! Check the "DMA" address instead.
+ 		 */
+ 		int high = inb_p(nic_base + EN0_RSARHI);
+ 		int low = inb_p(nic_base + EN0_RSARLO);
+ 		addr = (high << 8) + low;
+ 		if (((ring_offset + xfer_count) & 0xff) == low) break;
+ 	} while (--tries > 0);
+ 
+ 	if (tries <= 0)
+ 		DPRINTF((DBG_NE, "%s: RX transfer address mismatch,"
+ 			"0x%4.4X (expected) vs. 0x%4.4X (actual)\n",
+ 			ei->dev->if_name, ring_offset + xfer_count, addr));
+   }
+ #endif
+   ei->dmaing &= ~0x01;
+   return(ring_offset + count);
+ }
+ 
+ 
+ static void
+ ne_output(struct ei *ei, int count, char *buf, int start_page)
+ {
+   int nic_base = NE_BASE;
+ #ifdef BECKER_DEBUG
+   int retries = 0;
+ #endif
+ 
+   /*
+    * Round the count up for word writes.  Do we need to do this?
+    * What effect will an odd byte count have on the 8390?
+    * I should check someday.
+    */
+   if (ei->word16 && (count & 0x01)) count++;
+ 
+   if (ei->dmaing) {
+ 	DPRINTF((DBG_NE, "%s: DMAing conflict in ne_block_output."
+ 		"[DMAstat:%1X][irqlock:%1X]\n",
+ 		ei->dev->if_name, ei->dmaing, ei->irqlock));
+ 	return;
+   }
+   ei->dmaing |= 0x02;
+ 
+   /* We should already be in page 0, but to be safe... */
+   outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+ 
+ #ifdef BECKER_DEBUG
+ retry:
+ #endif
+ #if NE_RW_BUGFIX
+   /*
+    * Handle the read-before-write bug the same way as the
+    * Crynwr packet driver -- the NatSemi method doesn't work.
+    * Actually this doesn't aways work either, but if you have
+    * problems with your NEx000 this is better than nothing!
+    */
+   outb_p(0x42, nic_base + EN0_RCNTLO);
+   outb_p(0x00, nic_base + EN0_RCNTHI);
+   outb_p(0x42, nic_base + EN0_RSARLO);
+   outb_p(0x00, nic_base + EN0_RSARHI);
+   outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ 
+   /* Make certain that the dummy read has occured. */
+   SLOW_DOWN_IO;
+   SLOW_DOWN_IO;
+   SLOW_DOWN_IO;
+ #endif
+ 
+   /* Now the normal output. */
+   outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+   outb_p(count >> 8, nic_base + EN0_RCNTHI);
+   outb_p(0x00, nic_base + EN0_RSARLO);
+   outb_p(start_page, nic_base + EN0_RSARHI);
+ 
+   outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+   if (ei->word16) {
+ 	outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+   } else {
+ 	outsb(NE_BASE + NE_DATAPORT, buf, count);
+   }
+ 
+   /*
+    * This was for the ALPHA version only, but enough people have
+    * encountering problems that it is still here.
+    */
+ #ifdef BECKER_DEBUG
+   if (becker_debug & (1 << DBG_NE)) {	/* DMA termination address check... */
+ 	int addr, tries = 20;
+ 	do {
+ 		/*
+ 		 * DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ 		 * -- it's broken! Check the "DMA" address instead.
+ 		 */
+ 		int high = inb_p(nic_base + EN0_RSARHI);
+ 		int low = inb_p(nic_base + EN0_RSARLO);
+ 
+ 		addr = (high << 8) + low;
+ 		if ((start_page << 8) + count == addr) break;
+ 	} while (--tries > 0);
+ 
+ 	if (tries <= 0) {
+ 		DPRINTF((DBG_NE, "%s: TX transfer address mismatch,"
+ 			"0x%4.4X (expected) vs. 0x%4.4X (actual)\n",
+ 			ei->dev->if_name, (start_page << 8) + count, addr));
+ 		if (retries++ == 0) goto retry;
+ 	}
+   }
+ #endif
+   ei->dmaing &= ~0x02;
+ }
+ 
+ 
+ static int
+ ne_open(struct ei *ei)
+ {
+   ei->chip_open(ei);
+   return(0);
+ }
+ 
+ 
+ static int
+ ne_close(struct ei *ei)
+ {
+   DPRINTF((DBG_NE, "%s: shutting down ethercard\n", ei->dev->if_name));
+   ei->chip_init(ei, 0);
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ neprobe1(int ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   unsigned char SA_prom[32];
+   int wordlength = 2;
+   int i;
+   char *name;
+   int start_page, stop_page;
+   int neX000, ctron, dlink, dfi;
+   int reg0 = inb(ioaddr);
+ 
+   if (reg0 == 0xFF) return(-ENODEV);
+ 
+   /* Do a quick preliminary check that we have a 8390. */
+   {
+ 	int regd;
+ 
+ 	outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ 	regd = inb_p(ioaddr + 0x0d);
+ 	outb_p(0xff, ioaddr + 0x0d);
+ 	outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ 	inb_p(ioaddr + EN0_COUNTER0); /* clear the counter by reading */
+ 	if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ 		outb_p(reg0, ioaddr);
+ 		outb(regd, ioaddr + 0x0d);	/* restore the old values */
+ 		return(-ENODEV);
+ 	}
+   }
+   DPRINTF((DBG_NE, "NEx000 probe at 0x%3X:", ioaddr));
+ 
+   /*
+    * Read the 16 bytes of station address prom, returning 1 for
+    * an eight-bit interface and 2 for a 16-bit interface.
+    * We must first initialize registers, similar to NS8390_init(ei, 0).
+    * We can't reliably read the SAPROM address without this.
+    * (I learned the hard way!).
+    */
+   {
+ 	struct {
+ 		unsigned char value, offset;
+ 	} program_seq[] = {
+ 	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* select page 0 */
+ 	    {0x48,	EN0_DCFG},	/* set byte-wide (0x48) access */
+ 	    {0x00,	EN0_RCNTLO},	/* clear the count regs */
+ 	    {0x00,	EN0_RCNTHI},
+ 	    {0x00,	EN0_IMR},	/* mask completion IRQ */
+ 	    {0xFF,	EN0_ISR},
+ 	    {E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+ 	    {E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode */
+ 	    {32,	EN0_RCNTLO},
+ 	    {0x00,	EN0_RCNTHI},
+ 	    {0x00,	EN0_RSARLO},	/* DMA starting at 0x0000 */
+ 	    {0x00,	EN0_RSARHI},
+ 	    {E8390_RREAD+E8390_START, E8390_CMD},
+ 	};
+ 
+ 	for (i = 0; i < sizeof(program_seq) / sizeof(program_seq[0]); i++)
+ 		outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+   }
+ 
+   for(i = 0; i < 32 /*sizeof(SA_prom)*/; i += 2) {
+ 	SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+ 	SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+ 	if (SA_prom[i] != SA_prom[i + 1]) wordlength = 1;
+   }
+ 
+   if (wordlength == 2) {
+ 	/* We must set the 8390 for word mode. */
+ 	outb_p(0x49, ioaddr + EN0_DCFG);
+ 
+ 	/*
+ 	 * We used to reset the ethercard here, but it doesn't
+ 	 * seem to be necessary.
+ 	 */
+ 
+ 	/* Un-double the SA_prom values. */
+ 	for (i = 0; i < 16; i++)
+ 	    SA_prom[i] = SA_prom[i + i];
+   }
+ 
+ #if NE_SHOW_ALL_SAPROM
+   /* If your ethercard isn't detected define this to see the SA_PROM. */
+   for(i = 0; i < sizeof(SA_prom); i++)
+ 	printk(" %2.2X", SA_prom[i]);
+ #else
+   /* Fetch the hardware address. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = SA_prom[i];
+ 	DPRINTF((DBG_NE, " %2.2X", sa.sa_data[i]));
+   }
+ #endif
+ 
+   neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+   ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+   dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
+   dfi = (SA_prom[0] == 'D' && SA_prom[1] == 'F' && SA_prom[2] == 'I');
+ 
+   /* Set up the rest of the parameters. */
+   if (neX000 || dlink || dfi) {
+ 	if (wordlength == 2) {
+ 		name = dlink ? "DE200" : "NE2000";
+ 		start_page = NESM_START_PG;
+ 		stop_page = NESM_STOP_PG;
+ 	} else {
+ 		name = dlink ? "DE100" : "NE1000";
+ 		start_page = NE1SM_START_PG;
+ 		stop_page = NE1SM_STOP_PG;
+ 	}
+   } else if (ctron) {
+ 	name = "Cabletron";
+ 	start_page = 0x01;
+ 	stop_page = (wordlength == 2) ? 0x40 : 0x20;
+   } else {
+ 	DPRINTF((DBG_NE, " not found."));
+ 	return(-ENODEV);
+   }
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr;
+ 
+   if (ei->irq < 2) {
+ 	autoirq_setup(0);
+ 	outb_p(0x50, ioaddr + EN0_IMR);	/* enable one interrupt */
+ 	outb_p(0x00, ioaddr + EN0_RCNTLO);
+ 	outb_p(0x00, ioaddr + EN0_RCNTHI);
+ 	outb_p(E8390_RREAD+E8390_START, ioaddr); /* trigger it... */
+ 	outb_p(0x00, ioaddr + EN0_IMR); 		/* mask it again */
+ 	ei->irq = autoirq_report(0);
+ 	DPRINTF((DBG_NE, " autoIRQ%d", ei->irq));
+   } else if (ei->irq == 2) {
+ 	/*
+ 	 * Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ 	 * or don't know which one to set.
+ 	 */
+ 	ei->irq = 9;
+   }
+ 
+   /* Set up the chip-specific stuff. */
+   (void) NS8390_setup(ei);
+ 
+   /*
+    * Snarf the interrupt now.  There's no point in waiting since
+    * we cannot share and the board will usually be enabled.
+    */
+   {
+ 	int irqval = irqaction(ei->irq, ei->sigaction);
+ 	if (irqval) {
+ 		printk(" unable to get IRQ%d (irqval=%d)\n",
+ 						ei->irq, irqval);
+ 		return(-EAGAIN);
+ 	}
+   }
+ 
+   snarf_region(ioaddr, 32);
+ 
+   DPRINTF((DBG_NE, "%s: %s at 0x%3X, using IRQ%d\n",
+ 			ei->dev->if_name, name, ioaddr, ei->irq));
+ 
+   ei->name = name;
+   ei->tx_start_page = start_page;
+   ei->stop_page = stop_page;
+   ei->word16 = (wordlength == 2);
+ 
+   ei->rx_start_page = start_page + TX_PAGES;
+ #ifdef PACKETBUF_MEMSIZE
+   /* Allow the packet buffer size to be overridden by know-it-alls. */
+   ei->stop_page = ei->tx_start_page + PACKETBUF_MEMSIZE;
+ #endif
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = ne_reset;
+   ei->card_open = ne_open;
+   ei->card_close = ne_close;
+   ei->card_input = ne_input;
+   ei->card_output = ne_output;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Probe for various non-shared-memory ethercards.
+  *
+  * NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+  * buffer memory space.  NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+  * the SAPROM, while other supposed NE2000 clones must be detected by their
+  * SA prefix.
+  *
+  * Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+  * mode results in doubled values, which can be detected and compansated for.
+  *
+  * The probe is also responsible for initializing the card and filling
+  * in the 'dev' and 'ei_status' structures.
+  *
+  * We use the minimum memory size for some ethercard product lines, if we
+  * can't distinguish models.  You can increase the packet buffer size by
+  * setting PACKETBUF_MEMSIZE.  Reported Cabletron packet buffer locations
+  * are:
+  *
+  * E1010   starts at 0x100 and ends at 0x2000.
+  * E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+  * E2010	 starts at 0x100 and ends at 0x4000.
+  * E2010-x starts at 0x100 and ends at 0xffff.
+  */
+ int
+ ne_probe(struct ei *ei)
+ {
+   static int ne_card_nr = 0;
+   int *port, ports[] = { 0x300, 0x280, 0x320, 0x340, 0x360, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "NEx000";
+   sprintf(ei->dev->if_name, "ne%d", ne_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(neprobe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	if (check_region(*port, 32)) continue;
+ 
+ 	/* Check if a card is present. */
+ 	if (neprobe1(*port, ei) == 0) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/becker/ns8390.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/ns8390.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,691 ----
+ /* 8390.c: A general NS8390 ethernet driver core for linux. */
+ /*
+   Written 1992,1993 by Donald Becker.
+   
+   Copyright 1993 United States Government as represented by the
+   Director, National Security Agency.	 This software may be used and
+   distributed according to the terms of the GNU Public License,
+   incorporated herein by reference.
+   
+   This is the chip-specific code for many 8390-based ethernet adaptors.
+   This is not a complete driver, it must be combined with board-specific
+   code such as ne.c, wd.c, 3c503.c, etc.
+   
+   The Author may be reached as becker@super.org or
+   C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+   */
+ /*
+   Braindamage remaining:
+   Much of this code should be cleaned up post-1.00, but it has been
+   extensively beta tested in the current form.
+   
+   Sources:
+   The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+   The NE* programming info came from the Crynwr packet driver, and figuring
+   out that the those boards are similar to the NatSemi evaluation board
+   described in AN-729.	Thanks NS, no thanks to Novell/Eagle.
+   */
+ 
+ /* These are the operational function interfaces to board-specific routines.
+ 	void reset_8390(struct device *dev)
+ 		Resets the board associated with DEV, including a hardware reset of
+ 		the 8390.  This is only called when there is a transmit timeout, and
+ 		it is always followed by 8390_init().
+ 	void block_output(struct device *dev, int count, const unsigned char *buf,
+ 					  int start_page)
+ 		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The
+ 		"page" value uses the 8390's 256-byte pages.
+ 	int block_input(struct device *dev, int count, char *buf, int ring_offset)
+ 		Read COUNT bytes from the packet buffer into BUF.  Start reading from
+ 		RING_OFFSET, the address as the 8390 sees it.  The first read will
+ 		always be the 4 byte, page aligned 8390 header.  *If* there is a
+ 		subsequent read, it will be of the rest of the packet.
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <linux/types.h>
+ #include <linux/ptrace.h>
+ #include <linux/string.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/in.h>
+ #include <linux/interrupt.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ 
+ #if 0
+ static char *version =
+     "8390.c:v0.99-15e 2/16/94 Donald Becker (becker@super.org)\n";
+ #endif
+ struct ei *NS8390_irq2ei_map[16];
+ 
+ 
+ /*
+  * This page of functions should be 8390 generic.
+  * Follow National Semi's recommendations for initializing
+  * the "NIC".
+  */
+ static void
+ NS8390_init(struct ei *ei, int startp)
+ {
+   unsigned char *ptr;
+   int e8390_base = ei->iobase;
+   int i;
+   int endcfg = ei->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+     
+   /* Follow National Semi's recommendations for initing the DP83902. */
+   outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
+   outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */
+ 
+   /* Clear the remote byte count registers. */
+   outb_p(0x00, e8390_base + EN0_RCNTLO);
+   outb_p(0x00, e8390_base + EN0_RCNTHI);
+ 
+   /* Set to monitor and loopback mode -- this is vital!. */
+   outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+   outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ 
+   /* Set the transmit page and receive ring. */
+   outb_p(ei->tx_start_page, e8390_base + EN0_TPSR);
+   ei->tx1 = ei->tx2 = 0;
+   outb_p(ei->rx_start_page, e8390_base + EN0_STARTPG);
+   outb_p(ei->stop_page-1, e8390_base + EN0_BOUNDARY);
+ 				/* 3c503 says 0x3f,NS says 0x26*/
+ 
+   ei->current_page = ei->rx_start_page;		/* assert boundary+1 */
+   outb_p(ei->stop_page, e8390_base + EN0_STOPPG);
+ 
+   /* Clear the pending interrupts and mask. */
+   outb_p(0xFF, e8390_base + EN0_ISR);
+   outb_p(0x00, e8390_base + EN0_IMR);
+     
+   /*
+    * Copy the station address into the DS8390 registers,
+    * and set the multicast hash bitmap to receive all multicasts.
+    */
+   ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+   cli();
+   outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
+   for(i = 0; i < ETH_ALEN; i++) {
+ 	outb_p(ptr[i], e8390_base + EN1_PHYS + i);
+   }
+ 
+   /*
+    * Initialize the multicast list to accept-all.
+    * If we enable multicast the higher levels can do the filtering.
+    */
+   for(i = 0; i < 8; i++)
+ 	outb_p(0xff, e8390_base + EN1_MULT + i);
+     
+   outb_p(ei->rx_start_page, e8390_base + EN1_CURPAG);
+   outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
+   sti();
+ 
+   if (startp) {
+ 	outb_p(0xff, e8390_base + EN0_ISR);
+ 	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ 	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
+ 	outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ 
+ 	/* 3c503 TechMan says rxconfig only after the NIC is started. */
+ 	outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on,  */
+   }
+ }
+ 
+ 
+ /*
+  * Trigger a transmit start, assuming the length is valid.
+  */
+ static void
+ NS8390_trigger_send(struct ei *ei, unsigned int length, int start_page)
+ {
+   int e8390_base = ei->iobase;
+     
+   ei->txing = 1;
+   outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
+     
+   if (inb_p(e8390_base) & E8390_TRANS) {
+ 	DPRINTF((DBG_8390,
+ 		"%s: trigger_send() called with the transmitter busy.\n",
+ 			   				ei->dev->if_name));
+ 	return;
+   }
+   outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+   outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+   outb_p(start_page, e8390_base + EN0_TPSR);
+   outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
+ }
+ 
+ 
+ static int
+ NS8390_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   int e8390_base;
+   struct ei *ei;
+   int length, send_length;
+     
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in NS8390_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   e8390_base = ei->iobase;
+ 
+   /*
+    * We normally shouldn't be called if dev->tbusy is set, but the
+    * existing code does anyway.
+    * If it has been too long (> 100 or 150ms.) since the last Tx we
+    * assume the board has died and kick it.
+    */
+ 
+   if (dev->if_tbusy) {	/* Do timeouts, just like the 8003 driver. */
+ 	int txsr = inb(e8390_base+EN0_TSR), isr;
+ 	int tickssofar = jiffies - dev->if_txtimer;
+ 
+ 	if (tickssofar < 10 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
+ 		return(1);
+ 	}
+ 
+ 	isr = inb(e8390_base+EN0_ISR);
+ 	printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
+ 						dev->if_name, txsr, isr);
+ 
+ 	/* Does the 8390 thinks it has posted an interrupt? */
+ 	if (isr)
+ 		printk("%s: Possible IRQ conflict on IRQ%d?\n",
+ 					dev->if_name, ei->irq);
+ 	  else {
+ 		/* The 8390 probably hasn't gotten on the cable yet. */
+ 		printk("%s: Possible network cable problem?\n", dev->if_name);
+ 	}
+ 
+ 	/* Try to restart the card.  Perhaps the user has fixed something. */
+ 	ei->card_init(ei);
+ 	ei->chip_init(ei, 1);
+ 	dev->if_txtimer = jiffies;
+ 	printk("\n");
+   }
+     
+   /*
+    * Sending a NULL skb means some higher layer thinks we've missed an
+    * tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+    * itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   length = skb->len;
+   if (skb->len <= 0) return(0);
+ 
+   /* Block a timer-based transmit from overlapping. */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0) {
+ 	printk("%s: Transmitter access conflict.\n", dev->if_name);
+ 	return(1);
+   }
+ 
+   /* Mask interrupts from the ethercard. */
+   outb(0x00, e8390_base + EN0_IMR);
+   ei->irqlock = 1;
+ 
+   send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+ 
+   if (ei->pingpong) {
+ 	int output_page;
+ 
+ 	if (ei->tx1 == 0) {
+ 		output_page = ei->tx_start_page;
+ 		ei->tx1 = send_length;
+ #ifdef BECKER_DEBUG
+ 		if (ei->tx2 > 0)
+ 			DPRINTF((DBG_8390,
+ 			"%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+ 			dev->if_name, ei->tx2, ei->lasttx, ei->txing));
+ #endif
+ 	} else if (ei->tx2 == 0) {
+ 		output_page = ei->tx_start_page + 6;
+ 		ei->tx2 = send_length;
+ #ifdef BECKER_DEBUG
+ 		if (ei->tx1 > 0)
+ 			DPRINTF((DBG_8390,
+ 			"%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+ 			dev->if_name, ei->tx1, ei->lasttx, ei->txing));
+ #endif
+ 	} else {	/* We should never get here. */
+ 		DPRINTF((DBG_8390,
+ 			"%s: No packet buffer space for ping-pong use.\n",
+ 				dev->if_name));
+ 		ei->irqlock = 0;
+ 		dev->if_tbusy = 1;
+ 		outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ 		return(1);
+ 	}
+ 
+ 	ei->card_output(ei, length, skb->raw, output_page);
+ 	if (! ei->txing) {
+ 		NS8390_trigger_send(ei, send_length, output_page);
+ 		dev->if_txtimer = jiffies;
+ 		if (output_page == ei->tx_start_page) {
+ 			ei->tx1 = -1;
+ 			ei->lasttx = -1;
+ 		} else {
+ 			ei->tx2 = -1;
+ 			ei->lasttx = -2;
+ 		}
+ 		ei->txing = 1;
+ 	} else
+ 		ei->txqueue++;
+ 		dev->if_tbusy = (ei->tx1 && ei->tx2);
+   } else {  /* No pingpong, just a single Tx buffer. */
+ 		ei->card_output(ei, length, skb->raw, ei->tx_start_page);
+ 		NS8390_trigger_send(ei, send_length, ei->tx_start_page);
+ 		dev->if_txtimer = jiffies;
+ 		dev->if_tbusy = 1;
+   }
+ 
+   /* Turn 8390 interrupts back on. */
+   ei->irqlock = 0;
+   outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ 
+   return(0);
+ }
+ 
+ 
+ /* We have a good packet(s), get it/them out of the buffers. */
+ static void
+ NS8390_receive(struct ei *ei)
+ {
+   int e8390_base = ei->iobase;
+   int rxing_page, this_frame, next_frame, current_offset;
+   int rx_pkt_count = 0;
+   struct e8390_pkt_hdr rx_frame;
+   int num_rx_pages = ei->stop_page-ei->rx_start_page;
+     
+   while (++rx_pkt_count < 10) {
+ 	int pkt_len;
+ 		
+ 	/* Get the rx page (incoming packet pointer). */
+ 	outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+ 	rxing_page = inb_p(e8390_base + EN1_CURPAG);
+ 	outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+ 		
+ 	/* Remove one frame from the ring.  Boundary is alway a page behind. */
+ 	this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+ 	if (this_frame >= ei->stop_page) this_frame = ei->rx_start_page;
+ 
+ 	/*
+ 	 * Someday we'll omit the previous, iff we never get this message.
+ 	 * (There is at least one clone claimed to have a problem.)
+ 	 */
+ #ifdef BECKER_DEBUG
+ 	if (this_frame != ei->current_page)
+ 		DPRINTF((DBG_8390,
+ 		"%s: mismatched read page pointers %2x vs %2x.\n",
+ 			   ei->dev->if_name, this_frame, ei->current_page));
+ #endif
+ 
+ 	if (this_frame == rxing_page)	/* Read all the frames? */
+ 		break;				/* Done for now */
+ 	
+ 	current_offset = this_frame << 8;
+ 	ei->card_input(ei, sizeof(rx_frame),
+ 			   (char *)&rx_frame, current_offset);
+ 	
+ 	pkt_len = rx_frame.count - sizeof(rx_frame);
+ 	next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+ 	
+ 	/*
+ 	 * Check for bogosity warned by 3c503 book: the status byte
+ 	 * is never written.  This happened a lot during testing!
+ 	 * This code should be cleaned up someday.
+ 	 */
+ 	if (rx_frame.next != next_frame &&
+ 	    rx_frame.next != next_frame + 1 &&
+ 	    rx_frame.next != next_frame - num_rx_pages &&
+ 	    rx_frame.next != next_frame + 1 - num_rx_pages) {
+ 		ei->current_page = rxing_page;
+ 		outb(ei->current_page-1, e8390_base+EN0_BOUNDARY);
+ 		ei->dev->if_stats->rx_errors++;
+ 		continue;
+ 	}
+ 
+ 	if (pkt_len < 60  ||  pkt_len > 1518) {
+ 		DPRINTF((DBG_8390,
+ 		"%s: bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+ 			ei->dev->if_name, rx_frame.count,
+ 			rx_frame.status, rx_frame.next));
+ 		ei->dev->if_stats->rx_errors++;
+ 	} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
+ 		struct sk_buff *skb;
+ 			
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ #ifdef BECKER_DEBUG
+ 			DPRINTF((DBG_8390,
+ 				"%s: Couldn't allocate a sk_buff of size %d.\n",
+ 				ei->dev->if_name, pkt_len));
+ #endif
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		} else {
+ 			skb->dev = ei->dev;
+ 			
+ 			ei->card_input(ei, pkt_len, skb->raw,
+ 					   current_offset + sizeof(rx_frame));
+ 			if (dev_rint(ei->dev, skb, pkt_len)) {
+ 				ei->dev->if_stats->rx_dropped++;
+ 				break;
+ 			}
+ 			ei->dev->if_stats->rx_packets++;
+ 		}
+ 	} else {
+ 		int errs = rx_frame.status;
+ 
+ #ifdef BECKER_DEBUG
+ 		DPRINTF((DBG_8390,
+ 			"%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ 			ei->dev->if_name, rx_frame.status,
+ 			rx_frame.next, rx_frame.count));
+ #endif
+ 		if (errs & ENRSR_FO) ei->dev->if_stats->rx_fifo++;
+ 	}
+ 	next_frame = rx_frame.next;
+ 
+ 	/* This _should_ never happen: it's here for avoiding bad clones. */
+ 	if (next_frame >= ei->stop_page) {
+ 		printk("%s: next frame inconsistency, %#2x..",
+ 					ei->dev->if_name, next_frame);
+ 		next_frame = ei->rx_start_page;
+ 	}
+ 	ei->current_page = next_frame;
+ 	outb(next_frame-1, e8390_base+EN0_BOUNDARY);
+   }
+ 
+   /*
+    * If any worth-while packets have been received, dev_rint()
+    * has done a mark_bh(NET_BH) for us and will work on them
+    * when we get to the bottom-half routine.
+    */
+     
+   /* Bug alert!  Reset ENISR_OVER to avoid spurious overruns! */
+   outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
+ }
+ 
+ 
+ /*
+  * We have a receiver overrun: we have to kick the
+  * 8390 to get it started again.
+  */
+ static void
+ NS8390_rx_overrun(struct ei *ei)
+ {
+   int e8390_base = ei->iobase;
+   int reset_start_time = jiffies;
+     
+   /* We should already be stopped and in page0.  Remove after testing. */
+   outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+   
+   DPRINTF((DBG_8390, "%s: Receiver overrun.\n", ei->dev->if_name));
+   ei->dev->if_stats->rx_overrun++;
+ 
+   /*
+    * The old Biro driver does dummy = inb_p( RBCR[01] ); at this point.
+    * It might mean something -- magic to speed up a reset?  A 8390 bug?
+    */
+ 
+   /*
+    * Wait for the reset to complete. This should happen almost instantly,
+    * but could take up to 1.5msec in certain rare instances.  There is no
+    * easy way of timing something in that range, so we use 'jiffies' as
+    * a sanity check.
+    */
+   while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0) {
+ 	if (jiffies - reset_start_time > 1) {
+ 		printk("%s: reset did not complete at ei_rx_overrun.\n",
+ 						ei->dev->if_name);
+ 		ei->chip_init(ei, 1);
+ 		return;
+ 	};
+   }
+ 
+   /* Remove packets right away. */
+   NS8390_receive(ei);
+     
+   outb_p(0xff, e8390_base+EN0_ISR);
+ 
+   /* Generic 8390 insns to start up again, same as in open_8390(). */
+   outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+   outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ }
+ 
+ 
+ /*
+  * We have finished a transmit: check for errors and then
+  * trigger the next packet to be sent.
+  */
+ static void
+ NS8390_tx_intr(struct ei *ei)
+ {
+   int e8390_base = ei->iobase;
+   int status = inb(e8390_base + EN0_TSR);
+     
+   outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+     
+   if (ei->pingpong) {
+ 	ei->txqueue--;
+ 	if (ei->tx1 < 0) {
+ 		if (ei->lasttx != 1 && ei->lasttx != -1)
+ 			printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
+ 				   ei->name, ei->lasttx, ei->tx1);
+ 		ei->tx1 = 0;
+ 		ei->dev->if_tbusy = 0;
+ 		if (ei->tx2 > 0) {
+ 			NS8390_trigger_send(ei, ei->tx2, ei->tx_start_page + 6);
+ 			ei->dev->if_txtimer = jiffies;
+ 			ei->txing = 1;
+ 			ei->tx2 = -1,
+ 			ei->lasttx = 2;
+ 		} else {
+ 			ei->lasttx = 20;
+ 			ei->txing = 0;
+ 		}
+ 	} else if (ei->tx2 < 0) {
+ 		if (ei->lasttx != 2 && ei->lasttx != -2)
+ 			printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+ 				   ei->name, ei->lasttx, ei->tx2);
+ 		ei->tx2 = 0;
+ 		ei->dev->if_tbusy = 0;
+ 		if (ei->tx1 > 0) {
+ 			NS8390_trigger_send(ei, ei->tx1, ei->tx_start_page);
+ 			ei->dev->if_txtimer = jiffies;
+ 			ei->txing = 1;
+ 			ei->tx1 = -1;
+ 			ei->lasttx = 1;
+ 		} else {
+ 			ei->lasttx = 10;
+ 			ei->txing = 0;
+ 		}
+ 	} else {
+ 		printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
+ 			   ei->dev->if_name, ei->lasttx);
+ 	}
+   } else {
+ 	ei->txing = 0;
+ 	ei->dev->if_tbusy = 0;
+   }
+     
+   /* Minimize Tx latency: update the statistics after we restart TXing. */
+   if (status & ENTSR_COL) ei->dev->if_stats->tx_collision++;
+   if (status & ENTSR_PTX) ei->dev->if_stats->tx_packets++;
+     else {
+ 	ei->dev->if_stats->tx_errors++;
+ 	if (status & ENTSR_ABT) ei->dev->if_stats->tx_aborted++;
+ 	if (status & ENTSR_CRS) ei->dev->if_stats->tx_carrier++;
+ 	if (status & ENTSR_FU)  ei->dev->if_stats->tx_fifo++;
+ 	if (status & ENTSR_CDH) ei->dev->if_stats->tx_heartbeat++;
+ 	if (status & ENTSR_OWC) ei->dev->if_stats->tx_window++;
+   }
+ 
+   mark_bh(NET_BH);
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the ether interface interrupts.
+  */
+ static void
+ NS8390_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = (struct ei *)(NS8390_irq2ei_map[irq]);
+   int e8390_base;
+   int interrupts, boguscount = 0;
+     
+   if (ei == NULL) {
+ 	printk("BECKER: NS8390: interrupt: irq%d for unknown device.\n", irq);
+ 	return;
+   }
+   e8390_base = ei->iobase;
+   if (ei->interrupt || ei->irqlock) {
+ 	/* The "irqlock" check is only for testing. */
+ 	sti();
+ 	printk(ei->irqlock
+ 		   ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
+ 		   : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
+ 		   ei->dev->if_name, inb_p(e8390_base + EN0_ISR),
+ 		   inb_p(e8390_base + EN0_IMR));
+ 	return;
+   }
+   ei->interrupt = 1;
+   sti(); /* Allow other interrupts. */
+     
+   /* Change to page 0 and read the intr status reg. */
+   outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+   DPRINTF((DBG_8390, "%s: NS8390: interrupt(isr=%#2.2x).\n",
+ 			ei->dev->if_name, inb_p(e8390_base + EN0_ISR)));
+     
+   /* !!Assumption!! -- we stay in page 0. Don't break this. */
+   while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && ++boguscount < 9) {
+ 	if (interrupts & ENISR_RDC) {
+ 		/* Ack meaningless DMA complete. */
+ 		outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ 	}
+ 	if (interrupts & ENISR_OVER) {
+ 		NS8390_rx_overrun(ei);
+ 	} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+ 		/* Got a good (?) packet. */
+ 		NS8390_receive(ei);
+ 	}
+ 
+ 	/* Push the next to-transmit packet through. */
+ 	if (interrupts & ENISR_TX) {
+ 		NS8390_tx_intr(ei);
+ 	} else if (interrupts & ENISR_COUNTERS) {
+ 		ei->dev->if_stats->rx_frame += inb_p(e8390_base + EN0_COUNTER0);
+ 		ei->dev->if_stats->rx_crc   += inb_p(e8390_base + EN0_COUNTER1);
+ 		ei->dev->if_stats->rx_missed += inb_p(e8390_base + EN0_COUNTER2);
+ 		outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+ 	}
+ 		
+ 	/* Ignore the transmit errs and reset intr for now. */
+ 	if (interrupts & ENISR_TX_ERR) {
+ 		outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+ 	}
+ 	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+   }
+     
+   if (interrupts) {
+ 	DPRINTF((DBG_8390, "%s: unknown interrupt %#2x\n",
+ 				ei->dev->if_name, interrupts));
+ 	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ 	outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+   }
+   ei->interrupt = 0;
+ }
+ 
+ 
+ /*
+  * Open/initialize the board.  This routine goes all-out, setting
+  * everything up anew at each open, even though many of these
+  * registers should only need to be set once at boot.
+  */
+ static void
+ NS8390_open(struct ei *ei)
+ {
+   /* Setup the reverse pointer. */
+   NS8390_irq2ei_map[ei->irq] = ei;
+   ei->chip_init(ei, 1);
+ 
+   /* The old local flags... */
+   ei->tx1 = ei->tx2 = 0;
+   ei->txing = 0;
+   ei->interrupt = 0;
+   ei->irqlock = 0;
+ 
+   /* ... are now global. */
+   ei->dev->if_tbusy = 0;
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  */
+ static void
+ NS8390_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   short ioaddr = ei->iobase;
+     
+   if (num_addrs > 0) {
+ 	/*
+ 	 * The multicast-accept list is initialized to accept-all,
+ 	 * and we rely on higher-level filtering for now.
+ 	 */
+ 	outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
+   } else if (num_addrs < 0) {
+ 	outb_p(E8390_RXCONFIG | 0x10, ioaddr + EN0_RXCR);
+   } else {
+ 	outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
+   }
+ }
+ 
+ 
+ static struct sigaction NS8390_sigaction = {
+   NS8390_interrupt, 0, 0, NULL
+ };
+ 
+ 
+ /* Initialize the rest of the NS8390-related fields. */
+ int
+ NS8390_setup(struct ei *ei)
+ {
+ #ifndef NO_PINGPONG
+   ei->pingpong = 1;
+ #endif
+   ei->sigaction = &NS8390_sigaction;
+   ei->chip_init = NS8390_init;
+   ei->chip_open = NS8390_open;
+   ei->chip_s_multi = NS8390_set_multicast;
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = NS8390_xmit;
+ 
+   return(0);
+ }
*** original/net/drv/becker/ns8390.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/ns8390.h	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,119 ----
+ /* Generic NS8390 register definitions. */
+ /* This file is part of Donald Becker's 8390 drivers, and is distributed
+    under the same license.
+    Some of these names and comments originated from the Crynwr
+    packet drivers, which are distributed under the GPL. */
+ 
+ #ifndef _BECKER_NS8390_H
+ #define _BECKER_NS8390_H
+ 
+ #include <linux/if_ether.h>
+ #include <linux/ioport.h>
+ 
+ 
+ #define TX_2X_PAGES	12
+ #define TX_1X_PAGES	6
+ #define TX_PAGES	(ei->pingpong ? TX_2X_PAGES : TX_1X_PAGES)
+ 
+ /* Some generic ethernet register configurations. */
+ #define E8390_TX_IRQ_MASK	0x0a	/* For register EN0_ISR */
+ #define E8390_RX_IRQ_MASK	0x05
+ #define E8390_RXCONFIG		0x04	/* EN0_RXCR: broadcasts, no multicast,errors */
+ #define E8390_RXOFF		0x20	/* EN0_RXCR: Accept no packets */
+ #define E8390_TXCONFIG		0x00	/* EN0_TXCR: Normal transmit mode */
+ #define E8390_TXOFF		0x02	/* EN0_TXCR: Transmitter off */
+ 
+ /*  Register accessed at EN_CMD, the 8390 base addr.  */
+ #define E8390_STOP	0x01	/* Stop and reset the chip */
+ #define E8390_START	0x02	/* Start the chip, clear reset */
+ #define E8390_TRANS	0x04	/* Transmit a frame */
+ #define E8390_RREAD	0x08	/* Remote read */
+ #define E8390_RWRITE	0x10	/* Remote write  */
+ #define E8390_NODMA	0x20	/* Remote DMA */
+ #define E8390_PAGE0	0x00	/* Select page chip registers */
+ #define E8390_PAGE1	0x40	/* using the two high-order bits */
+ #define E8390_PAGE2	0x80	/* Page 3 is invalid. */
+ 
+ #define E8390_CMD	0x00	/* The command register (for all pages) */
+ /* Page 0 register offsets. */
+ #define EN0_CLDALO	0x01	/* Low byte of current local dma addr  RD */
+ #define EN0_STARTPG	0x01	/* Starting page of ring bfr WR */
+ #define EN0_CLDAHI	0x02	/* High byte of current local dma addr  RD */
+ #define EN0_STOPPG	0x02	/* Ending page +1 of ring bfr WR */
+ #define EN0_BOUNDARY	0x03	/* Boundary page of ring bfr RD WR */
+ #define EN0_TSR		0x04	/* Transmit status reg RD */
+ #define EN0_TPSR	0x04	/* Transmit starting page WR */
+ #define EN0_NCR		0x05	/* Number of collision reg RD */
+ #define EN0_TCNTLO	0x05	/* Low  byte of tx byte count WR */
+ #define EN0_FIFO	0x06	/* FIFO RD */
+ #define EN0_TCNTHI	0x06	/* High byte of tx byte count WR */
+ #define EN0_ISR		0x07	/* Interrupt status reg RD WR */
+ #define EN0_CRDALO	0x08	/* low byte of current remote dma address RD */
+ #define EN0_RSARLO	0x08	/* Remote start address reg 0 */
+ #define EN0_CRDAHI	0x09	/* high byte, current remote dma address RD */
+ #define EN0_RSARHI	0x09	/* Remote start address reg 1 */
+ #define EN0_RCNTLO	0x0a	/* Remote byte count reg WR */
+ #define EN0_RCNTHI	0x0b	/* Remote byte count reg WR */
+ #define EN0_RSR		0x0c	/* rx status reg RD */
+ #define EN0_RXCR	0x0c	/* RX configuration reg WR */
+ #define EN0_TXCR	0x0d	/* TX configuration reg WR */
+ #define EN0_COUNTER0	0x0d	/* Rcv alignment error counter RD */
+ #define EN0_DCFG	0x0e	/* Data configuration reg WR */
+ #define EN0_COUNTER1	0x0e	/* Rcv CRC error counter RD */
+ #define EN0_IMR		0x0f	/* Interrupt mask reg WR */
+ #define EN0_COUNTER2	0x0f	/* Rcv missed frame error counter RD */
+ 
+ /* Bits in EN0_ISR - Interrupt status register */
+ #define ENISR_RX	0x01	/* Receiver, no error */
+ #define ENISR_TX	0x02	/* Transmitter, no error */
+ #define ENISR_RX_ERR	0x04	/* Receiver, with error */
+ #define ENISR_TX_ERR	0x08	/* Transmitter, with error */
+ #define ENISR_OVER	0x10	/* Receiver overwrote the ring */
+ #define ENISR_COUNTERS	0x20	/* Counters need emptying */
+ #define ENISR_RDC	0x40	/* remote dma complete */
+ #define ENISR_RESET	0x80	/* Reset completed */
+ #define ENISR_ALL	0x3f	/* Interrupts we will enable */
+ 
+ /* Bits in EN0_DCFG - Data config register */
+ #define ENDCFG_WTS	0x01	/* word transfer mode selection */
+ 
+ /* Page 1 register offsets. */
+ #define EN1_PHYS   0x01	/* This board's physical enet addr RD WR */
+ #define EN1_CURPAG 0x07	/* Current memory page RD WR */
+ #define EN1_MULT   0x08	/* Multicast filter mask array (8 bytes) RD WR */
+ 
+ /* Bits in received packet status byte and EN0_RSR*/
+ #define ENRSR_RXOK	0x01	/* Received a good packet */
+ #define ENRSR_CRC	0x02	/* CRC error */
+ #define ENRSR_FAE	0x04	/* frame alignment error */
+ #define ENRSR_FO	0x08	/* FIFO overrun */
+ #define ENRSR_MPA	0x10	/* missed pkt */
+ #define ENRSR_PHY	0x20	/* physical/multicase address */
+ #define ENRSR_DIS	0x40	/* receiver disable. set in monitor mode */
+ #define ENRSR_DEF	0x80	/* deferring */
+ 
+ /* Transmitted packet status, EN0_TSR. */
+ #define ENTSR_PTX 0x01	/* Packet transmitted without error */
+ #define ENTSR_ND  0x02	/* The transmit wasn't deferred. */
+ #define ENTSR_COL 0x04	/* The transmit collided at least once. */
+ #define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
+ #define ENTSR_CRS 0x10	/* The carrier sense was lost. */
+ #define ENTSR_FU  0x20  /* A "FIFO underrun" occured during transmit. */
+ #define ENTSR_CDH 0x40	/* The collision detect "heartbeat" signal was lost. */
+ #define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
+ 
+ 
+ /* The per-packet-header format. */
+ struct e8390_pkt_hdr {
+   unsigned char		status;	/* status */
+   unsigned char		next;	/* pointer to next packet. */
+   unsigned short	count;	/* header + packet lenght in bytes */
+ };
+ 
+ 
+ /* From 8390.c */
+ extern struct ei	*NS8390_irq2ei_map[];
+ extern int		NS8390_setup(struct ei *ei);
+ 
+ 
+ #endif /* _BECKER_NS8390_H */
*** original/net/drv/becker/smc-ultra.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/smc-ultra.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,269 ----
+ /* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+ /*
+     Written 1993 by Donald Becker.  If released, this code will be
+     Copyright 1993 United States Government as represented by the
+     Director, National Security Agency.  This software may be used and
+     distributed according to the terms of the GNU Public License,
+     incorporated herein by reference.
+     
+     This is a driver for the SMC Ultra ethercard.
+ 
+     The Author may be reached as becker@super.org or
+     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/ioport.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ #if 0
+ static char *version =
+ 	"smc-ultra.c:v0.07 3/1/94 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ #define START_PG	0x00	/* First page of TX buffer */
+ 
+ #define ULTRA_CMDREG	0	/* Offset to ASIC command register. */
+ #define  ULTRA_RESET	0x80	/* Board reset, in ULTRA_CMDREG. */
+ #define  ULTRA_MEMENB	0x40	/* Enable the shared memory. */
+ #define ULTRA_NIC_OFFSET  16	/* NIC register offset from the base_addr. */
+ 
+ 
+ static void
+ ultra_reset(struct ei *ei)
+ {
+   int cmd_port = ei->ioaddr; /* ASIC base addr */
+ 
+   DPRINTF((DBG_ULTRA, "%s: resetting Ultra, t=%ld...",
+ 				ei->dev->if_name, jiffies));
+   outb(ULTRA_RESET, cmd_port);
+   ei->txing = 0;
+ 
+   outb(ULTRA_MEMENB, cmd_port);
+ 
+   DPRINTF((DBG_ULTRA, "%s: reset done\n", ei->dev->if_name));
+ }
+ 
+ 
+ static int
+ ultra_open(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr; /* ASIC addr */
+ 
+   if (irqaction(ei->irq, ei->sigaction)) return(-EAGAIN);
+ 
+   outb(ULTRA_MEMENB, ioaddr);	/* enable memory, 16 bit mode */
+   outb(0x80, ioaddr + 5);
+   outb(0x01, ioaddr + 6);	/* enable interrupts and memory */
+   ei->chip_open(ei);
+   return(0);
+ }
+ 
+ 
+ static int
+ ultra_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr; /* CMDREG */
+ 
+   DPRINTF((DBG_ULTRA, "%s: shutting down ethercard\n", ei->dev->if_name));
+   ei->start = 0;
+   ei->dev->if_tbusy = 1;
+ 
+   outb(0x00, ioaddr + 6);	/* disable interrupts */
+   free_irq(ei->irq);
+   NS8390_irq2ei_map[ei->irq] = NULL;
+ 
+   ei->chip_init(ei, 0);
+ 
+   /*
+    * We should someday disable shared memory and change to
+    * 8-bit mode "just in case"...
+    */
+   return(0);
+ }
+ 
+ 
+ /*
+  * Block input and output are easy on shared memory ethercards,
+  * the only complication is when the ring buffer wraps.
+  */
+ static int
+ ultra_input(struct ei *ei, int count, char *buf, int ring_offset)
+ {
+   void *xfer_start = (void *)(ei->mem_start + ring_offset
+ 				- (START_PG<<8));
+ 
+   if (xfer_start + count > (void*) ei->rmem_end) {
+ 	/* We must wrap the input move. */
+ 	int semi_count = (void*)ei->rmem_end - xfer_start;
+ 	memcpy(buf, xfer_start, semi_count);
+ 	count -= semi_count;
+ 	memcpy(buf + semi_count, (char *)ei->rmem_start, count);
+ 	return(ei->rmem_start + count);
+   }
+   memcpy(buf, xfer_start, count);
+   return(ring_offset + count);
+ }
+ 
+ 
+ static void
+ ultra_output(struct ei *ei, int count, char *buf, int start_page)
+ {
+   unsigned char *shmem
+ 	= (unsigned char *)ei->mem_start + ((start_page - START_PG)<<8);
+ 
+   memcpy(shmem, buf, count);
+ }
+ 
+ 
+ static int
+ ultraprobe1(int ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int i;
+   int checksum = 0;
+   char *model_name;
+   unsigned char eeprom_irq = 0;
+ 
+   /* Values from various config regs. */
+   unsigned char num_pages, irqreg, addr, reg4 = inb(ioaddr + 4) & 0x7f;
+ 
+   /* Select the station address register set. */
+   outb(reg4, ioaddr + 4);
+ 
+   for (i = 0; i < 8; i++) checksum += inb(ioaddr + 8 + i);
+   if ((checksum & 0xff) != 0xFF) return(-ENODEV);
+   
+   DPRINTF((DBG_ULTRA, "%s: SMC Ultra probe at 0x%3X,",
+ 				ei->dev->if_name, ioaddr));
+ 
+   /* Fetch the hardware address. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(ioaddr + 8 + i);
+ 	DPRINTF((DBG_ULTRA, " %02.2X", sa.sa_data[i]));
+   }
+ 
+   /*
+    * Switch from the station address to the alternate register
+    * set and read the useful registers there.
+    */
+   outb(0x80 | reg4, ioaddr + 4);
+ 
+   /*
+    * Enabled FINE16 mode to avoid BIOS ROM width mismatches
+    * during reboot.
+    */
+   outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+   irqreg = inb(ioaddr + 0xd);
+   addr = inb(ioaddr + 0xb);
+ 
+   /*
+    * Switch back to the station address register set so that
+    * the MS-DOS driver can find the card after a warm boot.
+    */
+   outb(reg4, ioaddr + 4);
+ 
+   model_name = "SMC Ultra";
+ 
+   if (ei->irq < 2) {
+       unsigned char irqmap[] = { 0, 9, 3, 5, 7, 10, 11, 15 };
+       int irq;
+ 
+       /* The IRQ bits are split. */
+       irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+       if (irq == 0) {
+ 		DPRINTF((DBG_ULTRA, ", failed to detect IRQ line\n"));
+ 		return(-ENODEV);
+       }
+       ei->irq = irq;
+       eeprom_irq = 1;
+   }
+ 
+   /* OK, were are certain this is going to work.  Setup the device. */
+   snarf_region(ioaddr, 32);
+ 
+   /* The 8390 isn't at the base address, so fake the offset */
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr + ULTRA_NIC_OFFSET;
+ 
+   { 
+       int addr_tbl[4] = { 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000 };
+       short num_pages_tbl[4] = { 0x20, 0x40, 0x80, 0xff };
+ 
+       ei->membase = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
+       num_pages = num_pages_tbl[(addr >> 4) & 3];
+   }
+   ei->name = model_name;
+   ei->word16 = 1;
+   ei->tx_start_page = START_PG;
+   ei->rx_start_page = START_PG + TX_PAGES;
+   ei->stop_page = num_pages;
+ 
+   /* Set up the chip-specific stuff. */
+   (void) NS8390_setup(ei);
+ 
+   ei->mem_start = ei->membase;
+   ei->rmem_start = ei->mem_start + TX_PAGES*256;
+   ei->mem_end = ei->rmem_end
+       = ei->mem_start + (ei->stop_page - START_PG)*256;
+ 
+   DPRINTF((DBG_ULTRA, ",%sIRQ%d memory 0x%lX-0x%lX\n",
+ 	eeprom_irq ? "" : "assigned ",
+ 	ei->irq, ei->mem_start, ei->mem_end-1));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = ultra_reset;
+   ei->card_open = ultra_open;
+   ei->card_close = ultra_close;
+   ei->card_input = ultra_input;
+   ei->card_output = ultra_output;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Probe for the Ultra.  This looks like a 8013 with the
+  * station address PROM at I/O ports <base>+8 to <base>+13,
+  * with a checksum following.
+  */
+ int
+ ultra_probe(struct ei *ei)
+ {
+   static int smc_card_nr = 0;
+   int *port, ports[] = { 0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "SMC Ultra";
+   sprintf(ei->dev->if_name, "ultra%d", smc_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(ultraprobe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	if (check_region(*port, 32)) continue;
+ 
+ 	if ((inb(*port + 7) & 0xF0) == 0x20 &&	/* check chip ID nibble */
+ 		(ultraprobe1(*port, ei) == 0)) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/becker/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/utils.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,75 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		Various functions for the BECKER Ethernet Kit.
+  *
+  * Version:	@(#)utils.c	1.3.0	03/01/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include <stdarg.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ int becker_debug = 0;
+ 
+ 
+ #ifdef BECKER_DEBUG
+ void
+ becker_dprintf(int level, char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (becker_debug & (1 << (level - 1))) {
+ 	va_start(args, fmt);
+ 	vsprintf(buff, fmt, args);
+ 	va_end(args);
+ 	printk("%s", buff);
+   }
+ }
+ #endif
+ 
+ 
+ int
+ becker_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((unsigned long *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		becker_debug = 0;
+ 		break;
+ 
+ 	default:
+ 		if (val > 0 && val < 128) {
+ 			becker_debug |= (1 << (val - 1));
+ 			return(0);
+ 		} else if (val >= 128) {
+ 			becker_debug &= ~(1 << (val - 128));
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/drv/becker/wd.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/wd.c	Mon May  9 13:13:02 1994
***************
*** 0 ****
--- 1,378 ----
+ /* wd.c: A WD80x3 ethernet driver for linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 	Copyright 1993 United States Government as represented by the
+ 	Director, National Security Agency.	 This software may be used and
+ 	distributed according to the terms of the GNU Public License,
+ 	incorporated herein by reference.
+ 	
+ 	This is a driver for WD8003 and WD8013 "compatible" ethercards.
+     
+ 	The Author may be reached as becker@super.org or
+ 	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ 
+ 	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+ */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/ioport.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+ #include "becker.h"
+ #include "global.h"
+ #include "ns8390.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"wd.c:v0.99-14 11/21/93 Donald Becker (becker@super.org)\n";
+ #endif
+ 
+ 
+ #define WD_START_PG	0x00	/* First page of TX buffer */
+ #define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
+ #define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
+ 
+ #define WD_CMDREG	0	/* Offset to ASIC command register. */
+ #define  WD_RESET	0x80	/* Board reset, in WD_CMDREG. */
+ #define  WD_MEMENB	0x40	/* Enable the shared memory. */
+ #define WD_CMDREG5	5	/* Offset to 16-bit-only ASIC register 5. */
+ #define  ISA16		0x80	/* Enable 16 bit access from the ISA bus. */
+ #define  NIC16		0x40	/* Enable 16 bit access from the 8390. */
+ #define WD_NIC_OFFSET	16	/* Offset to the 8390 NIC from the base_addr. */
+ 
+ 
+ static void
+ wd_reset(struct ei *ei)
+ {
+   int wd_cmd_port = ei->ioaddr; /* WD_CMDREG */
+ 
+   outb(WD_RESET, wd_cmd_port);
+   DPRINTF((DBG_WD, "resetting the WD80x3 t=%lu...", jiffies));
+   ei->txing = 0;
+ 
+   /* Set up the ASIC registers, just in case something changed them. */
+   outb((((ei->mem_start >> 13) & 0x3f) | WD_MEMENB), wd_cmd_port);
+   if (ei->word16)
+ 	outb(NIC16 | ((ei->mem_start >> 19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+ 
+   DPRINTF((DBG_WD, "reset done\n"));
+ }
+ 
+ 
+ /*
+  * Block input and output are easy on shared memory ethercards,
+  * and trivial on the Western digital card where there is no
+  * choice of how to do it.  The only complications are that the
+  * ring buffer wraps, and need to map switch between 8- and
+  * 16-bit modes.
+  */
+ static int
+ wd_input(struct ei *ei, int count, char *buf, int ring_offset)
+ {
+   int wd_cmdreg = ei->ioaddr; /* WD_CMDREG */
+   void *xfer_start = (void *)(ei->mem_start + ring_offset
+ 				- (WD_START_PG<<8));
+ 
+   /* We'll always get a 4 byte header read first. */
+   if (count == 4) {
+ 	if (ei->word16) outb(ISA16 | ei->reg5, wd_cmdreg+WD_CMDREG5);
+ 	((int*)buf)[0] = ((int*)xfer_start)[0];
+ 	return(0);
+   }
+ 
+   if (xfer_start + count > (void*) ei->rmem_end) {
+ 	/* We must wrap the input move. */
+ 	int semi_count = (void*)ei->rmem_end - xfer_start;
+ 	memcpy(buf, xfer_start, semi_count);
+ 	count -= semi_count;
+ 	memcpy(buf + semi_count, (char *)ei->rmem_start, count);
+   } else
+ 	memcpy(buf, xfer_start, count);
+ 
+   /* Turn off 16 bit access so that reboot works. ISA brain-damage. */
+   if (ei->word16)
+ 	outb(ei->reg5, wd_cmdreg+WD_CMDREG5);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ wd_output(struct ei *ei, int count, char *buf, int start_page)
+ {
+   int wd_cmdreg = ei->ioaddr; /* WD_CMDREG */
+   unsigned char *shmem
+ 	= (unsigned char *)ei->mem_start + ((start_page - WD_START_PG)<<8);
+ 
+   if (ei->word16) {
+ 	/* Turn on and off 16 bit access so that reboot works. */
+ 	outb(ISA16 | ei->reg5, wd_cmdreg+WD_CMDREG5);
+ 	memcpy(shmem, buf, count);
+ 	outb(ei->reg5, wd_cmdreg+WD_CMDREG5);
+   } else
+ 	memcpy(shmem, buf, count);
+ }
+ 
+ 
+ static int
+ wd_open(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr; /* WD_CMDREG */
+ 
+   /*
+    * Map in the shared memory. Always set register 0 last
+    * to remain compatible with very old boards.
+    */
+   ei->reg0 = ((ei->mem_start >> 13) & 0x3f) | WD_MEMENB;
+   ei->reg5 = ((ei->mem_start >> 19) & 0x1f) | NIC16;
+ 
+   if (ei->word16) outb(ei->reg5, ioaddr+WD_CMDREG5);
+   outb(ei->reg0, ioaddr); /* WD_CMDREG */
+ 
+   ei->chip_open(ei);
+   return(0);
+ }
+ 
+ 
+ static int
+ wd_close(struct ei *ei)
+ {
+   int wd_cmdreg = ei->ioaddr;	/* WD_CMDREG */
+ 
+   DPRINTF((DBG_WD, "%s: shutting down ethercard\n", ei->dev->if_name));
+   ei->chip_init(ei, 0);
+ 
+   /* Change from 16-bit to 8-bit shared memory so reboot works. */
+   outb(ei->reg5, wd_cmdreg + WD_CMDREG5 );
+ 
+   /* And disable the shared memory. */
+   outb(ei->reg0 & ~WD_MEMENB, wd_cmdreg);
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * See if we can find a WD80x3 card at the given address.
+  */
+ static int
+ wdprobe1(int ioaddr, struct ei *ei)
+ {
+   struct sockaddr sa;
+   int i;
+   int checksum = 0;
+   int ancient = 0;		/* An old card without config registers. */
+   int word16 = 0;		/* 0 = 8 bit, 1 = 16 bit */
+   char *model_name;
+ 
+   /* Perform a simple test to see if ANYTHING is present there. */
+   for (i = 0; i < 8; i++) checksum += inb(ioaddr + 8 + i);
+   if ((checksum & 0xff) != 0xFF) return(-ENODEV);
+ 
+   DPRINTF((DBG_WD, "%s: WD80x3 at 0x%3X, ", ei->dev->if_name, ioaddr));
+ 
+   /* Fetch the hardware address. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(ioaddr + 8 + i);
+ 	DPRINTF((DBG_WD, " %02.2X", sa.sa_data[i]));
+   }
+ 
+   /*
+    * The following PureData probe code was contributed by
+    * Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+    * configuration differently from others so we have to check for them.
+    * This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+    */
+   if ((inb(ioaddr + 0) == 'P') && (inb(ioaddr+1) == 'D')) {
+ 	unsigned char reg5 = inb(ioaddr + 5);
+ 
+ 	switch (inb(ioaddr + 2)) {
+ 		case 0x03:
+ 			word16 = 0;
+ 			model_name = "PDI8023-8";
+ 			break;
+ 
+ 		case 0x05:
+ 			word16 = 0;
+ 			model_name = "PDUC8023";
+ 			break;
+ 
+ 		case 0x0a:
+ 			word16 = 1;
+ 			model_name = "PDI8023-16";
+ 			break;
+ 
+ 		default:	/*
+ 				 * Either 0x01 (dumb) or they've
+ 				 * released a new version.
+ 				 */
+ 			word16 = 0;
+ 			model_name = "PDI8023";
+ 			break;
+ 	}
+ 	ei->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+ 	ei->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+   } else {
+ 	/*
+ 	 * This method of checking for a 16-bit board is borrowed
+ 	 * from the we.c driver.  A simpler method is just to look
+ 	 * in ASIC reg. 0x03.  I'm comparing the two methods in alpha
+ 	 * test to make certain they return the same result.
+ 	 */
+ 
+ 	/*
+ 	 * Check for the old 8 bit board - it has register 0/8 aliasing.
+ 	 * Do NOT check i>=6 here -- it hangs the old 8003 boards!
+ 	 */
+ 	for (i = 0; i < 6; i++) {
+ 		if (inb(ioaddr + i) != inb(ioaddr + 8 + i)) break;
+ 	}
+ 	if (i >= 6) {
+ 		ancient = 1;
+ 		model_name = "WD8003-old";
+ 		word16 = 0;
+ 	} else {
+ 		int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+ 		outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+ 		if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+ 			&& (tmp & 0x01) == 0x01   ) {	/* In a 16 slot. */
+ 
+ 			int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+ 
+ 			/* Magic to set ASIC to word-wide mode. */
+ 			outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ 			outb(tmp, ioaddr+1);
+ 			model_name = "WD8013";
+ 			word16 = 1; 	/* we have a 16bit board here! */
+ 		} else {
+ 			model_name = "WD8003";
+ 			word16 = 0;
+ 		}
+ 		outb(tmp, ioaddr+1);	/* restore original reg1 value */
+ 	}
+   }
+ 
+   if (ei->mem_start == 0) {
+ 	/* Sanity and old 8003 check */
+ 	int reg0 = inb(ioaddr);
+ 
+ 	if (reg0 == 0xff || reg0 == 0) {
+ 		/* Future plan: this could check a few likely locations first. */
+ 		ei->membase = 0xd0000;
+ 		DPRINTF((DBG_WD, " assigning address 0x%lX", ei->membase));
+ 	} else {
+ 		int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+ 
+ 		/* Some boards don't have the register 5 -- it returns 0xff. */
+ 		if (high_addr_bits == 0x1f || word16 == 0)
+ 					high_addr_bits = 0x01;
+ 		ei->membase = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+ 	}
+   }
+ 
+   /* The 8390 isn't at the base address -- the ASIC regs are there! */
+   ei->ioaddr = ioaddr;
+   ei->iobase = ioaddr + WD_NIC_OFFSET;
+ 
+   if (ei->irq < 2) {
+ 	int irqmap[] = { 9,3,5,7,10,11,15,4 };
+ 	int reg1 = inb(ioaddr+1);
+ 	int reg4 = inb(ioaddr+4);
+ 
+ 	if (ancient || reg1 == 0xff)	/* Ack!! No way to read the IRQ! */
+ 		ei->irq = word16 ? 10 : 5;
+ 	  else
+ 		ei->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+   } else if (ei->irq == 2)	/* fixup bogosity: IRQ2 is really IRQ9 */
+ 		ei->irq = 9;
+ 
+   /* Set up the chip-specific stuff. */
+   (void) NS8390_setup(ei);
+ 
+   /*
+    * Snarf the interrupt now.  There's no point in waiting since
+    * we cannot share and the board will usually be enabled.
+    */
+   {
+ 	int irqval = irqaction(ei->irq, ei->sigaction);
+ 	if (irqval) {
+ 		printk(" unable to get IRQ%d (irqval=%d)\n",
+ 						ei->irq, irqval);
+ 		return(-EAGAIN);
+ 	}
+   }
+ 
+   /* OK, were are certain this is going to work.  Setup the device. */
+   snarf_region(ioaddr, 32);
+ 
+   ei->name = model_name;
+   ei->word16 = word16;
+   ei->tx_start_page = WD_START_PG;
+   ei->rx_start_page = WD_START_PG + TX_PAGES;
+   ei->stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+ 
+   /* Don't map in the shared memory until the board is actually opened. */
+   ei->mem_start = ei->membase;
+   ei->rmem_start = ei->mem_start + TX_PAGES*256;
+   ei->mem_end = ei->rmem_end =
+ 		ei->mem_start + (ei->stop_page - WD_START_PG)*256;
+ 
+   DPRINTF((DBG_WD, " %s, IRQ%d, shared memory at 0x%lX-0x%lX\n",
+ 	 model_name, ei->irq, ei->mem_start, ei->mem_end-1));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = wd_reset;
+   ei->card_open = wd_open;
+   ei->card_close = wd_close;
+   ei->card_input = wd_input;
+   ei->card_output = wd_output;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Probe for the WD8003 and WD8013.  These cards have the station
+  * address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+  * following. A Soundblaster can have the same checksum as an WDethercard,
+  * so we have an extra exclusionary check for it.
+  *
+  * The wdprobe1() routine initializes the card and fills the
+  * station address field.
+  */
+ int
+ wd_probe(struct ei *ei)
+ {
+   static int wd_card_nr = 0;
+   int *port, ports[] = { 0x300, 0x280, 0x380, 0x240, 0 };
+ 
+   /* Set up the device name. */
+   ei->name = "WD80x3";
+   sprintf(ei->dev->if_name, "wd%d", wd_card_nr++);
+ 
+   if (ei->ioaddr != DDI_IOAUTO) {
+ 	if (ei->ioaddr == DDI_IOANY) return(0);
+ 	  else return(wdprobe1(ei->ioaddr, ei));
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	if (check_region(*port, 32)) continue;
+ 
+ 	/* Check if a card is present.  Be careful about any soundcard! */
+ 	if ((inb(*port + 8) != 0xff) &&
+ 	    (inb(*port + 9) != 0xff) &&
+ 	    (wdprobe1(*port, ei) == 0)) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/becker/znote.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/becker/znote.c	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,854 ----
+ /* znote.c: A Zenith Z-Note ethernet driver for linux. */
+ /*
+ 	Written 1993 by Donald Becker.
+ 
+ 	The author may be reached as becker@super.org.
+ 	This driver is based on the Linux skeleton driver.  The copyright of the
+ 	skeleton driver is held by the United States Government, as represented
+ 	by DIRNSA, and it is released under the GPL.
+ 
+   References:
+ 	   The Crynwr packet driver.
+ 
+ 	  "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
+ 	  Intel Microcommunications Databook, Vol. 1, 1990.
+     As usual with Intel, the documentation is incomplete and inaccurate.
+ 	I had to read the Crynwr packet driver to figure out how to actually
+ 	use the i82593, and guess at what register bits matched the loosely
+ 	related i82586.
+  */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/ptrace.h>
+ #include <linux/fcntl.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/ioport.h>
+ #include <linux/interrupt.h>
+ #include <linux/malloc.h>
+ #include <linux/ioport.h>
+ #include <linux/string.h>
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include <errno.h>
+ #include "becker.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version =
+ 	"znet.c:v0.02 12/22/93 becker@super.org\n";
+ #endif
+ 
+ /* The DMA modes we need aren't in <dma.h>. */
+ #define DMA_RX_MODE		0x14	/* Auto init, I/O to mem, ++, demand. */
+ #define DMA_TX_MODE		0x18	/* Auto init, Mem to I/O, ++, demand. */
+ #define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+ #define DMA_BUF_SIZE		8192
+ #define RX_BUF_SIZE		8192
+ #define TX_BUF_SIZE		8192
+ 
+ /* Commands to the i82593 channel 0. */
+ #define CMD0_CHNL_0		0x00
+ #define CMD0_CHNL_1		0x10		/* Switch to channel 1. */
+ #define CMD0_NOP		(CMD0_CHNL_0)
+ #define CMD0_PORT_1		CMD0_CHNL_1
+ #define CMD1_PORT_0		1
+ #define CMD0_IA_SETUP		1
+ #define CMD0_CONFIGURE		2
+ #define CMD0_MULTICAST_LIST	3
+ #define CMD0_TRANSMIT		4
+ #define CMD0_DUMP		6
+ #define CMD0_DIAGNOSE		7
+ #define CMD0_Rx_ENABLE		8
+ #define CMD0_Rx_DISABLE		10
+ #define CMD0_Rx_STOP		11
+ #define CMD0_RETRANSMIT		12
+ #define CMD0_ABORT		13
+ #define CMD0_RESET		14
+ 
+ #define CMD0_ACK		0x80
+ 
+ #define CMD0_STAT0		(0 << 5)
+ #define CMD0_STAT1		(1 << 5)
+ #define CMD0_STAT2		(2 << 5)
+ #define CMD0_STAT3		(3 << 5)
+ 
+ 
+ struct znote {
+   int		rx_dma,
+ 		tx_dma;
+   void		*buffer;
+   ushort	*rx_buffer;
+   ushort	*tx_buffer;
+   ushort	*rx_cur;
+   ushort	*tx_cur;
+ };
+ #define EIPVT(ei)	((struct znote *) (ei)->pvt)
+ 
+ 
+ static char	dma_buffer[3*DMA_BUF_SIZE + 8];
+ static struct ei *znote_irq2ei_map[16];
+ 
+ 
+ /*
+  * The configuration block.  What an undocumented nightmare.  The first
+  * set of values are those suggested (without explaination) for ethernet
+  * in the Intel 82586 databook.	 The rest appear to be completely undocumented,
+  * except for cryptic notes in the Crynwr packet driver.  This driver uses
+  * the Crynwr values verbatim.
+  */
+ static unsigned char i593_init[] = {
+   0xAA,			/* 1: 16-byte input & 80-byte output FIFO. */
+ 			/*	  threshhold, 96-byte FIFO, 82593 mode. */
+   0x88,			/* 2: Continuous w/interrupts, 128-clock DMA.*/
+   0x2E,			/* 3: 8-byte preamble, NO address insertion, */
+ 			/*	  6-byte Ethernet address, loopback off.*/
+   0x00,			/* 4: Default priorities & backoff methods. */
+   0x60,			/* 5: 96-bit interframe spacing. */
+   0x00,			/* 6: 512-bit slot time (low-order). */
+   0xF2,			/* 7: Slot time (high-order), 15 COLL retries. */
+   0x00,			/* 8: Promisc-off, broadcast-on, default CRC. */
+   0x00,			/* 9: Default carrier-sense, collision-detect. */
+   0x40,			/* 10: 64-byte minimum frame length. */
+   0x5F,			/* 11: Type/length checks OFF, no CRC input,
+ 				   "jabber" termination, etc. */
+   0x00,			/* 12: Full-duplex disabled. */
+   0x3F,			/* 13: Default multicast addresses & backoff. */
+   0x07,			/* 14:	Default IFS retriggering. */
+   0x31,			/* 15:	Internal retransmit, drop "runt" packets,
+ 				   synchr. DRQ deassertion, 6 status bytes. */
+   0x22,			/* 16: Receive ring-buffer size (8K), 
+ 				   receive-stop register enable. */
+ };
+ 
+ #define	ZNMAGIC		"NETIDBLK"	/* the magic string */
+ struct netidblk {
+ char		magic[8];		/* the magic number (string) */
+ unsigned char	netid[8];		/* the physical station address */
+ char		nettype,
+ 		globalopt;
+ char		vendor[8];		/* the machine vendor and product name. */
+ char		product[8];
+ char		irq1, irq2;		/* interrupts, only one is currently used.	*/
+ char		dma1, dma2;
+ short		dma_mem_misc[8];	/* DMA buffer locations (unused in Linux). */
+ short		iobase1, iosize1;
+ short		iobase2, iosize2;	/* Second iobase unused. */
+ char		driver_options;		/* Misc. bits */
+ char		pad;
+ };
+ 
+ 
+ /* Forward declaration. */
+ static void znote_set_multicast(struct ei *ei, int num_addrs, void *addrs);
+ 
+ 
+ static void
+ update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+ {
+   outb(CMD0_PORT_1, ioaddr);
+ 
+   DPRINTF((DBG_ZNOTE, "Updating stop hit with value 0x%02X\n",
+ 					(rx_stop_offset >> 6) | 0x80));
+   outb((rx_stop_offset >> 6) | 0x80, ioaddr);
+   outb(CMD1_PORT_0, ioaddr);
+ }
+ 
+ 
+ /*
+  * wait_for_done
+  * - this is a blatent rip-off of the wait_for_done routine
+  * from the Crynwr packet driver. It does not work correctly -
+  * doesn't acknowledge the interrupts it gets or something.
+  * It does determine when the command is done, or if there are
+  * none executing, though...			-Mike
+  */
+ static int
+ wait_for_done(short ioaddr)
+ {
+   unsigned int stat;
+   unsigned stop_time = jiffies + 10;
+   int ticks = 0;
+ 
+   /* Check to see if we are busy. */
+   outb(CMD0_NOP+CMD0_STAT3,ioaddr);
+   stat = inb(ioaddr);
+ 
+   /* check if busy */
+   if ((stat & 3) == 0) {
+ 	DPRINTF((DBG_ZNOTE, "ZNote: wait_for_done: not busy, status 0x%02X\n",
+ 								stat));
+ 	return(0);
+   }
+ 
+   while (jiffies < stop_time) {
+ 	/* Now check. */
+ 	outb(CMD0_NOP+CMD0_STAT3,ioaddr);
+ 	stat = inb(ioaddr);
+ 	if ((stat & 3) == 0) {
+ 		DPRINTF((DBG_ZNOTE,
+ 			"ZNote: command completed after %d ticks status 0x%02X\n",
+ 				   				ticks, stat));
+ 		outb((CMD0_NOP|CMD0_ACK),ioaddr);
+ 		return(0);
+ 	}
+ 	ticks++;
+   }
+   outb(CMD0_ABORT, ioaddr);
+ 
+   DPRINTF((DBG_ZNOTE,
+ 	"ZNote: wait_for_done: command not ACK'd, status 0x%02X after abort 0x%02X\n",
+ 		   					stat, inb(ioaddr)));
+ 
+   /* Should re-initialize here... */
+   return(1);
+ }
+ 
+ 
+ static int
+ do_command(struct ei *ei, int command, int length, ushort *buffer)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   /* This isn't needed, but is here for safety. */
+   outb(CMD0_NOP + CMD0_STAT3, ioaddr);
+ 
+   DPRINTF((DBG_ZNOTE, "ZNote: do_command() while the i82593 is busy\n"));
+ 
+   cli();
+   disable_dma(EIPVT(ei)->tx_dma);
+   clear_dma_ff(EIPVT(ei)->tx_dma);
+   set_dma_mode(EIPVT(ei)->tx_dma, DMA_TX_MODE);
+   set_dma_addr(EIPVT(ei)->tx_dma, (unsigned int) EIPVT(ei)->tx_buffer);
+   set_dma_count(EIPVT(ei)->tx_dma, (length + 1) >> 1);
+   sti();
+   enable_dma(EIPVT(ei)->tx_dma);
+   outb(command, ioaddr);
+   return(0);
+ }
+ 
+ 
+ /*
+  * Initialize the hardware.  We have to do this when the board is
+  * open()ed or when we come out of suspend mode.
+  */
+ static void
+ znote_card_init(struct ei *ei)
+ {
+   short ioaddr = ei->ioaddr;
+ 
+   EIPVT(ei)->rx_cur = EIPVT(ei)->rx_buffer;
+   EIPVT(ei)->tx_cur = EIPVT(ei)->tx_buffer;
+ 
+   /* Protect against a DMA flip-flop reset by an interrupting task. */
+   cli(); {
+ 	/* Hmmm, these probably aren't needed? */
+ 	disable_dma(EIPVT(ei)->rx_dma);
+ 	disable_dma(EIPVT(ei)->tx_dma);
+ 
+ 	clear_dma_ff(EIPVT(ei)->rx_dma);
+ 	set_dma_mode(EIPVT(ei)->rx_dma, DMA_RX_MODE);
+ 	set_dma_mode(EIPVT(ei)->tx_dma, DMA_TX_MODE);
+ 
+ 	set_dma_addr(EIPVT(ei)->rx_dma, (unsigned int) EIPVT(ei)->rx_buffer);
+ 	set_dma_count(EIPVT(ei)->rx_dma, RX_BUF_SIZE);
+ 
+ 	set_dma_addr(EIPVT(ei)->tx_dma, (unsigned int) EIPVT(ei)->tx_buffer);
+ 	set_dma_count(EIPVT(ei)->tx_dma, TX_BUF_SIZE);
+ 
+ 	/* Enable both DMA channels. */
+ 	enable_dma(EIPVT(ei)->rx_dma);
+ 	enable_dma(EIPVT(ei)->tx_dma);
+   } sti();
+ 
+   DPRINTF((DBG_ZNOTE, "%s: initializing the i82593... ", ei->dev->if_name));
+ 
+   /*
+    * Do an empty configure command, just like the Crynwr driver.
+    * This resets the chip to its default values.
+    */
+   EIPVT(ei)->tx_buffer[0] = 0;
+   do_command(ei, CMD0_CONFIGURE + CMD0_CHNL_1, 2, EIPVT(ei)->tx_buffer);
+ 
+   DPRINTF((DBG_ZNOTE, "set Mode... "));
+   znote_set_multicast(ei, 0, 0);
+   {
+ 	unsigned stop_time = jiffies + 3; 
+ 
+ 	while (jiffies < stop_time)
+ 			;
+   }
+ 
+   DPRINTF((DBG_ZNOTE, "set Address... "));
+   {
+ 	unsigned char *ptr;
+ 	unsigned stop_time = jiffies + 3; 
+ 
+ 	EIPVT(ei)->tx_buffer[0] = ETH_ALEN;
+ 	ptr = (ei->dev->mac_aget(ei->dev))->sa_data;
+ 	memcpy(EIPVT(ei)->tx_buffer + 1, ptr, ETH_ALEN);
+ 	do_command(ei, CMD0_IA_SETUP + CMD0_CHNL_1,
+ 		   8, EIPVT(ei)->tx_buffer);
+ 	while (jiffies < stop_time)
+ 		    ;
+   }
+   wait_for_done(ioaddr);
+ 
+   update_stop_hit(ioaddr, 8192);
+   DPRINTF((DBG_ZNOTE, "enabling RX\n"));
+   outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr);
+   ei->dev->if_tbusy = 0;
+ }
+ 
+ 
+ static void
+ znote_receive(struct ei *ei)
+ {
+   short next_frame_end_offset = 0; 	/* offset of next frame start */
+   short *cur_frame_end;
+   short cur_frame_end_offset;
+   int ioaddr = ei->ioaddr;
+   int boguscount = 1;
+ 
+   outb(CMD0_STAT2, ioaddr);
+   cur_frame_end_offset = inw(ioaddr);
+ 
+   if (cur_frame_end_offset == EIPVT(ei)->rx_cur - EIPVT(ei)->rx_buffer) {
+ 	printk("%s: interrupted, but nothing to receive, offset 0x%03X\n",
+ 		   ei->dev->if_name, cur_frame_end_offset);
+ 	return;
+   }
+ 
+   /*
+    * Use same method as the Crynwr driver: construct a forward list in
+    * the same area of the backwards links we now have.  This allows us to
+    * pass packets to the upper layers in the order they were received --
+    * important for fast-path sequential operations.
+    */
+   while (EIPVT(ei)->rx_buffer + cur_frame_end_offset != EIPVT(ei)->rx_cur
+ 			&& ++boguscount < 5) {
+ 	unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
+ 	int count, status;
+ 
+ 	if (cur_frame_end_offset < 4) {
+ 		/*
+ 		 * Oh no, we have a special case: the frame trailer
+ 		 * wraps around the end of the ring buffer.  We've
+ 		 * saved space at the end of the ring buffer for just
+ 		 * this problem.
+ 		 */
+ 		memcpy(EIPVT(ei)->rx_buffer + (RX_BUF_SIZE / 2),
+ 		       EIPVT(ei)->rx_buffer, 8);
+ 		cur_frame_end_offset += (RX_BUF_SIZE / 2);
+ 	}
+ 	cur_frame_end = EIPVT(ei)->rx_buffer + cur_frame_end_offset - 4;
+ 
+ 	lo_status = *cur_frame_end++;
+ 	hi_status = *cur_frame_end++;
+ 	status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
+ 	lo_cnt = *cur_frame_end++;
+ 	hi_cnt = *cur_frame_end++;
+ 	count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
+ 
+ 	DPRINTF((DBG_ZNOTE,
+ 		"constructing trailer at location 0x%03X, 0x%04X 0x%04X "
+ 		"0x%04X 0x%04X count 0x%X status 0x%04X\n",
+ 		cur_frame_end_offset<<1, lo_status, hi_status,
+ 		lo_cnt, hi_cnt, count, status));
+ 	cur_frame_end[-4] = status;
+ 	cur_frame_end[-3] = next_frame_end_offset;
+ 	cur_frame_end[-2] = count;
+ 	next_frame_end_offset = cur_frame_end_offset;
+ 	cur_frame_end_offset -= ((count + 1) >> 1) + 3;
+ 	if (cur_frame_end_offset < 0)
+ 		  cur_frame_end_offset += (RX_BUF_SIZE / 2);
+   };
+ 
+   /* Now step forward through the list. */
+   do {
+ 	ushort *this_rfp_ptr = EIPVT(ei)->rx_buffer + next_frame_end_offset;
+ 	int status = this_rfp_ptr[-4];
+ 	int pkt_len = this_rfp_ptr[-2];
+   
+ 	DPRINTF((DBG_ZNOTE,
+ 		"Looking at trailer ending at 0x%04X status 0x%04X "
+ 		"length 0x%03X next 0x%04X\n",
+ 		next_frame_end_offset<<1, status, pkt_len,
+ 		this_rfp_ptr[-3]<<1));
+ 
+ 	/* Once again we must assume that the i82586 docs apply. */
+ 	if (! (status & 0x2000)) {		/* there was an error */
+ 		ei->dev->if_stats->rx_errors++;
+ 		if (status & 0x0800)
+ 			ei->dev->if_stats->rx_crc++;
+ 		if (status & 0x0400)
+ 			ei->dev->if_stats->rx_frame++;
+ 		if (status & 0x0200)
+ 			ei->dev->if_stats->rx_overrun++; /* wrong */
+ 		if (status & 0x0100)
+ 			ei->dev->if_stats->rx_fifo++;
+ 		if (status & 0x0080)
+ 			ei->dev->if_stats->rx_length++;
+ 	} else {
+ 		/* Malloc up new buffer. */
+ 		struct sk_buff *skb;
+ 
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ 			printk("%s: memory squeeze, dropping packet\n",
+ 							ei->dev->if_name);
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		skb->len = pkt_len;
+ 		skb->dev = ei->dev;
+ 
+ 		if ((int) EIPVT(ei)->rx_cur + pkt_len >
+ 		    (int) EIPVT(ei)->rx_buffer + RX_BUF_SIZE) {
+ 			int semi_cnt = (int) EIPVT(ei)->rx_buffer +
+ 					     RX_BUF_SIZE -
+ 					(int) EIPVT(ei)->rx_cur;
+ 			memcpy(skb->raw, EIPVT(ei)->rx_cur, semi_cnt);
+ 			memcpy(skb->raw + semi_cnt, EIPVT(ei)->rx_buffer,
+ 				   pkt_len - semi_cnt);
+ 		} else {
+ 			memcpy(skb->raw, EIPVT(ei)->rx_cur, pkt_len);
+ 	  	}
+ 
+ 		if (dev_rint(ei->dev, skb, skb->len)) {
+ 			ei->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		ei->dev->if_stats->rx_packets++;
+ 	}
+ 
+ 	EIPVT(ei)->rx_cur = this_rfp_ptr;
+ 	if (EIPVT(ei)->rx_cur >= EIPVT(ei)->rx_buffer + (RX_BUF_SIZE / 2))
+ 		EIPVT(ei)->rx_cur -= (RX_BUF_SIZE / 2);
+ 
+ 	update_stop_hit(ioaddr, (EIPVT(ei)->rx_cur -
+ 				 EIPVT(ei)->rx_buffer) << 1);
+ 	next_frame_end_offset = this_rfp_ptr[-3];
+ 	if (next_frame_end_offset == 0)		/* read all the frames? */
+ 				break;		/* done for now */
+ 	this_rfp_ptr = EIPVT(ei)->rx_buffer + next_frame_end_offset;
+   } while (--boguscount);
+ 
+   /*
+    * If any worth-while packets have been received, dev_rint()
+    * has done a mark_bh(NET_BH) for us and will work on them
+    * when we get to the bottom-half routine.
+    */
+ }
+ 
+ 
+ /* The ZNote interrupt handler. */
+ static void
+ znote_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct ei *ei = znote_irq2ei_map[irq];
+   int ioaddr, boguscnt = 20;
+ 
+   if (ei == NULL) {
+ 	printk("ZNote: interrupt: IRQ%d for unknown device\n", irq);
+ 	return;
+   }
+   ei->interrupt = 1;
+   ioaddr = ei->ioaddr;
+ 
+   outb(CMD0_STAT0, ioaddr);
+   do {
+ 	ushort status = inb(ioaddr);
+ 
+ #ifdef BECKER_DEBUG
+ 	{
+ 		ushort result, rx_ptr, running;
+ 
+ 		outb(CMD0_STAT1, ioaddr);
+ 		result = inw(ioaddr);
+ 		outb(CMD0_STAT2, ioaddr);
+ 		rx_ptr = inw(ioaddr);
+ 		outb(CMD0_STAT3, ioaddr);
+ 		running = inb(ioaddr);
+ 
+ 		DPRINTF((DBG_ZNOTE,
+ 		    "%s: interrupt, status %02X, %04X %04X %02X serial %d\n",
+ 			ei->dev->if_name, status, result,
+ 			rx_ptr, running, boguscnt));
+ 	}
+ #endif
+ 	if ((status & 0x80) == 0) break;
+ 
+ 	if ((status & 0x0F) == 4) {	/* transmit done */
+ 		int tx_status;
+ 
+ 		outb(CMD0_STAT1, ioaddr);
+ 		tx_status = inw(ioaddr);
+ 
+ 		/* It's undocumented, but tx_status seems to match the i82586. */
+ 		if (tx_status & 0x2000) {
+ 			ei->dev->if_stats->tx_packets++;
+ 			ei->dev->if_stats->tx_collision += (tx_status & 0xf);
+ 			ei->dev->if_tbusy = 0;
+ 			mark_bh(NET_BH);
+ 		} else {
+ 			if (tx_status & 0x0600)
+ 				ei->dev->if_stats->tx_carrier++;
+ 			if (tx_status & 0x0100)
+ 				ei->dev->if_stats->tx_fifo++;
+ 			if (!(tx_status & 0x0040))
+ 				ei->dev->if_stats->tx_heartbeat++;
+ 			if (tx_status & 0x0020)
+ 				ei->dev->if_stats->tx_aborted++;
+ 
+ 			/* ...and the catch-all. */
+ 			if (tx_status | 0x0760 != 0x0760)
+ 				ei->dev->if_stats->tx_errors++;
+ 		}
+ 	}
+ 
+ 	if ((status & 0x40) || (status & 0x0f) == 11) {
+ 		znote_receive(ei);
+ 	}
+ 
+ 	/* Clear the interrupts we've handled. */
+ 	outb(CMD0_ACK, ioaddr);
+   } while (boguscnt--);
+ 
+   ei->interrupt = 0;
+ }
+ 
+ 
+ static int
+ znote_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct ei *ei;
+   short ioaddr;
+     
+   ei = becker_ctrl[dev->if_unit];
+   if (ei == NULL) {
+ 	printk("%s: unknown device in znote_xmit ???\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = ei->ioaddr;
+ 
+   /* Transmitter timeout, could be a serious problems. */
+   if (dev->if_tbusy) {
+ 	ushort event, tx_status, rx_offset, state;
+ 	int ticksofar = (jiffies - dev->if_txtimer);
+ 
+ 	if (ticksofar < 10) return(1);
+ 
+ 	outb(CMD0_STAT0, ioaddr);
+ 	event = inb(ioaddr);
+ 	outb(CMD0_STAT1, ioaddr);
+ 	tx_status = inw(ioaddr);
+ 	outb(CMD0_STAT2, ioaddr);
+ 	rx_offset = inw(ioaddr);
+ 	outb(CMD0_STAT3, ioaddr);
+ 	state = inb(ioaddr);
+ 	printk("%s: transmit timed out, status 0x%02X 0x%04X 0x%04X 0x%02X,"
+ 		" resetting\n", dev->if_name, event, tx_status,
+ 		rx_offset, state);
+ 	if (tx_status == 0x0400)
+ 		printk("%s: TX carrier error, check transceiver cable\n",
+ 				 dev->if_name);
+ 
+ 	outb(CMD0_RESET, ioaddr);
+ 	znote_card_init(ei);
+ 	return(1);
+   }
+ 
+   /*
+    * Some higher layer thinks we've missed an tx-done interrupt.
+    * Caution: dev_tint() handles the cli()/sti() itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   DPRINTF((DBG_ZNOTE, "%s: znote_xmit() called\n", dev->if_name));
+ 
+   /* Check that the cart hasn't reset itself, probably from suspend. */
+   outb(CMD0_STAT0, ioaddr);
+   if (inw(ioaddr) == 0x0010 &&
+       inw(ioaddr) == 0x0000 &&
+       inw(ioaddr) == 0x0010) znote_card_init(ei);
+ 
+   /*
+    * Block a timer-based transmit from overlapping.  This could better be
+    * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+    */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 	printk("%s: transmitter access conflict\n", dev->if_name);
+     else {
+ 	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ 
+ 	EIPVT(ei)->tx_cur = EIPVT(ei)->tx_buffer;
+ 	*EIPVT(ei)->tx_cur++ = length;
+ 	memcpy(EIPVT(ei)->tx_cur, skb->raw, skb->len);
+ 	EIPVT(ei)->tx_cur += (length + 1) >> 1;
+ 	*EIPVT(ei)->tx_cur = 0;
+ 	do_command(ei, CMD0_TRANSMIT + CMD0_CHNL_1,
+ 		   length + 4, EIPVT(ei)->tx_buffer);
+ 	dev->if_txtimer = jiffies;
+ 
+ 	DPRINTF((DBG_ZNOTE,
+ 		"%s: transmitter queued, length %d\n", dev->if_name, length));
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ znote_close(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   ei->dev->if_tbusy = 1;
+   ei->start = 0;
+ 
+   outb(CMD0_RESET, ioaddr);		/* CMD0_RESET */
+ 
+   disable_dma(EIPVT(ei)->rx_dma);
+   disable_dma(EIPVT(ei)->tx_dma);
+   free_irq(ei->irq);
+ 
+   DPRINTF((DBG_ZNOTE, "%s: shutting down ethercard\n", ei->dev->if_name));
+ 
+   /* Turn off transceiver power. */
+   outb(0x10, 0xe6);			/* select LAN control register */
+   outb(inb(0xe7) & ~0x84, 0xe7); 	/* turn on LAN power (bit 2) */
+ 
+   return(0);
+ }
+ 
+ 
+ /* Open/initialize the board. */
+ static int
+ znote_open(struct ei *ei)
+ {
+   int ioaddr = ei->ioaddr;
+ 
+   DPRINTF((DBG_ZNOTE, "%s: znote_open() called\n", ei->dev->if_name));
+ 
+   /* Turn on the 82501 SIA, using zenith-specific magic. */
+   outb(0x10, 0xe6);			/* select LAN control register */
+   outb(inb(0xe7) | 0x84, 0xe7);		/* turn on LAN power (bit 2) */
+ 
+   /*
+    * According to the Crynwr driver we should wait 50 msec. for the
+    * LAN clock to stabilize.  My experiments indicates that the '593 can
+    * be initialized immediately.  The delay is probably needed for the
+    * DC-to-DC converter to come up to full voltage, and for the oscillator
+    * to be spot-on at 20Mhz before transmitting.
+    * Until this proves to be a problem we rely on the higher layers for the
+    * delay and save allocating a timer channel.
+    */
+    EIPVT(ei)->rx_cur = EIPVT(ei)->rx_buffer;
+    EIPVT(ei)->tx_cur = EIPVT(ei)->tx_buffer;
+ 
+   /* Protect against a DMA flip-flop reset by an interrupting task. */
+   cli(); {
+ 	/* Hmmm, these probably aren't needed? */
+ 	disable_dma(EIPVT(ei)->rx_dma);
+ 	disable_dma(EIPVT(ei)->tx_dma);
+ 
+ 	clear_dma_ff(EIPVT(ei)->rx_dma);
+ 	set_dma_mode(EIPVT(ei)->rx_dma, DMA_RX_MODE);
+ 	set_dma_mode(EIPVT(ei)->tx_dma, DMA_TX_MODE);
+ 
+ 	set_dma_addr(EIPVT(ei)->rx_dma, (unsigned int) EIPVT(ei)->rx_buffer);
+ 	set_dma_count(EIPVT(ei)->rx_dma, RX_BUF_SIZE);
+ 
+ 	set_dma_addr(EIPVT(ei)->tx_dma, (unsigned int) EIPVT(ei)->tx_buffer);
+ 	set_dma_count(EIPVT(ei)->tx_dma, TX_BUF_SIZE);
+ 
+ 	/* Enable both DMA channels. */
+ 	enable_dma(EIPVT(ei)->rx_dma);
+ 	enable_dma(EIPVT(ei)->tx_dma);
+   } sti();
+ 
+   /* Reset the chip, and start it up. */
+   outb(CMD0_RESET, ioaddr);
+ 
+   /* This follows the packet driver's lead, and checks for success. */
+   if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
+ 	printk("%s: problem turning on the transceiver power\n",
+ 						 ei->dev->if_name);
+ 
+   ei->dev->if_tbusy = 0;
+   ei->interrupt = 0;
+ 
+   znote_card_init(ei);
+   ei->start = 1;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adapter.
+  *
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets,
+  *			and do best-effort filtering.
+  *
+  * As a side effect this routine must also initialize the device parameters.
+  * This is taken advantage of in open().
+  *
+  * N.B. that we change i593_init[] in place.  This (properly) makes the
+  * mode change persistent, but must be changed if this code is moved to
+  * a multiple adaptor environment.
+  */
+ static void
+ znote_set_multicast(struct ei *ei, int num_addrs, void *addrs)
+ {
+   if (num_addrs < 0) {
+ 	/* Enable promiscuous mode */
+ 	i593_init[7] &= ~3;
+ 	i593_init[7] |= 1;
+ 	i593_init[13] &= ~8;
+ 	i593_init[13] |= 8;
+   } else if (num_addrs > 0) {
+ 	/* Enable accept-all-multicast mode */
+ 	i593_init[7] &= ~3;
+ 	i593_init[7] |= 0;
+ 	i593_init[13] &= ~8;
+ 	i593_init[13] |= 8;
+   } else {
+ 	/* Enable normal mode. */
+ 	i593_init[7] &= ~3;
+ 	i593_init[7] |= 0;
+ 	i593_init[13] &= ~8;
+ 	i593_init[13] |= 0;
+   }
+ 
+   EIPVT(ei)->tx_buffer[0] = sizeof(i593_init);
+   memcpy(EIPVT(ei)->tx_buffer + 1, i593_init, sizeof(i593_init));
+   do_command(ei, CMD0_CONFIGURE+CMD0_CHNL_1,
+ 	     sizeof(i593_init) + 2, EIPVT(ei)->tx_buffer);
+ 
+ #ifdef not_tested
+   if (num_addrs > 0) {
+ 	EIPVT(ei)->tx_buffer[0] = 6*num_addrs;
+ 	memcpy(EIPVT(ei)->tx_buffer + 1, addrs, EIPVT(ei)->tx_buffer[0]);
+ 	do_command(ioaddr, CMD0_MULTICAST_LIST+CMD0_CHNL_1,
+ 		   EIPVT(ei)->tx_buffer[0] + 2, EIPVT(ei)->tx_buffer);
+   }
+ #endif
+ }
+ 
+ 
+ /*
+  * The Z-Note probe is pretty easy since it can
+  * only happen on a Zenith.
+  */
+ int
+ znote_probe(struct ei *ei)
+ {
+   struct sockaddr sa;
+   struct netidblk *netinfo;
+   unsigned char *ptr;
+   char *p;
+   int i;
+ 
+   /* Set up the interface name. */
+   ei->name = "Z-Note";
+ 
+   /*
+    * This code should scan the region 0xf0000 to 0xfffff
+    * for a "NETIDBLK".  The alpha version cheats.
+    */
+   for(p = (char *)0xf0000; p < (char *)0x100000; p++) {
+ 	if (*p == 'N' && strncmp(p, ZNMAGIC, 8) == 0) break;
+   }
+   if (p >= (char *)0x100000) {
+ 	DPRINTF((DBG_ZNOTE, "No Z-Note ethernet adaptor found\n"));
+ 	return(-ENODEV);
+   }
+   netinfo = (struct netidblk *) p;
+   ei->ioaddr = netinfo->iobase1;
+   ei->iobase = netinfo->iobase1;
+   ei->irq = netinfo->irq1;
+ 
+   DPRINTF((DBG_ZNOTE, "%s: %s at 0x%3X,",
+ 		ei->dev->if_name, ei->name, ei->ioaddr));
+ 
+   /* The station address is in the "netidblk" at 0x0f0000. */
+   ptr = (unsigned char *) sa.sa_data;
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	ptr[i] = netinfo->netid[i];
+ 	DPRINTF((DBG_ZNOTE, " %2.2X", sa.sa_data[i]));
+   }
+ 
+   DPRINTF((DBG_ZNOTE, ", using IRQ%d DMA%d and DMA%d\n",
+ 			ei->irq, netinfo->dma1, netinfo->dma2));
+ 
+   /* Attach the ETH service. */
+   eth_setup(ei->dev);
+   (void) ei->dev->mac_aset(ei->dev, &sa);
+ 
+   /* Is this card- or chip specific? */
+   ei->dev->xmit = znote_xmit;
+   ei->chip_s_multi = znote_set_multicast;
+ 
+   /* Attach the card-specific routines. */
+   ei->card_init = NULL;
+   ei->card_open = znote_open;
+   ei->card_close = znote_close;
+   ei->card_input = NULL;
+   ei->card_output = NULL;
+ 
+   DPRINTF((DBG_ZNOTE, "%s: vendor '%16.16s' IRQ1%d IRQ2%d DMA1%d DMA2%d\n",
+ 	ei->dev->if_name, netinfo->vendor, netinfo->irq1,
+ 	netinfo->irq2, netinfo->dma1, netinfo->dma2));
+   DPRINTF((DBG_ZNOTE,
+ 	"%s: iobase1 0x%X size %d iobase2 0x%X size %d net type %02.2X\n",
+ 	ei->dev->if_name, netinfo->iobase1, netinfo->iosize1,
+ 	netinfo->iobase2, netinfo->iosize2, netinfo->nettype));
+ 
+   EIPVT(ei) = (struct znote *) kmalloc(sizeof(struct znote), GFP_ATOMIC);
+   if (EIPVT(ei) == NULL) {
+ 	printk("ZNote: %s: no memory for private data !\n", ei->dev->if_name);
+ 	return(-ENOMEM);
+   }
+   memset(EIPVT(ei), 0, sizeof(struct znote));
+   EIPVT(ei)->rx_dma = netinfo->dma1;
+   EIPVT(ei)->tx_dma = netinfo->dma2;
+ 
+   /* These should never fail. You can't add devices to a sealed box! */
+   if (request_irq(ei->irq, znote_interrupt) ||
+       request_dma(EIPVT(ei)->rx_dma) ||
+       request_dma(EIPVT(ei)->tx_dma)) {
+ 		printk("ZNote: not opened -- resource busy ?!?\n");
+ 		return(-EBUSY);
+   }
+   znote_irq2ei_map[ei->irq] = ei;
+ 
+   /*
+    * Allocate buffer memory. We can cross a 128K boundary, so we
+    * must be careful about the allocation.  It's easiest to waste 8K.
+    */
+   if (dma_page_eq(&dma_buffer[0], &dma_buffer[RX_BUF_SIZE])) {
+ 	EIPVT(ei)->rx_buffer = (short *)dma_buffer;
+ 	if (dma_page_eq(&dma_buffer[RX_BUF_SIZE + 8],
+ 			&dma_buffer[RX_BUF_SIZE + 8 + TX_BUF_SIZE]))
+ 		EIPVT(ei)->tx_buffer = (short *)&dma_buffer[RX_BUF_SIZE+8];
+ 	  else
+ 		EIPVT(ei)->tx_buffer = (short *)&dma_buffer[2*RX_BUF_SIZE];
+   } else {
+ 	EIPVT(ei)->rx_buffer = (short *)&dma_buffer[RX_BUF_SIZE];
+ 	EIPVT(ei)->tx_buffer = (short *)&dma_buffer[2*RX_BUF_SIZE + 8];
+   }
+ 
+   return(0);
+ }
*** original/net/drv/depca/HISTORY	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/HISTORY	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,95 ----
+ Revision 0.1:
+  working ALPHA-release (at least with an DE200 (DEC Etherworks Turbo))
+  no tuning attempted, no cli/sti's in code (?) 
+ 
+ Revision 0.2:
+  - toggle led of card in interrupt-routine
+  - dont disable cards interrupts when in isr. The ony communication with
+    the card now is to read the interrupt-cause, and, in case of trans-
+    mitting another packet, toggling the CSR0_TDMD - Bit. Also the 
+    address-register is no longer set before writes to the data-register, 
+    because it points during normal operation always to CSR0
+  - enable interrupts at entry to isr. They seem not to harm. This is
+    a result of the card's communication-logic: there is a well defined
+    ownership of each buffer, and the change-mechanism of the ownership-
+    status is also well defined: we may give the ownership to the card,
+    and the card gives the ownership to us. 
+  - inside the isr, poll the interupt-status until no more interrupt-
+    reason-bits set. 
+    This because I found that after reading the isr and doing the ack
+    for this, imedeatly another interrrupt bit may apear in
+    the isr (for which no other interrupt will occur). This happened
+    only under very high load and caused the card to become inoperable.
+    (I think because it expected another int-ack). After the modification,
+    i was not able to break it again, although the procedure looks not
+    completely save...
+ 
+ Revision 0.3:
+  - 0.2 Version didn't compile with pl10
+  - cosmetic changes
+  - handle IDON-interrupt seperate (also cosmetic)
+ 
+ Revision 0.4:
+  - fixed bug in isRom (shoud be <<9 and not <<8) (This will only harm 
+    those, which have rom installed at the address where the depca wants 
+    to live)
+  - fixed bug in adapter_init, which will make depca's with address-
+    bases not at 64K-boundary not to work (This is true i think for
+    all depca's with less than 64K address-space... :-)
+  - reduced calls to xlateAndMap to a minimum
+ 
+ Revision 0.5:
+  - Supports multiple depcas now. (as proposed by bob@inmos.co.uk)
+    (the max will be 2, because the iobase can be either 200 or 300)
+    The easiest way to do this was to use classes, so i did so... 
+    The side effect is, that it only compiles now with pl11 (pl10 is 
+    no longer supported, also i see no reason why c++ specific parts
+    could not be linked to pl10). 
+    I choosed the irq to be the index to a driver (so no two depcas 
+    can live at the same irq), because this is readily available in 
+    the interrupt.
+    Some of the mechanisms built in here could be moved to a higher layer 
+    (baseclass) because they are not specific to a special ethernet driver.
+    (e.g. struct device should  become class, then each driver can
+    inherit from it, and overload the open/close etc with its specific
+    implementation...)
+    Also, the structures associated with the lance memory could become
+    a class too.
+ 
+ Revision 0.6:
+  - more than 1 Transmitbuffer supported (1,2,4,8...) (But doesnt work
+    the expected way...
+  - missing "" in KnownSignatures added
+ 
+ Revision 0.7:
+  - nothing real new (Probably I indtroduced some bugs, but this then 
+    happended by accident).
+  - this c++ code now compiles in the 12++ fallback to c ... ;;-# (hope-
+    fully some other people whine about that too)
+ 
+ Revision 0.8:
+  - patches now against pl14 (no real change, only kmalloc -> alloc_skb)
+  - if device gets closed, the depca-rom is reenabled in order to allow
+    the faulty msdos-driver to detect the board after reboot. The device
+    gets closed if a "ifconfig eth0 down" is issued (Probably one might
+    want to write a shutdown script which does this...).
+  - (not new:) some reported that they receive the message "xmit full" from
+    the driver. This message says, that the driver wasnt able to send a
+    packet imedeatly because all xmit-buffers were still full. There is
+    currently only one xmit buffer supported (I think there is needed some
+    additional card-programming to use more than one, but dont know). So,
+    those who receive the xmit-full should comment out the associated printk
+    (Sorry, I gave some people recently the wrong tip to increase the number
+    of xmit-buffers...)
+  
+ (the revision counter gets ++ each time something gets changed after I have
+  given a copy to someone)
+ 
+ Revision 0.8DDI:
+   - ported to NET-2e DDI, FvK, Jacco
+ 
+ Revision 1.0:
+   - used the new driver by David C. Davies as a base.
+ 
+ Revision 1.1
+   - updated with DEPCA-0.35 fixes.
*** original/net/drv/depca/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/Makefile	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,36 ----
+ #
+ # Makefile for the Linux DEPCA device driver.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ OBJS	= main.o utils.o ioctl.o packet.o
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ depca.o:	$(OBJS)
+ 		ld -r -o depca.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/depca/README	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/README	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,95 ----
+     The driver was based on the 'lance.c' driver from Donald Becker which is
+     included with the standard driver distribution for linux. Modifications
+     were made to most routines and the hardware recognition routines were
+     written from scratch. Primary references used were:
+ 
+     1) Lance.c code in /linux/drivers/net/
+     2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
+        AMD, 1992 [(800) 222-9323].
+     3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
+        AMD, Pub. #17881, May 1993.
+     4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
+        AMD, Pub. #16907, May 1992
+     5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
+        Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
+     6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
+        Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
+     7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
+        Digital Equipment Corporation, 1989
+     8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
+        Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
+     
+     Peter Bauer's depca.c (V0.5) was referred to when debugging this driver.
+     The hash filter code was  derived from Reference  3 and has been  tested
+     only to the extent that the Table  A-1, page A-7,  was confirmed to fill
+     the   filter bit   positions  correctly.  Hash   filtering  is  not  yet
+     implemented in the current driver set.
+ 
+     The original DEPCA  card requires that  the ethernet ROM address counter
+     be enabled to count and has an 8 bit NICSR.  The ROM counter enabling is
+     only done when  a 0x08 is read as  the first address octet  (to minimise
+     the chances of  writing over some other hardware's  I/O  register).  The
+     size of the NICSR is tested by a word read: if  both bytes are the same,
+     the register  is 8 bits wide.   Also,  there is a   maximum of only 48kB
+     network  RAM for   this card.  My   thanks  to Torbjorn Lindh   for help
+     debugging all this  (and holding  my  feet to the   fire until I got  it
+     right).
+ 
+     The DE200  series  boards have  on-board 64kB  RAM for  use  as a shared
+     memory network  buffer. Only the DE100  cards make use  of a  2kB buffer
+     mode which has not  been implemented in  this driver (only the 32kB  and
+     64kB modes are supported [16kB/48kB for the original DEPCA]).
+ 
+     At the  most only 2 DEPCA  cards can be supported  because there is only
+     provision for two I/O base addresses on the cards (0x300 and 0x200). The
+     base  address is  'autoprobed' by  looking  for  the self  test PROM and
+     detecting the  card name.  The shared memory  base address is decoded by
+     'autoprobing' the Ethernet PROM address information. The second DEPCA is
+     detected and information placed  in the base_addr  variable of the  next
+     device  structure   (which is created    if  necessary),  thus  enabling
+     ethif_probe initialization for the device.
+ 
+     ************************************************************************
+ 
+     NOTE: If you are using two  DEPCAs, it is important  that you assign the
+     base memory addresses  correctly.  The driver  autoprobes I/O 0x300 then
+     0x200.  The base memory  address for the  first device must be less than
+     that of the second so that the auto probe will  correctly assign the I/O
+     and  memory addresses on  the same card.  I can't  think of  a way to do
+     this unambiguously at the moment, since there is nothing on the cards to
+     tie I/O and memory information together.
+ 
+     I am unable  to  test  2 cards   together for now,    so this  code   is
+     unchecked. All reports, good or bad, are welcome.
+ 
+     ************************************************************************
+ 
+     The board IRQ   setting must be  at an  unused IRQ which  is auto-probed
+     using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
+     {2,3,4,5,7}, whereas the  DE200 is at {5,9,10,11,15}.  Note that IRQ2 is
+     really IRQ9 in machines with 16 IRQ lines.
+ 
+     No 16MB memory  limitation should exist with this  driver as DMA is  not
+     used and the common memory area is in low memory on the network card (my
+     current system has 20MB and I've not had problems yet).
+ 
+     TO DO:
+     ------
+ 
+     1. Implement the 2k buffer mode - does anyone need it??
+ 
+     Revision History
+     ----------------
+ 
+     Version   Date        Description
+   
+       0.1   25-jan-94     Initial writing.
+       0.2   27-jan-94     Added LANCE TX buffer chaining.
+       0.3    1-feb-94     Added multiple DEPCA support.
+       0.31   4-feb-94     Added DE202 recognition.
+       0.32  19-feb-94     Tidy up. Improve multi-DEPCA support.
+       0.33  25-feb-94     Fix DEPCA ethernet ROM counter enable.
+                           Add jabber packet fix from murf@perftech.com
+ 			  and becker@super.org
+       0.34   7-mar-94     Fix DEPCA max network memory RAM & NICSR access.
+       0.35   8-mar-94     Added DE201 recognition.
*** original/net/drv/depca/depca.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/depca.h	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,44 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DEPCA device driver.
+  *
+  *		Define the DEPCA device driver interface and constants.
+  *
+  * Version:	@(#)depca.h	1.0.0	02/28/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Peter Bauer, Systematika GmbH, <100136.3530@compuserve.com>
+  * 		David C. Davies, <davies@wanton.enet.dec.com>
+  */
+ #ifndef _DEPCA_H
+ #define _DEPCA_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	DEPCA_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  DEPCA_DEBUG
+ #   define DEPCA_DEBUG
+ #endif
+ 
+ #ifdef	DEPCA_DEBUG
+ #   define DPRINTF(x)	depca_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ #define	DEPCA_MAX_DEV	4
+ 
+ 
+ /* DEPCA feature selection. */
+ 
+ 
+ extern int		depca_init(struct ddi *ddi);
+ 
+ 
+ #endif	/* _DEPCA_H */
*** original/net/drv/depca/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/global.h	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,202 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DEPCA device driver.
+  *
+  *		Define the DEPCA device driver internal functions.
+  *
+  * Version:	@(#)global.h	1.0.1	04/25/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Peter Bauer, Systematika GmbH, <100136.3530@compuserve.com>
+  * 		David C. Davies, <davies@wanton.enet.dec.com>
+  */
+ #ifndef _DEPCA_GLOBAL_H
+ #define _DEPCA_GLOBAL_H
+ 
+ 
+ /* I/O constant tables. Add here for more. */
+ #define DEPCA_IO_PORTS	{ 0x300, 0x200, 0 }
+ #define DEPCA_IRQ	{ 5, 9, 10, 11, 15, 0 }
+ #define DEPCA_RAM_BASES	{ 0xc0000, 0xd0000, 0xe0000, 0x00000 }
+ #define DEPCA_SIGNATURE { "DEPCA", "DE100", "DE200", "DE201", \
+ 			  "DE202", "DE210", "" }
+ #define DEPCA_NAME_LEN	8
+ 
+ 
+ /*
+  * I/O addresses. Note that the 2k buffer option is not supported in
+  * this driver.
+  */
+ #define DEPCA_NICSR	(ioaddr+0x00)   /* Network interface CSR */
+ #define DEPCA_RBI	(ioaddr+0x02)   /* RAM buffer index (2k buffer mode) */
+ #define DEPCA_DATA	(ioaddr+0x04)   /* LANCE registers' data port */
+ #define DEPCA_ADDR	(ioaddr+0x06)   /* LANCE registers' address port */
+ #define DEPCA_PROM	(ioaddr+0x0c)   /* Ethernet address ROM data port */
+ #define DEPCA_RBSA	(ioaddr+0x0e)   /* RAM buffer starting address (2k buff.) */
+ 
+ /* These are LANCE registers addressable through DEPCA_ADDR. */
+ #define CSR0		0
+ #define CSR1		1
+ #define CSR2		2
+ #define CSR3		3
+ 
+ /* NETWORK INTERFACE CSR (NI_CSR) bit definitions. */
+ #define TO		0x0100		/* Time Out for remote boot */
+ #define SHE      	0x0080		/* SHadow memory Enable */
+ #define BS       	0x0040		/* Bank Select */
+ #define BUF      	0x0020		/* BUFfer size (1->32k, 0->64k) */
+ #define RBE      	0x0010		/* Remote Boot Enable (1->net boot) */
+ #define AAC      	0x0008		/* Address ROM Address Counter (1->enable) */
+ #define IM       	0x0004		/* Interrupt Mask (1->mask) */
+ #define IEN      	0x0002		/* Interrupt tristate ENable (1->enable) */
+ #define LED      	0x0001		/* LED control */
+ 
+ /* Control and Status Register 0 (CSR0) bit definitions. */
+ #define ERR		0x8000		/* Error summary */
+ #define BABL    	0x4000		/* Babble transmitter timeout error  */
+ #define CERR    	0x2000		/* Collision Error */
+ #define MISS    	0x1000		/* Missed packet */
+ #define MERR    	0x0800		/* Memory Error */
+ #define RINT    	0x0400		/* Receiver Interrupt */
+ #define TINT    	0x0200		/* Transmit Interrupt */
+ #define IDON    	0x0100		/* Initialization Done */
+ #define INTR    	0x0080		/* Interrupt Flag */
+ #define INEA    	0x0040		/* Interrupt Enable */
+ #define RXON    	0x0020		/* Receiver on */
+ #define TXON    	0x0010		/* Transmitter on */
+ #define TDMD    	0x0008		/* Transmit Demand */
+ #define STOP    	0x0004		/* Stop */
+ #define STRT    	0x0002		/* Start */
+ #define INIT    	0x0001		/* Initialize */
+ 
+ /* CONTROL AND STATUS REGISTER 3 (CSR3). */
+ #define BSWP		0x0004		/* Byte SWaP */
+ #define ACON		0x0002		/* ALE control */
+ #define BCON		0x0001		/* Byte CONtrol */
+ 
+ /* Initialization Block Mode Register. */
+ #define PROM		0x8000		/* Promiscuous Mode */
+ #define EMBA		0x0080		/* Enable Modified Back-off Algorithm */
+ #define INTL		0x0040		/* Internal Loopback */
+ #define DRTY		0x0020		/* Disable Retry */
+ #define COLL		0x0010		/* Force Collision */
+ #define DTCR		0x0008		/* Disable Transmit CRC */
+ #define LOOP		0x0004		/* Loopback */
+ #define DTX		0x0002		/* Disable the Transmitter */
+ #define DRX		0x0001		/* Disable the Receiver */
+ 
+ /* Receive Message Descriptor 1 (RMD1) bit definitions. */
+ #define R_OWN		0x80000000	/* Owner bit 0 = host, 1 = lance */
+ #define R_ERR		0x4000		/* Error Summary */
+ #define R_FRAM		0x2000		/* Framing Error */
+ #define R_OFLO		0x1000		/* Overflow Error */
+ #define R_CRC		0x0800		/* CRC Error */
+ #define R_BUFF		0x0400		/* Buffer Error */
+ #define R_STP		0x0200		/* Start of Packet */
+ #define R_ENP		0x0100		/* End of Packet */
+ 
+ /* Transmit Message Descriptor 1 (TMD1) bit definitions. */
+ #define T_OWN		0x80000000	/* Owner bit 0 = host, 1 = lance */
+ #define T_ERR		0x4000		/* Error Summary */
+ #define T_ADD_FCS	0x2000		/* More the 1 retry needed to Xmit */
+ #define T_MORE		0x1000		/* >1 retry to transmit packet */
+ #define T_ONE		0x0800		/* 1 try needed to transmit the packet */
+ #define T_DEF		0x0400		/* Deferred */
+ #define T_STP		0x02000000	/* Start of Packet */
+ #define T_ENP		0x01000000	/* End of Packet */
+ 
+ /* Transmit Message Descriptor 3 (TMD3) bit definitions. */
+ #define TMD3_BUFF	0x8000		/* BUFFer error */
+ #define TMD3_UFLO	0x4000		/* UnderFLOw error */
+ #define TMD3_RES	0x2000		/* REServed */
+ #define TMD3_LCOL	0x1000		/* Late COLlision */
+ #define TMD3_LCAR	0x0800		/* Loss of CARrier */
+ #define TMD3_RTRY	0x0400		/* ReTRY error */
+ 
+ /* Miscellaneous. */
+ #define MASK_INTERRUPTS		1
+ #define UNMASK_INTERRUPTS	0
+ #define PROBE_LENGTH		32
+ #define PROBE_SEQUENCE		"FF0055AAFF0055AA"
+ #define CRC_POLYNOMIAL		0x04c11db7       /* Ethernet CRC polynomial */
+ #define MAX_NUM_DEPCAS		2
+ #define DEPCA_TOTAL_SIZE	16
+ 
+ 
+ /* Set the number of Tx and Rx buffers. */
+ #define RING_SIZE	16              /* 16 buffers */
+ 
+ /* FIXME: These should be written as ETH_xxx constants. */
+ #define PKT_BUF_SZ	(ETH_FRAME_LEN + 30) /* Buffer size for each Tx/Rx buffer */
+ 
+ 
+ /* The DEPCA Rx and Tx ring descriptors. */
+ struct depca_rx_head {
+   long	base;
+   short	buf_length;		/* This length is negative 2's complement! */
+   short	msg_length;		/* This length is "normal". */
+ };
+ 
+ struct depca_tx_head {
+   long	base;
+   short	length;			/* This length is negative 2's complement! */
+   short	misc;			/* Errors and TDR info */
+ };
+ 
+ struct depca_ring_info {
+ };
+ 
+ 
+ /* The Lance initialization block, described in databook, in common memory. */
+ struct depca_init {
+   unsigned short	mode;			/* Mode register */
+   unsigned char		phys_addr[ETH_ALEN];	/* Physical ethernet address */
+   unsigned short	filter[4];	/* Multicast filter. */
+   unsigned long		rx_ring;     	/* Rx ring base pointer & ring length */
+   unsigned long		tx_ring;	/* Tx ring base pointer & ring length */
+ };
+ 
+ 
+ struct depca_pvt {
+   struct depca_rx_head *rx_ring;	/* Ptr to start of RX desc ring */
+   struct depca_tx_head *tx_ring;	/* Ptr to start of TX desc ring */
+   struct depca_init	init_block;	/* Initialization block */
+   long			dma_buffs;	/* Start address of Rx and Tx buffers */
+   int			cur_rx,		/* The next free ring entry */
+ 			cur_tx;
+   int			dirty_rx,	/* The ring entries to be free()ed. */
+ 			dirty_tx;
+   short			ringSize;	/* ring size based on available memory */
+   short			rmask;		/* modulus mask based on ring size */
+   long			rlen;		/* log2(ringSize) for the descriptors */
+ };
+ 
+ 
+ struct depca {
+   char			*name;
+   int			ioaddr;		/* I/O address to use		*/
+   int			irq;		/* IRQ channel to use		*/
+   int			dma;
+   int			depca_na;
+   unsigned char		interrupt;	/* we are handling an intr	*/
+   unsigned char		start;		/* we are up && running		*/
+   unsigned long		mem_start;	/* Shared mem start.		*/
+ 
+   /* Various fields. */
+   struct ifnet		*dev;		/* easy for intr handling	*/
+ 
+   struct depca_pvt	*pvt;		/* private info block in shmem	*/
+ };
+ 
+ 
+ extern struct depca	*dp_ctrl[];
+ extern int		depca_debug;
+ 
+ 
+ extern int		depca_probe(struct depca *);
+ extern int		depca_ioctl(struct inode *inode, struct file *file,
+ 				    unsigned int cmd, unsigned long arg);
+ extern int		depca_dbg_ioctl(unsigned long arg, int level);
+ extern void		depca_dprintf(char *fmt, ...);
+ 
+ #endif	/* _DEPCA_GLOBAL_H */
*** original/net/drv/depca/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/ioctl.c	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,124 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DEPCA device driver.
+  *
+  *		DEPCA I/O control request handler.
+  *
+  * Version:	@(#)ioctl.c	1.0.1	04/05/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Peter Bauer, Systematika GmbH, <100136.3530@compuserve.com>
+  * 		David C. Davies, <davies@wanton.enet.dec.com>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "depca.h"
+ #include "global.h"
+ 
+ 
+ /* Check if the new MTU setting is OK. */
+ static int
+ depca_check_mtu(struct ifnet *dev, int new_mtu)
+ {
+   if ((new_mtu < ETH_ZLEN) || (new_mtu >= ETH_FRAME_LEN)) return(-EINVAL);
+   dev->if_mtu = new_mtu;
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ depca_check_probe(struct depca *dp, struct depca *newdp)
+ {
+   struct depca depca;
+   int i;
+ 
+   depca = *dp;
+   *dp = *newdp;
+ 
+   if ((i = depca_probe(dp)) == 0) return(0);
+   *dp = depca;
+   return(i);
+ }
+ 
+ 
+ /* Perform I/O control on an active DEPCA unit. */
+ int
+ depca_ioctl(struct inode *inode, struct file *file,
+ 	    unsigned int cmd, unsigned long arg)
+ {
+   struct depca new, *dp;
+   struct ddi tmp_ddi;
+   struct ifnet *dev;
+   int err;
+ 
+   DPRINTF(("DEPCA: ioctl(0x%X, 0x%X, 0x%X)\n", inode, cmd, arg));
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(depca_dbg_ioctl(arg, 1));
+ 
+ 	case DDIOCGCONF:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memset(&tmp_ddi, 0, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= DEPCA_MAX_DEV) return(-ENODEV);
+ 		dp = dp_ctrl[dev->if_unit];
+ 		if (dp == NULL) return(-ENODEV);
+ 
+ 		tmp_ddi.io = dp->ioaddr;
+ 		tmp_ddi.irq = dp->irq;
+ 		memcpy_tofs((void *)arg, &tmp_ddi, sizeof(struct ddi));
+ 
+ 		return(0);
+ 
+ 	case DDIOCSCONF:
+ 		err = verify_area(VERIFY_READ, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&tmp_ddi, (void *)arg, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= DEPCA_MAX_DEV) return(-ENODEV);
+ 		dp = dp_ctrl[dev->if_unit];
+ 		if (dp == NULL) return(-ENODEV);
+ 
+ 		new = *dp;
+ 		new.ioaddr = tmp_ddi.io;
+ 		new.irq = tmp_ddi.irq;
+ 		return(depca_check_probe(dp, &new));
+ 
+ 	case SIOCSIFMTU:
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= DEPCA_MAX_DEV) return(-ENODEV);
+ 		if (dp_ctrl[dev->if_unit] == NULL) return(-ENODEV);
+ 		return(depca_check_mtu(dev, (int) arg));
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
*** original/net/drv/depca/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/main.c	Mon May  9 13:13:03 1994
***************
*** 0 ****
--- 1,143 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DEPCA device driver.
+  *
+  *		Main program of the DEPCA driver.
+  *
+  * Version:	@(#)main.c	1.0.0	02/28/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Peter Bauer, Systematika GmbH, <100136.3530@compuserve.com>
+  *		Jacco Beije, Redproc, <redproc@warns.et.tudelft.nl>
+  * 		David C. Davies, <davies@wanton.enet.dec.com>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "depca.h"
+ #include "global.h"
+ 
+ 
+ #define VERSION	"1.0.0"
+ 
+ 
+ struct depca	*dp_ctrl[DEPCA_MAX_DEV];	/* DEPCA control blocks	*/
+   
+ 
+ static int
+ depca_fopen(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= DEPCA_MAX_DEV) return(-ENODEV);
+   if (dp_ctrl[minor] == NULL) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ depca_fclose(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= DEPCA_MAX_DEV) return;
+   if (dp_ctrl[minor] == NULL) return;
+ }
+ 
+ 
+ static struct file_operations depca_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   depca_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   depca_fopen,	/* OPEN		*/
+   depca_fclose	/* CLOSE	*/
+ };
+ 
+ 
+ int
+ depca_init(struct ddi *ddi)
+ {
+   static int depca_card_nr = 0;
+   struct ifnet *dev;
+   struct depca *dp;
+ 
+   if (depca_card_nr == 0) {
+ 	printk("%s: version %s: ", ddi->name, VERSION);
+ 
+ 	/* Register VFS device. */
+ 	if (ddi->major != 0) {
+ 		if (register_chrdev(ddi->major, ddi->name, &depca_fops) < 0) {
+ 			printk("cannot register device %d!\n", ddi->major);
+ 			return(-EINVAL);
+ 		}
+ 		printk("device %d/%d: OK\n", ddi->major, ddi->minor);
+ 	}
+   }
+   
+   /* Create an interface descriptor. */
+   if ((dev = dev_create(NULL)) == NULL) {
+ 	printk("%s: No memory for interface descriptor!\n", ddi->name);
+ 	return(-ENOMEM);
+   }
+   dev->if_unit = depca_card_nr++;
+   sprintf(dev->if_name, "dp%d", dev->if_unit);
+   dev->if_flags = 0;
+   dev->ioctl = depca_ioctl;
+ 
+   /* Allocate a local control block. */
+   dp = (struct depca *) kmalloc(sizeof(struct depca), GFP_KERNEL);
+   if (dp == NULL) {
+ 	printk("%s: No memory for device control block!\n", ddi->name);
+ 	return(-ENOMEM);
+   }
+   memset(dp, 0, sizeof(struct depca));
+   dp->ioaddr = ddi->io;
+   dp->irq = ddi->irq;
+   dp->dma = ddi->dma;
+   dp->mem_start = ddi->memaddr;
+   dp->dev = dev;
+   dp_ctrl[dev->if_unit] = dp;
+ 
+   /* Is the device present? */
+   if (depca_probe(dp) != 0) {
+ 	kfree(dp);
+ 	dp_ctrl[dev->if_unit] = NULL;
+ 	dev_free(dev);
+ 	return(-ENODEV);
+   }
+ 
+   /* Yes, so give the user some info. */
+   printk("%s: %s (", dev->if_name, dp->name);
+   if (dp->ioaddr == DDI_IOANY) printk("[USERCFG]");
+    else printk("IO=0x%X IRQ%d MEM=0x%06lX, ADDR=%s", dp->ioaddr,
+ 	dp->irq, dp->mem_start, dev->mac_sprint(dev->mac_aget(dev)));
+   printk("): OK\n");
+ 
+   /* All done... */
+   return(0);
+ }
*** original/net/drv/depca/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/packet.c	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,1131 ----
+ /*  depca.c: A DIGITAL DEPCA  & EtherWORKS ethernet driver for linux.
+ 
+     Written 1994 by David C. Davies.
+ 
+                       Copyright 1994 David C. Davies
+ 		                   and 
+ 			 United States Government
+ 	 (as represented by the Director, National Security Agency).  
+ 
+ 
+     This software may be used and distributed according to the terms of
+     the GNU Public License, incorporated herein by reference.
+ 
+     This driver is written for the Digital Equipment Corporation series
+     of DEPCA and EtherWORKS ethernet cards:
+ 
+         DEPCA       (the original)
+     	DE100
+ 	DE200 Turbo
+ 	DE201 Turbo
+ 	DE202 Turbo (TP BNC)
+ 	DE210
+ 
+     The  driver has been tested on DE100, DE200 and DE202 cards  in  a
+     relatively busy network.
+ 
+     This driver  will  not work  for the DE203,  DE204  and DE205 series  of
+     cards, since they have a new custom ASIC in place of the AMD LANCE chip.
+ 
+     The author may be reached as davies@wanton.lkg.dec.com or
+     Digital Equipment Corporation, 146 Main Street, Maynard MA 01754.
+ */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/fcntl.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/ioport.h>
+ #include <linux/in.h>
+ #include <linux/ptrace.h>
+ #include <linux/errno.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/io.h>
+ #include <asm/bitops.h>
+ #include <asm/dma.h>
+ #include <asm/system.h>
+ #include "depca.h"
+ #include "global.h"
+ 
+ 
+ #if 0
+ static char *version = "depca.c:v0.35 3/8/94 davies@wanton.lkg.dec.com\n";
+ #endif
+ 
+ 
+ /* Miscellaneous defines... */
+ #define GET_NICSR(a, b) \
+ 	if (dp->depca_na) { \
+ 		(a) = inw((b)); \
+ 	} else { \
+ 		(a) = inb((b)); \
+ 	}
+ 
+ #define PUT_NICSR(a, b) \
+ 	if (dp->depca_na) { \
+ 		outw((a), (b)); \
+ 	} else { \
+ 		outb((a), (b)); \
+ 	}
+ 
+ #define STOP_DEPCA \
+     outw(CSR0, DEPCA_ADDR) ; \
+     outw(STOP, DEPCA_DATA)
+ 
+ 
+ static int depca_na = 0;		/* NICSR bus width		*/
+ static short mem_chkd = 0;		/* holds which base addrs have been */
+ static struct depca *depca_irq2dp_map[16] = { NULL };
+ 
+ 
+ static char
+ asc2hex(char value)
+ {
+   value -= '0';				/* normalise to 0..9 range */
+   if (value >= 0) {
+ 	if (value > 9) {		/* but may not be 10..15 */
+ 		value &= 0x1f;		/* make A..F & a..f be the same */
+ 		value -= 0x07;		/* normalise to 10..15 range */
+ 		if ((value < 0x0a) ||
+ 	    	(value > 0x0f)) {	/* if outside range then... */
+ 			value = -1;	/* ...signal error */
+ 		}
+ 	}
+   } else {				/* outside 0..9 range... */
+ 	value = -1;			/* ...signal error */
+   }
+   return(value);			/* return hex char or error */
+ }
+ 
+ 
+ /*
+  * Look for a particular board name in the on-board Remote Diagnostics
+  * and Boot (RDB) ROM. This will also give us a clue to the network RAM
+  * base address.
+  */
+ static char *
+ DepcaSignature(unsigned long mem_addr)
+ {
+   static char signatures[][DEPCA_NAME_LEN] = DEPCA_SIGNATURE;
+   static char thisName[DEPCA_NAME_LEN];
+   unsigned long i,j,k;
+   char tmpstr[17];
+ 
+   /* Copy the first 16 bytes of ROM to a temporary string. */
+   for (i = 0; i < 16; i++) {
+ 	tmpstr[i] = *(unsigned char *) (mem_addr + 0xc000 + i);
+   }
+   tmpstr[i] = '\0';;
+ 
+   strcpy(thisName, "");
+   for (i = 0; *signatures[i] != '\0' && *thisName == '\0'; i++) {
+ 	for (j = 0, k = 0; j < 16 && k < strlen(signatures[i]); j++) {
+ 		if (signatures[i][k] == tmpstr[j]) { /* track signature */
+ 			k++;
+ 		} else {	/* lost signature; begin search again */
+ 			k = 0;
+ 		}
+ 	}
+ 	if (k == strlen(signatures[i])) {
+ 		strcpy(thisName,signatures[i]);
+ 	}
+   }
+   return(thisName);			/* return the device name string */
+ }
+ 
+ 
+ /*
+  * Look for a special sequence in the Ethernet station address PROM
+  * that is common across all DEPCA products.  Note that the original
+  * DEPCA needs its ROM address counter to be initialized and enabled.
+  * Only enable if the first address octet is a 0x08 - this minimizes
+  * the chances of messing around with some other hardware, but it
+  * assumes that this DEPCA card initialized itself correctly.
+  * It also assumes that all past and future DEPCA/EtherWORKS cards
+  * will have ethernet addresses beginning with a 0x08. The choice of
+  * byte or word addressing is made here based on whether word read of
+  * the NICSR returns two identical lower and upper bytes: if so the
+  * register is 8 bits wide.
+  */
+ static int
+ DevicePresent(short ioaddr)
+ {
+   static short fp = 1, sigLength = 0;
+   static char devSig[] = PROBE_SEQUENCE;
+   char data;
+   unsigned char LSB,MSB;
+   int i, j, nicsr, status = 0;
+ 
+  /*
+   * Initialize the counter on a DEPCA card. Two reads to ensure DEPCA
+   * ethernet address counter is a) cleared and b) the correct data read.
+   */
+   data = inb(DEPCA_PROM);			/* clear counter */
+   data = inb(DEPCA_PROM);			/* read data */
+ 
+  /*
+   * Determine whether a byte or word access should be made on the NICSR.
+   * Since the I/O 'functions' are actually in-line code, the choice not
+   * to use pointers to functions vs. just set a conditional, is made for
+   * us. This code assumes that the NICSR has an asymmetric bit pattern
+   * already in it.
+   */
+   nicsr = inw(DEPCA_NICSR);
+   LSB = nicsr & 0xff;
+   MSB = (((unsigned) nicsr) >> 8) & 0xff;
+   if (MSB == LSB) {
+ 	depca_na = 0;		/* byte accesses */
+   } else {
+ 	depca_na = 1;		/* word accesses */
+   }
+ 
+   /* Enable counter. We cannot yet use the NICSR macros here... */
+   if (data == 0x08) {
+ 	nicsr |= AAC;
+ 	if (depca_na) {
+ 		outw(nicsr, DEPCA_NICSR);
+ 	} else {
+ 		outb(nicsr, DEPCA_NICSR);
+ 	}
+   }
+   
+   /*
+    * Convert the ASCII signature to a hex equivalent and
+    * pack in place.  We only do this once!...
+    */
+   if (fp) {
+ 	for (i = 0, j = 0; devSig[i] != '\0' && !status; i += 2, j++) {
+ 		if ((devSig[i] = asc2hex(devSig[i])) >= 0) {
+ 			devSig[i] <<= 4;
+ 			if ((devSig[i + 1] = asc2hex(devSig[i + 1])) >= 0) {
+ 				devSig[j] = devSig[i] + devSig[i + 1];
+ 			} else {
+ 				status= -1;
+ 			}
+ 		} else {
+ 			status= -1;
+ 		}
+ 	}
+ 	sigLength = j;
+ 	fp = 0;
+   }
+ 
+   /* 
+    * Search the Ethernet address ROM for the signature. Since the ROM
+    * address counter can start at an arbitrary point, the search must
+    * include the entire probe sequence length plus the length of the
+    * (signature - 1).  Stop the search IMMEDIATELY after the signature
+    * is found so that the PROM address counter is correctly positioned
+    * at the start of the ethernet address for later read out.
+    */
+   if (! status) {
+ 	for (i = 0, j = 0; j < sigLength &&
+ 			i < PROBE_LENGTH + sigLength - 1; i++) {
+ 		data = inb(DEPCA_PROM);
+ 		if (devSig[j] == data) {	/* track signature */
+ 			j++;
+ 		} else {	/* lost signature; begin search again */
+ 			j = 0;
+ 		}
+ 	}
+ 	if (j != sigLength) status = -1;	/* search failed */
+   }
+ 
+   return(status);
+ }
+ 
+ 
+ /* Initialize the LANCE Rx and Tx descriptor rings. */
+ static void
+ depca_init_ring(struct depca *dp)
+ {
+   unsigned char *ptr;
+   unsigned long i;
+ 
+   dp->pvt->init_block.mode = DTX | DRX;		/* disable RX and TX */
+   dp->pvt->cur_rx = dp->pvt->cur_tx = 0;
+   dp->pvt->dirty_rx = dp->pvt->dirty_tx = 0;
+ 
+   /* Initialize the base addresses and length of each buffer in the ring. */
+   for (i = 0; i < dp->pvt->ringSize; i++) {
+ 	dp->pvt->rx_ring[i].base = (dp->pvt->dma_buffs + i*PKT_BUF_SZ) | R_OWN;
+ 	dp->pvt->rx_ring[i].buf_length = -PKT_BUF_SZ;
+ 	dp->pvt->tx_ring[i].base =
+ 		(dp->pvt->dma_buffs + (i+dp->pvt->ringSize) * PKT_BUF_SZ) &
+ 						(unsigned long)(0x00ffffff);
+   }
+ 
+   /* Set up the initialization block. */
+   ptr = (dp->dev->mac_aget(dp->dev))->sa_data;
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	dp->pvt->init_block.phys_addr[i] = ptr[i];
+   }
+   for (i = 0; i < 4; i++) {
+       dp->pvt->init_block.filter[i] = 0x0000;
+   }
+   dp->pvt->init_block.rx_ring =
+ 	(unsigned long)dp->pvt->rx_ring | dp->pvt->rlen;
+   dp->pvt->init_block.tx_ring =
+ 	(unsigned long)dp->pvt->tx_ring | dp->pvt->rlen;
+ 
+   dp->pvt->init_block.mode = 0x0000;		/* enable the TX and RX */ 
+ }
+ 
+ 
+ static void
+ LoadCSRs(struct depca *dp)
+ {
+   int ioaddr = dp->ioaddr;
+ 
+   /* Initialization block address LSW. */
+   outw(CSR1, DEPCA_ADDR);
+   outw((unsigned short)(unsigned long)&dp->pvt->init_block, DEPCA_DATA);
+ 
+   /* Initialisation block address MSW. */
+   outw(CSR2, DEPCA_ADDR);
+   outw((unsigned short)((unsigned long)&dp->pvt->init_block >> 16), DEPCA_DATA);
+ 
+   /* ALE control. */
+   outw(CSR3, DEPCA_ADDR);
+   outw(ACON, DEPCA_DATA);
+   outw(CSR0, DEPCA_ADDR);	/* point back to CSR0 */
+ }
+ 
+ 
+ static int
+ InitRestartDepca(struct depca *dp)
+ {
+   int ioaddr = dp->ioaddr;
+   int i;
+ 
+   outw(CSR0, DEPCA_ADDR);		/* point back to CSR0 */
+   outw(INIT, DEPCA_DATA);		/* initialize DEPCA */
+ 
+   /* Wait for LANCE to complete initialization. */
+   for (i = 0; (i < 100) && !(inw(DEPCA_DATA) & IDON); i++)
+ 				; 
+ 
+   if (i == 100) {
+ 	printk("%s: DEPCA unopened after %d ticks, init block 0x%lX csr0 0x%4.4X\n",
+ 		dp->dev->if_name, i, (long) &dp->pvt->init_block,
+ 		inw(DEPCA_DATA));
+ 	return(-1);
+   }
+ 
+   /* Clear IDON by writing a "1", enable interrupts and start LANCE. */
+   outw(IDON | INEA | STRT, DEPCA_DATA);
+ 
+   DPRINTF(("%s: DEPCA open after %d ticks, init block 0x%lX csr0 0x%4.4X\n",
+ 	dp->dev->if_name, i, (long) &dp->pvt->init_block, inw(DEPCA_DATA)));
+   return(0);
+ }
+ 
+ 
+ /* Copy a buffer to the adapter transmit page memory. Start sending. */
+ static int
+ depca_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct depca *dp;
+   int ioaddr;
+ 
+   /*
+    * If some higher layer thinks we've missed a tx-done
+    * interrupt, we are passed NULL. Caution: dev_tint()
+    * handles the cli()/sti() itself.  
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   dp = dp_ctrl[dev->if_unit];
+   if (dp == NULL) {
+ 	printk("DEPCA: %s: unknown device in depca_xmit() !\n", dev->if_name);
+ 	return(1);
+   }
+   ioaddr = dp->ioaddr;
+ 
+   /* Transmitter timeout, serious problems. */
+   if (dev->if_tbusy) {
+ 	int tickssofar = (jiffies - dev->if_txtimer);
+ 
+ 	if (tickssofar < 10) return(1);
+ 
+ 	STOP_DEPCA;
+ 	printk("%s: transmit timed out, status %4.4X, resetting\n",
+ 					dev->if_name, inw(DEPCA_DATA));
+ 	
+ 	depca_init_ring(dp);
+ 	LoadCSRs(dp);
+ 	InitRestartDepca(dp);
+ 	dev->if_tbusy = 0;
+ 	dev->if_txtimer = jiffies;
+ 	return(0);
+   }
+ 
+   if (skb->len <= 0) return(0);
+ 
+ #ifdef DEPCA_DEBUG
+   if (depca_debug > 0) {
+ 	outw(CSR0, DEPCA_ADDR);
+ 	DPRINTF(("%s: start_xmit() called, csr0 %4.4X\n",
+ 					dev->if_name, inw(DEPCA_DATA)));
+   }
+ #endif
+ 
+   /*
+    * Block a timer-based transmit from overlapping.  This could
+    * better be done with atomic_swap(1, dev->if_tbusy), but
+    * set_bit() works as well.
+    */
+   if (set_bit(0, (void*)&dev->if_tbusy) != 0)
+ 	printk("%s: transmitter access conflict\n", dev->if_name);
+     else
+ 
+   /*
+    * The TX buffer, skb, has to be copied into the local network RAM
+    * for the LANCE to access it. The skb may be at > 16MB for large 
+    * (memory) systems.
+    */
+   {				/* Fill in a Tx ring entry */
+ 	unsigned char *buf;
+ 	int entry = dp->pvt->cur_tx++;
+ 	char *p = skb->raw;
+ 	long skbL = skb->len;
+ 	int len;
+ 
+ 	entry &= dp->pvt->rmask;	/* Ring around buffer number. */
+ 	buf = (unsigned char *)(dp->pvt->tx_ring[entry].base & 0x00ffffff);
+ 
+ 	/* Wait for a full ring to free up */
+ 	while (dp->pvt->tx_ring[entry].base < 0)
+ 			;
+ 
+ 	/* 
+ 	 * Caution: the write order is important here... don't set up the
+ 	 * ownership rights until all the other information is in place.
+ 	 */
+ 	len = ((skbL > ETH_FRAME_LEN) ? ETH_FRAME_LEN : skbL);
+ 							/* skb too long */
+ 	if (len < ETH_ZLEN) len = ETH_ZLEN;		/* len too short */
+ 	skbL -= len;
+ 	dp->pvt->tx_ring[entry].length = -len;
+ 
+ 	/* Clears various error flags */
+ 	dp->pvt->tx_ring[entry].misc = 0x0000;
+ 
+ 	/* Copy the data from the socket buffer to the LANCE memory. */
+ 	memcpy((unsigned char *)(buf), skb->raw, len);
+ 
+ 	/* Hand over buffer ownership to the LANCE. */
+ 	if (skbL <= 0) dp->pvt->tx_ring[entry].base |= (T_ENP);
+ 	dp->pvt->tx_ring[entry].base |= (T_OWN|T_STP);
+ 
+ 	/* Trigger an immediate send demand. */
+ 	outw(CSR0, DEPCA_ADDR);
+ 	outw(INEA | TDMD, DEPCA_DATA);
+ 
+ 	dev->if_txtimer = jiffies;
+ 
+ 	/* For oversized buffers: copy the rest. */
+ 	for (p += len; skbL > 0; p += len) {
+ 		/* Get new buffer pointer. */
+ 		entry = dp->pvt->cur_tx++;
+ 		entry &= dp->pvt->rmask;	/* Ring around buffer number. */
+ 		buf = (unsigned char *)
+ 			(dp->pvt->tx_ring[entry].base & 0x00ffffff);
+ 
+ 		/* Wait for a full ring to free up. */
+ 		while (dp->pvt->tx_ring[entry].base < 0)
+ 					;
+ 		dev->if_tbusy = 0;
+ 
+ 		/* Copy ethernet header to the new buffer. */
+ 		memcpy((unsigned char *)buf, skb->raw, ETH_HLEN);
+ 
+ 		/* Determine length of data buffer. */
+ 		len = ((skbL > ETH_DATA_LEN) ? ETH_DATA_LEN : skbL);
+ 							/* skbL too long */
+ 		if (len < ETH_ZLEN) len = ETH_ZLEN;	/* len too short */
+ 		skbL -= len;
+ 		dp->pvt->tx_ring[entry].length = -len;
+ 
+ 		/* Clears various error flags. */
+ 		dp->pvt->tx_ring[entry].misc = 0x0000;
+ 
+ 		/* Copy the data from the socket buffer to the LANCE memory. */
+ 		memcpy((unsigned char *)(buf + ETH_HLEN),
+ 		       (unsigned char *)p, len);
+ 
+ 		/* Hand over buffer ownership to the LANCE. */
+ 		if (skbL <= 0) dp->pvt->tx_ring[entry].base |= T_ENP;
+ 		dp->pvt->tx_ring[entry].base |= T_OWN;
+ 	}
+ 
+ #ifdef DEPCA_DEBUG
+ 	if (depca_debug > 0) {
+ 	unsigned char *pkt =
+ 		(unsigned char *)(dp->pvt->tx_ring[entry].base & 0x00ffffff);
+ 
+ 	printk("%s: tx ring[%d], %#lx, sk_buf %#lx len %d.\n",
+ 		dev->if_name, entry, (unsigned long) &dp->pvt->tx_ring[entry],
+ 		dp->pvt->tx_ring[entry].base, -dp->pvt->tx_ring[entry].length);
+ 	printk("%s: Tx %2.2x %2.2x %2.2x ... %2.2x  %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x  %2.2x %2.2x.\n",
+ 		dev->if_name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6],
+ 		pkt[7], pkt[8], pkt[11], pkt[12], pkt[13], pkt[14], pkt[15]);
+ 	}
+ #endif
+ 
+ 	/* Check if the TX ring is full or not - 'tbusy' cleared if not full. */
+ 	if (dp->pvt->tx_ring[(entry+1) & dp->pvt->rmask].base >= 0)
+ 							dev->if_tbusy = 0;
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /* A packet was sent by the DEPCA. */
+ static void
+ depca_txintr(struct depca *dp)
+ {
+   int dirty_tx = dp->pvt->dirty_tx & dp->pvt->rmask;
+ 
+   DPRINTF(("%s: cleaning tx ring, dirty %d clean %d\n",
+ 	dp->dev->if_name, dirty_tx, (dp->pvt->cur_tx & dp->pvt->rmask)));
+   
+   /* 
+    * While the dirty entry is not the current one AND 
+    * the LANCE doesn't own it... 
+    */
+   for (; dirty_tx != (dp->pvt->cur_tx & dp->pvt->rmask) &&
+ 	 dp->pvt->tx_ring[dirty_tx].base > 0;
+ 	 dirty_tx = ++dp->pvt->dirty_tx & dp->pvt->rmask) {
+ 
+ #ifdef DEPCA_DEBUG
+ 	unsigned long *tmdp = (unsigned long *)(&dp->pvt->tx_ring[dirty_tx]);
+ #endif
+ 	int status = dp->pvt->tx_ring[dirty_tx].base >> 16;
+ 
+ 	if (status < 0) {			/* Packet not yet sent! */
+ 		DPRINTF(("interrupt for packet not yet sent!\n"));
+ 		break;
+ 	}
+ 	if (status & T_ERR) {
+ 		/* There was an major error, log it. */
+ 		int err_status = dp->pvt->tx_ring[dirty_tx].misc;
+ 
+ 		dp->dev->if_stats->tx_errors++;
+ 		if (err_status & TMD3_RTRY)
+ 			dp->dev->if_stats->tx_aborted++;
+ 		if (err_status & TMD3_LCAR)
+ 			dp->dev->if_stats->tx_carrier++;
+ 		if (err_status & TMD3_LCOL)
+ 			dp->dev->if_stats->tx_window++;
+ 		if (err_status & TMD3_UFLO)
+ 			dp->dev->if_stats->tx_fifo++;
+ 		/* We should re-init() after the FIFO error. */
+ 	} else if (status & (T_MORE | T_ONE)) {
+ 		dp->dev->if_stats->tx_collision++;
+ 	} else {
+ 		dp->dev->if_stats->tx_packets++;
+ 	}
+ 
+ 	DPRINTF(("%s: TX done entry %d, %4.4lX %4.4lX %4.4lX %4.4lX\n",
+ 		dp->dev->if_name, dirty_tx,
+ 		tmdp[0], tmdp[1], tmdp[2], tmdp[3]));
+   }
+ 
+   mark_bh(NET_BH);
+ }
+ 
+ 
+ /* We have a good packet(s), get it/them out of the buffers. */
+ static void
+ depca_rxintr(struct depca *dp)
+ {
+   int entry = dp->pvt->cur_rx & dp->pvt->rmask;
+ 
+   /* If we own the next entry, it's a new packet. Send it up. */
+   for (; dp->pvt->rx_ring[entry].base >= 0;
+ 	 entry = (++dp->pvt->cur_rx) & dp->pvt->rmask) {
+ 	int status = dp->pvt->rx_ring[entry].base >> 16;
+ 	int chained;
+ 
+ 	/*
+ 	 * There is a tricky error noted by John Murphy, <murf@perftech.com>
+ 	 * to Russ Nelson: even with full-sized buffers, it's possible for a
+ 	 * jabber packet to use two buffers, with only the last one correctly
+ 	 * noting the error.
+ 	 */
+ 
+ 	/* Check for a chaining buffer. */
+ 	chained = 0;
+ 	if (status == R_STP) { 
+ 		chained = 1;
+ 
+ 		/* 
+ 		 * Wait for next buffer to complete to check for errors. This
+ 		 * is slow but infrequent and allows for correct hardware buffer
+ 		 * chaining (whilst defeating the chaining's purpose).
+ 		 */
+ 		while ((status =
+ 		  (dp->pvt->rx_ring[(entry+1)&dp->pvt->rmask].base >> 16)) < 0)
+ 					;
+ 
+ 		/* NB: 'status' now comes from the buffer following 'entry'. */
+ 	}
+ 	  
+ 	if (status & R_ERR) {			/* there was an error */
+ 		dp->dev->if_stats->rx_errors++;	/* update the error stats */
+ 		if (status & R_FRAM)
+ 			dp->dev->if_stats->rx_frame++;
+ 		if (status & R_OFLO)
+ 			dp->dev->if_stats->rx_overrun++;
+ 		if (status & R_CRC)
+ 			dp->dev->if_stats->rx_crc++;
+ 		if (status & R_BUFF)
+ 			dp->dev->if_stats->rx_fifo++;
+ 	} else {
+ 		/* Malloc up new buffer. */
+ 		short pkt_len = dp->pvt->rx_ring[entry].msg_length;
+ 		struct sk_buff *skb;
+ 
+ 		skb = skb_create(pkt_len);
+ 		if (skb == NULL) {
+ 			printk("%s: memory squeeze, deferring packet\n",
+ 							dp->dev->if_name);
+ 			dp->dev->if_stats->rx_dropped++;/* really, deferred */
+ 			break;
+ 		}
+ 		skb->len = pkt_len;
+ 		skb->dev = dp->dev;
+ 
+ 		memcpy(skb->raw,
+ 		       (char *) (dp->pvt->rx_ring[entry].base & 0x00ffffff),
+ 		       pkt_len);
+ 
+ 		/*
+ 		 * Notify the upper protocol layers that there is another 
+ 		 * packet to handle.
+ 		 */
+ 		if (dev_rint(dp->dev, skb, skb->len)) {
+ 			dp->dev->if_stats->rx_dropped++;
+ 			break;
+ 		}
+ 		dp->dev->if_stats->rx_packets++;
+ 	}
+ 
+ 	/* Turn over ownership of the current entry back to the LANCE. */
+ 	dp->pvt->rx_ring[entry].base |= R_OWN;
+ 	if (chained && (status & R_ERR)) {          /* next entry also bad */
+ 		entry = (++dp->pvt->cur_rx) & dp->pvt->rmask;
+ 		dp->pvt->rx_ring[entry].base |= R_OWN;
+ 	}
+   }
+ 
+   /* 
+    * We should check that at least two ring entries are free.
+    * If not, we should free one and mark stats->rx_dropped++. 
+    */
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver: Handle the network
+  * interface interrupts.
+  */
+ static void
+ depca_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct depca *dp;
+   int csr0, ioaddr, nicsr;
+ 
+   dp = depca_irq2dp_map[irq];
+   if (dp == NULL) {
+ 	printk("DEPCA: IRQ%d for unknown device!\n", irq);
+ 	return;
+   }
+   ioaddr = dp->ioaddr;
+ 
+   if (dp->interrupt)
+ 	printk("%s: re-entering the interrupt handler\n", dp->dev->if_name);
+   dp->interrupt = MASK_INTERRUPTS;
+ 
+   /* Mask the DEPCA board interrupts and turn on the LED. */
+   GET_NICSR(nicsr, DEPCA_NICSR);
+   nicsr |= (IM|LED);
+   PUT_NICSR(nicsr, DEPCA_NICSR);
+ 
+   outw(CSR0, DEPCA_ADDR);
+   csr0 = inw(DEPCA_DATA);
+ 
+   /* Acknowledge all of the current interrupt sources ASAP. */
+   outw(csr0 & ~(INEA|TDMD|STOP|STRT|INIT), DEPCA_DATA);
+ 
+   DPRINTF(("%s: interrupt: csr0=0x%2.2X new csr=0x%2.2X\n",
+ 			dp->dev->if_name, csr0, inw(DEPCA_DATA)));
+ 
+   if (csr0 & RINT)		/* RX interrupt (packet arrived) */
+ 	depca_rxintr(dp);
+ 
+   if (csr0 & TINT)		/* TX interrupt (packet sent) */
+         depca_txintr(dp);
+ 
+   /* Clear the interrupts we've handled. */
+   outw(CSR0, DEPCA_ADDR);
+   outw(BABL|CERR|MISS|MERR|RINT|TINT|IDON|INEA, DEPCA_DATA);
+ 
+   DPRINTF(("%s: exiting interrupt, csr%d=0x%4.4X\n",
+ 		dp->dev->if_name, inw(DEPCA_ADDR), inw(DEPCA_DATA)));
+ 
+   /* Unmask the DEPCA board interrupts and turn off the LED */
+   nicsr = (nicsr & ~IM & ~LED);
+   PUT_NICSR(nicsr, DEPCA_NICSR);
+ 
+   dp->interrupt = UNMASK_INTERRUPTS;
+ }
+ 
+ 
+ /*
+  * Calculate the hash code and update the logical address filter
+  * from a list of ethernet multicast addresses.
+  * Derived from a 'C' program in the AMD data book:
+  * "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", 
+  * Pub #17781, Rev. A, May 1993
+  */
+ static void
+ SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table)
+ {
+   char j, ctrl, bit, octet, hashcode;
+   short int i;
+   long int CRC, poly = (long int) CRC_POLYNOMIAL;
+ 
+   for (i = 0; i < num_addrs; i++) {	/* for each address in the list */
+ 	/* Is multicast address? */ 
+ 	if (((char) *(addrs + ETH_ALEN * i) & 0x01) == 1) {
+ 		CRC = (long int) 0xffffffff;	/* init CRC for each address */
+ 		/* For each address octet. */
+ 		for (octet = 0; octet < ETH_ALEN; octet++) {
+ 			/* Process each address bit. */
+ 			for(j = 0; j < 8; j++) {
+ 				bit = (((char)*
+ 				    (addrs + ETH_ALEN * i + octet)) >> j) & 0x01;
+ 
+ 				/* Shift the control bit. */
+ 				ctrl = ((CRC < 0) ? 1 : 0);
+ 
+ 				/* Shift the CRC. */
+ 				CRC <<= 1;
+ 
+ 				/* (bit) XOR (control bit) */
+ 				if (bit ^ ctrl) {
+ 					CRC ^= poly;/* (CRC) XOR (polynomial) */
+ 				}
+ 			}
+ 		}
+ 		hashcode = (CRC & 0x00000001);/* hashcode is 6 LSb of CRC ... */
+ 		for (j = 0; j < 5; j++) {	/* ... in reverse order. */
+ 			hashcode <<= 1;
+ 			CRC >>= 1;
+ 			hashcode |= (CRC & 0x00000001);
+ 		}                                      
+ 		octet = hashcode >> 3;	/* bit[3-5] -> octet in filter */
+ 					/* bit[0-2] -> bit in octet */
+ 		multicast_table[octet] |= (1 << (hashcode & 0x07));
+ 	}
+   }
+ }
+ 
+ 
+ /* Open/initialize the board. */
+ static int
+ depca_open(struct ifnet *dev)
+ {
+   struct depca *dp;
+   int nicsr, ioaddr;
+ 
+   DPRINTF(("DEPCA: open: %s\n", dev->if_name));
+ 
+   dp = dp_ctrl[dev->if_unit];
+   if (dp->ioaddr == DDI_IOAUTO || dp->ioaddr == DDI_IOANY) {
+ 	DPRINTF(("%s: device has not been configured yet!\n",
+ 					dp->dev->if_name));
+ 	return(-ENODEV);
+   }
+   ioaddr = dp->ioaddr;
+ 
+   if (request_irq(dp->irq, depca_interrupt)) {
+         printk("DEPCA: open: requested IRQ%d is busy\n", dp->irq);
+ 	return(-EAGAIN);
+   }
+   depca_irq2dp_map[dp->irq] = dp;
+ 
+   /* Stop the DEPCA & get the board status information. */
+   STOP_DEPCA;
+   GET_NICSR(nicsr, DEPCA_NICSR);
+ 
+   /* Re-initialize the DEPCA... */
+   depca_init_ring(dp);			/* initialize the descriptor rings */
+   LoadCSRs(dp);
+ 
+   /* Enable DEPCA board interrupts. */
+   nicsr = ((nicsr & ~IM & ~LED)|SHE|IEN);
+   PUT_NICSR(nicsr, DEPCA_NICSR);
+   outw(CSR0,DEPCA_ADDR);
+ 
+   dev->if_tbusy = 0;                         
+   dp->interrupt = 0;
+   dp->start = 1;
+ 
+   (void) InitRestartDepca(dp);
+ 
+   GET_NICSR(nicsr, DEPCA_NICSR);
+   DPRINTF(("DEPCA: CSR0: 0x%4.4X  nicsr: 0x%4.4X\n",
+ 		inw(DEPCA_DATA), nicsr));
+ 
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to depca_open(). */
+ static int
+ depca_close(struct ifnet *dev)
+ {
+   struct depca *dp;
+   int ioaddr;
+ 
+   DPRINTF(("DEPCA: close: %s\n", dev->if_name));
+ 
+   dp = dp_ctrl[dev->if_unit];
+   if (dp->ioaddr == DDI_IOAUTO || dp->ioaddr == DDI_IOANY) {
+ 	DPRINTF(("%s: device has not been configured yet!\n",
+ 					dp->dev->if_name));
+ 	return(-ENODEV);
+   }
+   ioaddr = dp->ioaddr;
+ 
+   dev->if_tbusy = 1;
+   dp->start = 0;
+   dp->interrupt = 0;
+ 
+   outw(CSR0, DEPCA_ADDR);
+ 
+   DPRINTF(("%s: shutting down ethercard, status was 0x%02.2X\n",
+ 					dev->if_name, inw(DEPCA_DATA)));
+ 
+   /* We stop the DEPCA here -- it occasionally polls memory if we don't. */
+   outw(STOP, DEPCA_DATA);
+ 
+   free_irq(dp->irq);
+ 
+   depca_irq2dp_map[dp->irq] = NULL;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Set or clear the multicast filter for this adaptor.
+  * num_addrs == -1	Promiscuous mode, receive all packets
+  * num_addrs == 0	Normal mode, clear multicast list
+  * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+  * 			best-effort filtering.
+  */
+ static int
+ depca_set_multi(struct ifnet *dev, int num_addrs, void *addrs)
+ {
+   struct depca *dp;
+   short ioaddr;
+ 
+   dp = dp_ctrl[dev->if_unit];
+   if (dp->ioaddr == DDI_IOAUTO || dp->ioaddr == DDI_IOANY) {
+ 	DPRINTF(("%s: device has not been configured yet!\n",
+ 					dp->dev->if_name));
+ 	return(-ENODEV);
+   }
+   ioaddr = dp->ioaddr;
+   
+   /* We take the simple way out and always enable promiscuous mode. */
+   STOP_DEPCA;	/* temporarily stop the depca */
+ 
+   dp->pvt->init_block.mode = PROM;       /* Set promiscuous mode */
+   if (num_addrs >= 0) {
+ 	short multicast_table[4];
+ 	int i;
+ 
+ 	SetMulticastFilter(num_addrs, (char *)addrs, (char *)multicast_table);
+ 
+ 	/* We don't use the multicast table, but rely on upper-layer filtering. */
+ 	memset(multicast_table,
+ 		(num_addrs == 0) ? 0 : 0xFF, sizeof(multicast_table));
+ 
+ 	for (i = 0; i < 4; i++) {
+ 		dp->pvt->init_block.filter[i] = multicast_table[i];
+ 	}
+ 	dp->pvt->init_block.mode &= ~PROM; /* unset promiscuous mode */
+   } else {
+ 	dp->pvt->init_block.mode |= PROM;    /* set promiscuous mode */
+   }
+ 
+   outw(CSR0, DEPCA_ADDR);
+   outw(IDON|INEA|STRT, DEPCA_DATA); /* resume normal operation */
+   return(0);
+ }
+ 
+ 
+ static int
+ depca_probe1(short ioaddr, struct depca *dp)
+ {
+   struct sockaddr sa;
+   unsigned long mem_start, mem_base[] = DEPCA_RAM_BASES;
+   char *name = (char *)NULL;
+   int i, j, de100 = 0;
+   unsigned int nicsr, offset, netRAM;
+ 
+   DPRINTF(("DEPCA: probing at 0x%3X\n", dp->ioaddr));
+ 
+   /*
+    * Stop the DEPCA. Enable the DBR ROM and the ethernet ROM address counter
+    * (for the really old DEPCAs). Disable interrupts and remote boot.
+    */
+   STOP_DEPCA;
+ 
+   GET_NICSR(nicsr, DEPCA_NICSR);
+   nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | AAC | IM);
+   PUT_NICSR(nicsr, DEPCA_NICSR);
+ 
+   /* If the DEPCA won't stop, it's either not there or it hates us. */
+   if (inw(DEPCA_DATA) != STOP) return(-ENODEV);
+ 
+   /*
+    * Now find out what kind of DEPCA we have. The DE100 uses a different
+    * addressing scheme for some registers compared to the DE2xx series.
+    * Note that a base address location is marked as checked if no DEPCA
+    * is there or one is found (when the search is immediately terminated).
+    * This shortens the search time a little for multiple DEPCAs.
+    */
+   for (j = 0, i = 0; mem_base[i] && (j == 0);) {
+ 	/* Has the memory been checked? */
+ 	if (((mem_chkd >> i) & 0x01) == 0) {
+ 		/* Check for a DEPCA here. */
+ 		name = DepcaSignature(mem_base[i]);
+ 		mem_chkd |= (0x01 << i);	/* mark location checked */
+ 		if (*name != '\0') {		/* one found? */
+ 			j = 1;			/* set exit flag */
+ 		} else {
+ 			i++;			/* increment search index */
+ 		}
+ 	}
+   }
+ 
+   /* If no valid DEPCA signature is found, forget about it. */
+   if (*name == '\0') return(-ENODEV);
+ 
+   /* Found a DEPCA device. */
+   mem_start = mem_base[i];
+   dp->mem_start = mem_start;
+   dp->ioaddr = ioaddr;
+ 
+   DPRINTF(("%s: %s at 0x%04X, ", dp->dev->if_name, name, ioaddr));
+ 
+   /*
+    * There is a 32 byte station address PROM at DEPCA_PROM address.
+    * The first six bytes are the station address. They can be read
+    * directly since the signature search set up the ROM address 
+    * counter correctly just before this function.
+    *
+    * For the DE100 we have to be careful about which port is used
+    * to read the ROM info.
+    */
+   if (strstr(name, "DE100")) de100 = 1;
+ 
+   /* Fetch and store the station address. */
+   for (i = 0; i < ETH_ALEN; i++) {
+ 	sa.sa_data[i] = inb(DEPCA_PROM + de100);	/* 0 or 1 */
+ 	DPRINTF((" %02X", sa.sa_data[i]));
+   }
+ 
+   /* Leave ROM counter in known state. */
+   for (; i < 32; i++) {
+ 	j = inb(DEPCA_PROM);
+   }
+ 
+   /* We have a DEPCA, so clain the I/O space. */
+   snarf_region(ioaddr, DEPCA_TOTAL_SIZE);
+ 
+   /* Set up the maximum amount of network RAM(kB). */
+   if (strstr(name, "DEPCA") == (char *)NULL) {
+ 	netRAM = 64;
+   } else {
+ 	netRAM = 48;
+   }
+ 
+   /* Determine the base address for the DEPCA RAM from the NI-CSR. */
+   if (nicsr & BUF) {
+ 	offset = 0x8000;		/* 32kbyte RAM offset*/
+ 	nicsr &= ~BS;			/* DEPCA RAM in top 32k */
+ 	DPRINTF((",with %dkB RAM", netRAM-(offset >> 10)));
+   } else {
+ 	offset = 0x0000;		/* 64k/48k bytes RAM */
+ 	DPRINTF((",with %dkB RAM", netRAM));
+   }
+   mem_start += offset;
+   DPRINTF((" at 0x%05lX", mem_start));
+ 
+   /* Enable the shadow RAM. */
+   nicsr |= SHE;
+   PUT_NICSR(nicsr, DEPCA_NICSR);
+  
+   /* Attach the ETH service. */
+   eth_setup(dp->dev);
+   (void) dp->dev->mac_aset(dp->dev, &sa);
+ 
+   /*
+    * Calculate the ring size based on the available RAM found above.
+    * Allocate an equal number of buffers, each of size PKT_BUF_SZ (1544
+    * bytes) to the Tx and Rx, allowing one descriptor entry (8 bytes)
+    * for each buffer.  Make sure that this ring size is <= RING_SIZE.
+    * The ring size must be a power of 2.
+    */
+   j = (((netRAM << 10) - offset) / (PKT_BUF_SZ + 8)) >> 1;
+   for (i = 0; j > 1; i++)
+ 	j >>= 1;
+ 
+   /*
+    * Hold the ring size information here before the depca private
+    * structure is allocated. Need this for the memory space calculations.
+    */
+   j = 1 << i;
+ 
+   /*
+    * Set up memory information in the device structure.
+    * Align the descriptor rings on an 8 byte (quadword) boundary.
+    *
+    *	depca_private area
+    *	rx ring descriptors
+    *	tx ring descriptors
+    *	rx buffers
+    *	tx buffers
+    */
+   dp->pvt = (struct depca_pvt *) ((mem_start + 0x07) & ~0x07);      
+   memset(dp->pvt, 0, sizeof(struct depca_pvt));
+ 
+   /* Tx & Rx descriptors (aligned to a quadword boundary) */
+   mem_start = ((((unsigned long)dp->pvt + sizeof(struct depca_pvt)) +
+ 		(unsigned long)0x07) & (unsigned long)~0x07);
+   dp->pvt->rx_ring = (struct depca_rx_head *) mem_start;
+ 
+   mem_start += (sizeof(struct depca_rx_head) * j);
+   dp->pvt->tx_ring = (struct depca_tx_head *) mem_start;
+ 
+   mem_start += (sizeof(struct depca_tx_head) * j);
+   dp->pvt->dma_buffs = mem_start & 0x00ffffff;
+ 
+   /* Point mem_start to the start of the Tx buffers. */
+   mem_start += (PKT_BUF_SZ * j);
+ 
+   /* Initialize the data structures. */
+   memset(dp->pvt->rx_ring, 0, sizeof(struct depca_rx_head) * j);
+   memset(dp->pvt->tx_ring, 0, sizeof(struct depca_tx_head) * j);
+ 
+   /* This should never happen. */
+   if ((int)(dp->pvt->rx_ring) & 0x07) {
+ 	printk("**ERROR** DEPCA Rx/Tx descriptor rings alignment error !\n");
+ 	return(-ENODEV);
+   }
+ 
+   /* Finish initialising the ring information. */
+   dp->pvt->ringSize = j;
+   if (dp->pvt->ringSize > RING_SIZE) dp->pvt->ringSize = RING_SIZE;
+   dp->pvt->rmask = dp->pvt->ringSize - 1;
+ 
+   /* Calculate the real RLEN size for the descriptors. It is log2(ringSize). */
+   for (i = 0, j = dp->pvt->ringSize; j > 1; i++)
+ 			j >>= 1;
+   dp->pvt->rlen = (unsigned long) (i << 29);
+ 
+   /* Load the initialization block. */
+   depca_init_ring(dp);
+ 
+   /* Initialize the control and status registers. */
+   LoadCSRs(dp);
+ 
+ #if 0
+   /* Store the NICSR width for this DEPCA. */
+   dp->depca_na = depca_na;
+ #endif
+ 
+   /* Enable DEPCA board interrupts for autoprobing. */
+   nicsr = ((nicsr & ~IM)|IEN);
+   PUT_NICSR(nicsr, DEPCA_NICSR);
+ 
+   /*
+    * To auto-IRQ we enable the initialization-done and DMA err,
+    * interrupts. For now we will always get a DMA error.
+    */
+   if (dp->irq < 2) {
+ 	autoirq_setup(0);
+ 
+ 	/* Trigger an initialization just for the interrupt. */
+ 	outw(INEA | INIT, DEPCA_DATA);
+ 	  
+ 	dp->irq = autoirq_report(1);
+ 	if (dp->irq) {
+ 		DPRINTF((" and probed IRQ%d\n", dp->irq));
+ 	} else {
+ 		DPRINTF((". Failed to detect IRQ line\n"));
+ 		return(-EAGAIN);
+ 	}
+   } else {
+ 	DPRINTF((". Assigned IRQ%d\n", dp->irq));
+   }
+ 
+   dp->name = name;
+   dp->dev->open = depca_open;
+   dp->dev->close = depca_close;
+   dp->dev->xmit = depca_xmit;
+   dp->dev->set_multi = depca_set_multi;
+ 
+   depca_irq2dp_map[dp->irq] = NULL;	/* done in depca_open() ! */
+ 
+   /* Yoohoo, all went well.  We're in business! */
+   return(0);
+ }
+ 
+ 
+ /* Check for a network adaptor of this type, and return '0' iff one exists. */
+ int
+ depca_probe(struct depca *dp)
+ {
+   int *port, ports[] = DEPCA_IO_PORTS;
+ 
+   /* Set up the interface name. */
+   dp->name = "DEPCA";
+ 
+   if (dp->ioaddr != DDI_IOAUTO) {
+ 	if (dp->ioaddr == DDI_IOANY) return(0);
+ 	if (DevicePresent(dp->ioaddr) == 0)	/* is DEPCA really here? */
+ 		return(depca_probe1(dp->ioaddr, dp));
+ 	return(-ENODEV);
+   }
+ 
+   for (port = &ports[0]; *port; port++) {
+ 	int ioaddr = *port;
+ 
+ 	if (check_region(ioaddr, DEPCA_TOTAL_SIZE)) continue;
+ 
+ 	/* Check for PROM signature. */
+ 	if (DevicePresent(dp->ioaddr) == 0) continue;	/* is DEPCA really here? */
+ 
+ 	if (depca_probe1(ioaddr, dp) == 0) return(0);
+   }
+ 
+   return(-ENODEV);
+ }
*** original/net/drv/depca/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/depca/utils.c	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,80 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DEPCA device driver.
+  *
+  *		Various functions.
+  *
+  * Version:	@(#)utils.c	1.0.0	02/28/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Peter Bauer, Systematika GmbH, <100136.3530@compuserve.com>
+  * 		David C. Davies, <davies@wanton.enet.dec.com>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <stdarg.h>
+ #include "depca.h"
+ #include "global.h"
+ 
+ 
+ int depca_debug = 0;				/* DEPCA debug flag	*/
+ 
+ 
+ #ifdef DEPCA_DEBUG
+ void
+ depca_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (depca_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif
+ 
+ 
+ int
+ depca_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		depca_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		depca_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			depca_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/drv/dlink/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/Makefile	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,36 ----
+ #
+ # Makefile for the Linux D-Link (DE600) device driver.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ OBJS	= main.o utils.o ioctl.o packet.o
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ dlink.o:	$(OBJS)
+ 		ld -r -o dlink.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/dlink/README	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/README	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,177 ----
+ This is version 0.32
+ 
+           CONTENTS:
+ 
+             1. Introduction.
+             2. License.
+             3. Files in this release.
+             4. Installation.
+             5. Problems and tuning.
+             6. Acknowledgments.
+ 
+ 
+           1. INTRODUCTION.
+ 
+           This is an Ethernet driver for the D-Link DE-600 Ethernet pocket
+           adapter for the parallel port, used with Linux on a laptop.
+           Some improvements over the 0.2X releases include:
+             o  driver code trying to send packets end to end,
+             o  a more solid interrupt handler,
+             o  a fix that modifies the tcp protocol so that
+                the DE-600 won't choke as easily on receives.
+ 
+           This is a beta release, i.e. it ought to work! (:-)
+ 
+           I have used this driver for NFS, ftp, telnet and X-clients on
+           remote machines. Transmissions with ftp seems to work as
+           good as can be expected (i.e. > 80k bytes/sec) from a
+           parallel port...:-)
+           The speed limit is now in the upper protocols,
+           at least for a 386SX-25 :-)
+ 
+           All comments/fixes to Bjorn Ekwall (bj0rn@blox.se).
+ 
+           (No, I have _not_ included any support for the DE-620 yet
+           since I have _no_ official documentation on how to program
+           that beast. There are some code modifications in place
+           already in case I get some _real_ info..., c'mon D-Link!)
+ 
+ 
+           2. LICENSE.
+ 
+           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.
+ 
+ 
+           3. FILES IN THIS RELEASE.
+ 
+           README.DLINK  This file.
+           d_link.c      The Source (,may it be with You :-).
+ 
+ 
+           4. INSTALLATION.
+ 
+            o Get the latest net binaries (those referring to /conf/net).
+ 
+            o Read the NET-2 and Ethernet HOWTO's and modify your setup.
+ 
+            o To include networking and the DE600 in your kernel, do:
+                # cd /linux
+                # make config       (answer yes on net and DE600)
+                # make clean
+                # make depend
+                # make zImage (or whatever magic you usually do)
+ 
+            o I use lilo to boot multiple kernels, so that I at least
+              can have one working kernel :-). If you do too, append
+              these lines to /etc/lilo/config:
+ 
+                   image = /usr/src/linux/zImage
+                        label = newlinux
+                        root = /dev/hda2 (or whatever YOU have...)
+ 
+                # /etc/lilo/install
+ 
+            o Do "sync" and reboot the new kernel with a D-Link pocket
+              adapter connected.
+ 
+           Now, watch for any fireworks (try to ignore (or live with)
+           the smoke... :-) or:
+ 
+                do
+                     read NET-FAQ and all info in /conf/net
+                     if fix in code needed...
+ 			 vi /linux/net/inet/d_link.c
+ 			 cd /linux
+                          make zImage
+ 			 /etc/lilo/install
+                          sync
+                          reboot
+                     endif
+                     try it...
+                until satisfied
+ 
+ 
+           5. "PROBLEMS" AND TUNING,
+ 
+            o Some machines have trouble handling the parallel port and
+              the adapter at high speed. If you experience problems:
+ 
+              - The adapter is not recognized at boot, i.e. an Ethernet
+                adress of 00:80:c8:... is not shown, try to add another
+                  "; SLOW_DOWN_IO"
+                at D_LINK_SLOW_DOWN near line 43. As a last resort, uncomment:
+                  "#define REALLY_SLOW_IO"
+                near line 48 (see <asm/io.h> for hints).
+ 
+              - You experience "timeout" messages: first try to add
+                another "; SLOW_DOWN_IO" at D_LINK_SLOW_DOWN near line 22,
+                _then_ try to increase the value (original value: 5)
+                at "if (tickssofar < 5)" near line 424.
+ 
+              - The adapter _is_ recognized at boot but you get
+                messages about "Network Unreachable": The problem
+                is probably _not_ with the d_link driver.
+                Check your net configuration instead (ifconfig and route)
+                in "rc.inet1".
+              
+            o There might be some temporary "slowdowns" when communicating
+              with other systems when receiving through the TCP layer.
+              A "fix" for this is made lastly in the source, in the function
+              "d_link_rspace()" that is used to modify TCP if there is
+              a DE-600 in use (see comments around lines 320 and 730).
+ 
+              The aim of this fix is to reduce the possibility of more
+              than two packets arriving adressed to the adapter within
+              the timespan it takes to copy one packet from the adapter.
+ 
+              Transfers with ftp with a packetsize of >= 1k will be taken
+              care of with this "fix", but telnet with many small packets
+              might run into problems sometimes. The "slowdown" will usually
+              take care of itself, especially if there are some larger packets
+              arriving now and then...
+ 
+              There is some room for "tuning" by changing (decreasing) the
+              values for "D_LINK_MAX_WINDOW" and "D_LINK_MIN_WINDOW"
+              defined near the end of the file (around line 726).
+              UDP (i.e. NFS) does not suffer from the same problem.
+ 
+            o The code inside d_link_interrupt() is somewhat of a
+              (educated) guesswork since my only source of information
+              is the asm-source (:-), though I have tried to improve on it.
+ 
+            o There is some rudimentary support for debugging, see
+ 	     the source. Use "-DD_LINK_DEBUG=3" when compiling.
+ 
+ 
+           6. ACKNOWLEDGMENTS.
+ 
+           This driver wouldn't have been done without the base
+           (and support) from Ross Biro (bir7@leland.stanford.edu).
+           The driver also relies upon GPL-ed source from D-Link Inc.
+           and from Russel Nelson at Crynwr Software (nelson@crynwr.com).
+           Additional input also from Donald Becker (becker@super.org).
+           Alpha release primary victim^H^H^H^H^H^Htester:
+           Erik Proper (erikp@cs.kun.nl).
+           Good input also from several users, most notably Mark Burton
+           <markb@ordern.demon.co.uk>.
+           Lastly, Fred van Kempen deserves all thanks for keeping up
+           the good work which will give us all a _great_ net package!
+ 
+ 
+           Happy hacking!
+ 
+           Bjorn Ekwall == bj0rn@blox.se
*** original/net/drv/dlink/RELEASE	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/RELEASE	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,7 ----
+ 
+ 	This is version 0.6.0 of Bjorn Ekwall's driver for
+ 	the D-Link DE600 pocket ethernet adapter for Linux.
+ 
+ 	It was adapted from his 0.32 driver.
+ 
+ Fred  11/29/93
*** original/net/drv/dlink/dlink.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/dlink.h	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,43 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the D-LINK device driver.
+  *
+  *		Define the D_LINK device driver interface and constants.
+  *
+  * Version:	@(#)dlink.h	0.6.1	12/03/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #ifndef _DLINK_H
+ #define _DLINK_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	DLINK_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  DLINK_DEBUG
+ #   define DLINK_DEBUG
+ #endif
+ 
+ #ifdef	DLINK_DEBUG
+ #   define DPRINTF(x)	dlink_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ #define	DLINK_MAX_DEV	3
+ 
+ 
+ /* D-Link feature selection. */
+ #define	HAVE_SLOW_IO	1		/* set to 1 for SLOOOWW I/O	*/
+ 
+ 
+ extern int		dlink_init(struct ddi *ddi);
+ 
+ 
+ #endif	/* _DLINK_H */
*** original/net/drv/dlink/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/global.h	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,171 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the D-LINK device driver.
+  *
+  *		Define the D-LINK device driver internal functions.
+  *
+  * Version:	@(#)global.h	0.6.0	11/29/93
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Bjorn Ekwall, <bj0rn@blox.se>
+  */
+ #ifndef _DLINK_GLOBAL_H
+ #define _DLINK_GLOBAL_H
+ 
+ 
+ /*
+  * OK, so I'm cheating, but there are an awful lot of
+  * reads and writes in order to get anything in and out
+  * of the DE-600 with 4 bits at a time in the parallel port,
+  * so every saved instruction really helps :-)
+  */
+ 
+ /* D-Link Ethernet pocket adapter ports. */
+ #define DATA_PORT	(dl->ioaddr + 0)
+ #define STATUS_PORT	(dl->ioaddr + 1)
+ #define COMMAND_PORT	(dl->ioaddr + 2)
+ 
+ /* D-Link COMMAND_PORT commands. */
+ #define SELECT_NIC	0x04 /* select Network Interface Card */
+ #define SELECT_PRN	0x1c /* select Printer */
+ #define NML_PRN		0xec /* normal Printer situation */
+ #define IRQEN		0x10 /* enable IRQ line */
+ 
+ /* D-Link STATUS_PORT. */
+ #define RX_BUSY		0x80
+ #define RX_GOOD		0x40
+ #define TX_FAILED16	0x10
+ #define TX_BUSY		0x08
+ 
+ /*
+  * D-Link DATA_PORT commands
+  * command in low 4 bits
+  * data in high 4 bits
+  * select current data nibble with HI_NIBBLE bit
+  */
+ #define WRITE_DATA	0x00 /* write memory */
+ #define READ_DATA	0x01 /* read memory */
+ #define STATUS		0x02 /* read  status register */
+ #define COMMAND		0x03 /* write command register (see COMMAND below) */
+ #define NULL_COMMAND	0x04 /* null command */
+ #define RX_LEN		0x05 /* read  received packet length */
+ #define TX_ADDR		0x06 /* set adapter transmit memory address */
+ #define RW_ADDR		0x07 /* set adapter read/write memory address */
+ #define HI_NIBBLE	0x08 /* read/write the high nibble of data,
+ 				or-ed with rest of command */
+ /*
+  * command register, (I don't know all about these bits...)
+  * accessed through DATA_PORT with low bits = COMMAND
+  */
+ #define RX_ALL		0x01 /* bit 0,1 = 01 */
+ #define RX_BP		0x02 /* bit 0,1 = 10 */
+ #define RX_MBP		0x03 /* bit 0,1 = 11 */
+ 
+ #define TX_ENABLE	0x04 /* bit 2 */
+ #define RX_ENABLE	0x08 /* bit 3 */
+ 
+ #define RESET		0x80 /* set bit 7 high */
+ #define STOP_RESET	0x00 /* set bit 7 low */
+ 
+ /*
+  * data to command register
+  * (high 4 bits in write to DATA_PORT)
+  */
+ #define RX_PAGE2_SELECT	0x10 /* bit 4, only 2 pages to select */
+ #define RX_BASE_PAGE	0x20 /* bit 5, always set when specifying RX_ADDR */
+ #define FLIP_IRQ	0x40 /* bit 6 */
+ 
+ /* Convenience definition, transmitter page 2 */
+ #define TX_PAGE2_SELECT	0x02
+ 
+ /*
+  * D-Link adapter internal memory:
+  *
+  * 0-2K 1:st transmit page (send from pointer up to 2K)
+  * 2-4K	2:nd transmit page (send from pointer up to 4K)
+  *
+  * 4-6K 1:st receive page (data from 4K upwards)
+  * 6-8K 2:nd receive page (data from 6K upwards)
+  *
+  * 8K+	Adapter ROM (contains magic code and last 3 bytes of Ethernet address)
+  */
+ #define MEM_2K		0x0800 /* 2048 */
+ #define MEM_4K		0x1000 /* 4096 */
+ #define MEM_6K		0x1800 /* 6144 */
+ #define NODE_ADDRESS	0x2000 /* 8192 */
+ 
+ /* Convenience macros/functions for D-Link adapter. */
+ #define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); D_LINK_SLOW_DOWN
+ #define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); D_LINK_SLOW_DOWN
+ 
+ /* Thanks for hints from Mark Burton <markb@ordern.demon.co.uk> */
+ #define d_link_put_byte(data) ( \
+ 	outb_p(((data) << 4)   | WRITE_DATA            , DATA_PORT), \
+ 	outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT))
+ 
+ /*
+  * The first two outb_p()'s below could perhaps be deleted if there
+  * would be more delay in the last two. Not certain about it yet...
+  */
+ #define d_link_put_command(cmd) ( \
+ 	outb_p(( dl->rx_page        << 4)   | COMMAND            , DATA_PORT), \
+ 	outb_p(( dl->rx_page        & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \
+ 	outb_p(((dl->rx_page | cmd) << 4)   | COMMAND            , DATA_PORT), \
+ 	outb_p(((dl->rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT))
+ 
+ #define d_link_setup_address(addr,type) ( \
+ 	outb_p((((addr) << 4) & 0xf0) | type            , DATA_PORT), \
+ 	outb_p(( (addr)       & 0xf0) | type | HI_NIBBLE, DATA_PORT), \
+ 	outb_p((((addr) >> 4) & 0xf0) | type            , DATA_PORT), \
+ 	outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT))
+ 
+ #define rx_page_adr() ((dl->rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K))
+ 
+ /* Flip bit, only 2 pages */
+ #define next_rx_page() (dl->rx_page ^= RX_PAGE2_SELECT)
+ 
+ #define tx_page_adr(a) (((a) + 1) * MEM_2K)
+ 
+ 
+ #define TX_PAGES	2
+ 
+ 
+ struct dlink {
+   int			ioaddr;		/* I/O address to use		*/
+   int			irq;		/* IRQ channel to use		*/
+ 
+   unsigned char		interrupt;	/* we are handling an intr	*/
+   unsigned char		start;		/* we are up && running		*/
+ 
+   volatile int		tx_fifo[TX_PAGES];
+   volatile int		tx_fifo_in;
+   volatile int		tx_fifo_out;
+   volatile int		free_tx_pages;
+   volatile int		rx_page;
+ 
+   /* Various fields. */
+   struct ifnet		*dev;		/* easy for intr handling	*/
+ };
+ 
+ 
+ struct devmap {
+   int	ioaddr;
+   int	irq;
+   int	inuse;
+ };
+ 
+ 
+ extern struct dlink	*dl_ctrl[];
+ extern int		dlink_units;
+ 
+ extern int		dlink_probe(struct dlink *dl);
+ extern int		dlink_check_port(struct dlink *dl);
+ extern void		dlink_adapter_init(struct dlink *dl, int startp);
+ extern int		dlink_xmit(struct ifnet *dev, struct sk_buff *skb);
+ extern int		dlink_ioctl(struct inode *inode, struct file *file,
+ 				    unsigned int cmd, unsigned long arg);
+ extern int		dlink_dbg_ioctl(unsigned long arg, int level);
+ extern void		dlink_dprintf(char *fmt, ...);
+ 
+ 
+ #endif	/* _DLINK_GLOBAL_H */
*** original/net/drv/dlink/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/ioctl.c	Mon May  9 13:13:04 1994
***************
*** 0 ****
--- 1,123 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DLINK device driver.
+  *
+  *		DLINK I/O control request handler.
+  *
+  * Version:	@(#)ioctl.c	0.6.0	11/29/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "dlink.h"
+ #include "global.h"
+ 
+ 
+ /* Check if the new MTU setting is OK. */
+ static int
+ dlink_check_mtu(struct ifnet *dev, int new_mtu)
+ {
+   if ((new_mtu < ETH_ZLEN) || (new_mtu >= ETH_FRAME_LEN)) return(-EINVAL);
+   dev->if_mtu = new_mtu;
+ 
+   return(0);
+ }
+ 
+ 
+ static int
+ dlink_check_probe(struct dlink *dl, struct dlink *newdl)
+ {
+   struct dlink dlink;
+   int i;
+ 
+   dlink = *dl;
+   *dl = *newdl;
+ 
+   if ((i = dlink_probe(dl)) == 0) return(0);
+   *dl = dlink;
+   return(i);
+ }
+ 
+ 
+ /* Perform I/O control on an active DLINK channel. */
+ int
+ dlink_ioctl(struct inode *inode, struct file *file,
+ 	   unsigned int cmd, unsigned long arg)
+ {
+   struct dlink new, *dl;
+   struct ddi tmp_ddi;
+   struct ifnet *dev;
+   int err;
+   
+ 
+   DPRINTF(("DLINK: ioctl(0x%X, 0x%X, 0x%X)\n", inode, cmd, arg));
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(dlink_dbg_ioctl(arg, 1));
+ 
+ 	case DDIOCGCONF:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memset(&tmp_ddi, 0, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= dlink_units) return(-ENODEV);
+ 		dl = dl_ctrl[dev->if_unit];
+ 		if (dl == NULL) return(-ENODEV);
+ 
+ 		tmp_ddi.io = dl->ioaddr;
+ 		tmp_ddi.irq = dl->irq;
+ 		memcpy_tofs((void *)arg, &tmp_ddi, sizeof(struct ddi));
+ 
+ 		return(0);
+ 
+ 	case DDIOCSCONF:
+ 		err = verify_area(VERIFY_READ, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&tmp_ddi, (void *)arg, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= dlink_units) return(-ENODEV);
+ 		dl = dl_ctrl[dev->if_unit];
+ 		if (dl == NULL) return(-ENODEV);
+ 
+ 		new = *dl;
+ 		new.ioaddr = tmp_ddi.io;
+ 		new.irq = tmp_ddi.irq;
+ 		return(dlink_check_probe(dl, &new));
+ 
+ 	case SIOCSIFMTU:
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= dlink_units) return(-ENODEV);
+ 		if (dl_ctrl[dev->if_unit] == NULL) return(-ENODEV);
+ 		return(dlink_check_mtu(dev, (int) arg));
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
*** original/net/drv/dlink/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/main.c	Mon May  9 14:19:00 1994
***************
*** 0 ****
--- 1,277 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the DLINK device driver.
+  *
+  *		Main program of the DLINK driver.
+  *
+  * Version:	@(#)main.c	0.6.1	12/09/93
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Donald J. Becker, <becker@super.org>
+  *		Johannes Stille, <johannes@titan.os.open.de>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "dlink.h"
+ #include "global.h"
+ 
+ 
+ #define VERSION	"0.6.0"
+ 
+ 
+ int dlink_units = 0;
+ struct dlink	*dl_ctrl[DLINK_MAX_DEV];	/* DLINK control blocks	*/
+ static struct devmap	dlink_map[] = {
+   { 0x3BC,	7,	0 },
+   { 0x378,	7,	0 },
+   { 0x278,	5,	0 },
+   { 0,		0,	0 }
+ };
+   
+ 
+ static int
+ dlink_fopen(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= DLINK_MAX_DEV) return(-ENODEV);
+   if (dl_ctrl[minor] == NULL) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ dlink_fclose(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= DLINK_MAX_DEV) return;
+   if (dl_ctrl[minor] == NULL) return;
+ }
+ 
+ 
+ static struct file_operations dlink_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   dlink_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   dlink_fopen,	/* OPEN		*/
+   dlink_fclose	/* CLOSE	*/
+ };
+ 
+ 
+ /*
+  * Open/initialize the board.
+  * This routine gets exclusive access to the parallel port
+  * by allocating its IRQ line.
+  */
+ static int
+ dlink_open(struct ifnet *dev)
+ {
+   struct dlink *dl;
+ 
+   DPRINTF(("dlink_open: dev 0x%X - %s, unit %d, next 0x%X\n",
+ 			dev, dev->if_name, dev->if_unit, dev->if_next));
+ 
+   dl = dl_ctrl[dev->if_unit];
+   if (dl->ioaddr == DDI_IOAUTO || dl->ioaddr == DDI_IOANY) {
+ 	DPRINTF(("%s: device has not been configured yet!\n",
+ 					dl->dev->if_name));
+ 	return(-ENODEV);
+   }
+ 
+   dlink_adapter_init(dl, -1);	/* allocate resources */
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to dlink_open(). */
+ static int
+ dlink_close(struct ifnet *dev)
+ {
+   struct dlink *dl;
+ 
+   DPRINTF(("dlink_close: dev 0x%X - %s, unit %d, next 0x%X\n",
+ 			dev, dev->if_name, dev->if_unit, dev->if_next));
+ 
+   dl = dl_ctrl[dev->if_unit];
+   if (dl->ioaddr == DDI_IOAUTO || dl->ioaddr == DDI_IOANY) {
+ 	DPRINTF(("%s: device has not been configured yet!\n",
+ 					dl->dev->if_name));
+ 	return(-ENODEV);
+   }
+ 
+   dlink_adapter_init(dl, 0);	/* release resources */
+   return(0);
+ }
+ 
+ 
+ /* Use the next possible I/O address. */
+ static struct devmap *
+ dlink_getaddr(struct dlink *dl)
+ {
+   struct devmap *dp;
+ 
+   dp = dlink_map;
+   while(dp->ioaddr != 0) {
+ 	if (dp->inuse == 0) {
+ 		dl->ioaddr = dp->ioaddr;
+ 		dl->irq    = dp->irq;
+ 		dp->inuse = 1;
+ 		return(dp);
+ 	}
+ 	dp++;
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Mark this address as "in use". */
+ static void
+ dlink_setaddr(struct dlink *dl)
+ {
+   struct devmap *dp;
+ 
+   dp = dlink_map;
+   while(dp->ioaddr != 0) {
+ 	if (dp->ioaddr == dl->ioaddr) {
+ 		dp->inuse = 1;
+ 		return;
+ 	}
+ 	dp++;
+   }
+ }
+ 
+ 
+ /* Check if the device is present. */
+ int
+ dlink_probe(struct dlink *dl)
+ {
+   struct devmap *dp;
+   int ret;
+ 
+   DPRINTF((" dlink_probe, dl=0x%X, addr 0x%X, irq %d ",
+ 				dl, dl->ioaddr, dl->irq));
+ 
+   switch(dl->ioaddr) {
+ 	case DDI_IOAUTO:	/* dynamic auto-probe */
+ 		dp = NULL;
+ 		while(1) {
+ 			if ((dp = dlink_getaddr(dl)) == 0) {
+ 				printk(" - no port found!\n");
+ 				return(-ENODEV);
+ 			}
+ 			if ((ret = dlink_check_port(dl)) == 0) break;
+ 		}
+ 		break;
+ 
+ 	case DDI_IOANY:		/* runtime configuration */
+ 		ret = 0;
+ 		break;
+ 
+ 	default:		/* a specific address was config'd */
+ 		ret = dlink_check_port(dl);
+ 		if (ret == 0) {
+ 			printk(" - adapter found at 0x%03X\n", dl->ioaddr);
+ 			dlink_setaddr(dl);
+ 		} else {
+ 			printk(" - no adapter found at 0x%X !\n", dl->ioaddr);
+ 		}
+ 		break;
+   }
+ 
+   return(ret);
+ }
+ 
+ 
+ int
+ dlink_init(struct ddi *ddi)
+ {
+   struct ifnet *dev;
+   struct dlink *dl;
+   int i;
+ 
+   if (dlink_units == 0) {
+ 	printk("%s: version %s: ", ddi->name, VERSION);
+ 
+ 	/* Register VFS device. */
+ 	if (ddi->major != 0) {
+ 		if (register_chrdev(ddi->major, ddi->name, &dlink_fops) < 0) {
+ 			printk("cannot register device %d!\n", ddi->major);
+ 			return(-EINVAL);
+ 		}
+ 		printk("device %d/%d: OK\n", ddi->major, ddi->minor);
+ 	}
+   }
+   
+   /* Allocate a local control block. */
+   dl = (struct dlink *) kmalloc(sizeof(struct dlink), GFP_KERNEL);
+   if (dl == NULL) {
+ 	printk("%s: No memory for device control block!\n", ddi->name);
+ 	return(-ENOMEM);
+   }
+   memset(dl, 0, sizeof(struct dlink));
+   dl->ioaddr = ddi->io;
+   dl->irq = ddi->irq;
+   dl_ctrl[dlink_units] = dl;
+ 
+   /* Create an interface descriptor. */
+   if ((dev = dev_create(NULL)) == NULL) {
+ 	printk("%s: No memory for interface descriptor!\n", ddi->name);
+ 	kfree_s(dl, sizeof(struct dlink));
+ 	dl_ctrl[dlink_units] = NULL;
+ 	return(-ENOMEM);
+   }
+   dl->dev = dev;
+   dev->if_unit = dlink_units;
+   dev->if_flags = 0;
+   dev->open = dlink_open;
+   dev->close = dlink_close;
+   dev->ioctl = dlink_ioctl;
+   dev->xmit = dlink_xmit;
+   dev->set_multi = NULL;
+ 
+   /* Is the device present? */
+   if ((i = dlink_probe(dl)) != 0) {
+ 	kfree_s(dl, sizeof(struct dlink));
+ 	dl_ctrl[dlink_units] = NULL;
+ 	dev_free(dev);
+ 	return(i);
+   }
+   dlink_units++;
+ 
+   printk(" (");
+   if (dl->ioaddr == DDI_IOAUTO) printk("[AUTOPROBE]");
+     else if (dl->ioaddr == DDI_IOANY) printk("[USERCFG]");
+     else printk("IO=0x%X, IRQ%d, ADDR=%s",
+ 	  dl->ioaddr, dl->irq, dev->mac_sprint(dev->mac_aget(dev)));
+   printk("): OK\n");
+ 
+   /* All done... */
+   return(0);
+ }
*** original/net/drv/dlink/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/packet.c	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,396 ----
+ /*
+  *	d_link.c
+  *
+  *	Portions (C) Copyright 1993 by Bjorn Ekwall
+  *	The Author may be reached as bj0rn@blox.se
+  *
+  *	Linux driver for the D-Link Ethernet pocket adapter.
+  *	Based on sources from linux 0.99pl5
+  *	and on a sample network driver core for linux.
+  *
+  *	Sample driver written 1993 by Donald Becker <becker@super.org>
+  *	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+  *
+  *	Based on adapter information gathered from DE600.ASM by D-Link Inc.,
+  *	as included on disk C in the v.2.11 of PC/TCP from FTP Software.
+  *
+  *	For DE600.asm:
+  *		Portions (C) Copyright 1990 D-Link, Inc.
+  *		Copyright, 1988-1992, Russell Nelson, Crynwr Software
+  *
+  *	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. /
+  */
+ 
+ /* Add another "; SLOW_DOWN_IO" here if your adapter won't work OK: */
+ #define D_LINK_SLOW_DOWN SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO
+ 
+ #if HAVE_SLOW_IO
+ #   define REALLY_SLOW_IO
+ #   define SLOW_IO_BY_JUMPING
+ #endif
+ 
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/fcntl.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/in.h>
+ #include <linux/ptrace.h>
+ #include <linux/errno.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+ #include "dlink.h"
+ #include "global.h"
+ 
+ 
+ static struct dlink	*irq2dlink_map[16];
+ 
+ 
+ static inline int
+ d_link_read_status(struct dlink *dl)
+ {
+   int status;
+ 
+   outb_p(STATUS, DATA_PORT);
+   status = inb(STATUS_PORT);
+   outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
+ 
+   return(status);
+ }
+ 
+ 
+ static inline unsigned char
+ d_link_read_byte(struct dlink *dl, unsigned char type)
+ {
+   unsigned char lo;
+ 
+   (void) outb_p((type), DATA_PORT);
+   lo = ((unsigned char) inb(STATUS_PORT)) >> 4;
+   (void) outb_p((type) | HI_NIBBLE, DATA_PORT);
+   return(((unsigned char) inb(STATUS_PORT) & (unsigned char) 0xf0) | lo);
+ }
+ 
+ 
+ static inline void
+ d_link_trigger_interrupt(struct dlink *dl)
+ {
+   d_link_put_command(FLIP_IRQ);
+   select_prn();
+   D_LINK_SLOW_DOWN;
+   select_nic();
+   d_link_put_command(0);
+ }
+ 
+ 
+ /* Returns 1 if tx still not done. */
+ static int
+ d_link_tx_intr(struct dlink *dl, int irq_status)
+ {
+   mark_bh(NET_BH);
+ 
+   /* Check if current transmission is done yet. */
+   if (irq_status & TX_BUSY) return(1); /* tx not done, try again */
+ 
+   /* If last transmission OK then bump fifo index. */
+   if (!(irq_status & TX_FAILED16)) {
+ 	dl->tx_fifo_out = (dl->tx_fifo_out + 1) % TX_PAGES;
+ 	dl->free_tx_pages++;
+ 	dl->dev->if_stats->tx_packets++;
+ 	dl->dev->if_tbusy = 0;
+   }
+ 
+   /* More to send, or resend last packet? */
+   if ((dl->free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) {
+ 	dl->dev->if_txtimer = jiffies;
+ 	d_link_setup_address(dl->tx_fifo[dl->tx_fifo_out], TX_ADDR);
+ 	d_link_put_command(TX_ENABLE);
+ 	return(1);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /* We have a good packet, get it out of the adapter. */
+ static void
+ d_link_rx_intr(struct dlink *dl)
+ {
+   struct sk_buff *skb;
+   int i, read_from;
+   int size;
+   register unsigned char *buffer;
+ 
+   cli();
+ 
+   /* Get size of received packet */
+   size = d_link_read_byte(dl, RX_LEN);		/* low byte */
+   size += (d_link_read_byte(dl, RX_LEN) << 8);	/* high byte */
+   size -= 4;				/* ignore trailing 4 CRC-bytes */
+ 
+   /* Tell adapter where to store next incoming packet, enable receiver. */
+   read_from = rx_page_adr();
+   next_rx_page();
+   d_link_put_command(RX_ENABLE);
+   sti();
+ 
+   if ((size < 32) || (size > ETH_FRAME_LEN))
+ 	printk("%s: bogus packet size %d\n", dl->dev->if_name, size);
+ 
+   skb = skb_create(size);
+   if (skb == NULL) {
+ 	printk("%s: couldn't allocate a sk_buff of size %d\n",
+ 		dl->dev->if_name, size);
+ 	dl->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+   buffer = skb->raw;
+ 
+   /* Copy the packet into the buffer. */
+   d_link_setup_address(read_from, RW_ADDR);
+   for (i = size; i > 0; --i, ++buffer)
+ 	*buffer = d_link_read_byte(dl, READ_DATA);
+ 	
+   dl->dev->if_stats->rx_packets++; /* count all receives */
+ 
+   if (dev_rint(dl->dev, skb, size)) {
+ 	printk("%s: receive buffers full\n", dl->dev->if_name);
+ 	return;
+   }
+ 
+   /*
+    * If any worth-while packets have been received, dev_rint()
+    * has done a mark_bh(INET_BH) for us and will work on them
+    * when we get to the bottom-half routine.
+    */
+ }
+ 
+ 
+ /*
+  * The typical workload of the driver:
+  * Handle the network interface interrupts.
+  */
+ static void
+ d_link_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct dlink *dl = irq2dlink_map[irq];
+   unsigned char	irq_status;
+   int retrig = 0;
+   int boguscount = 0;
+ 
+   /* This might just as well be deleted now, no crummy drivers present :-) */
+   if ((dl == NULL) || (dl->start == 0) || (dl->irq != irq)) {
+ 	printk("D-LINK: interrupt: IRQ%d for unknown device\n", irq);
+ 	return;
+   }
+ 
+   dl->interrupt = 1;
+   select_nic();
+   irq_status = d_link_read_status(dl);
+ 
+   /*
+    * Yeah, it _looks_ like busy waiting, smells like busy waiting
+    * and I know it's not PC, but please, it will only occur once
+    * in a while and then only for a loop or so (< 1ms for sure!)
+    */
+   do {
+ 	DPRINTF(("d_link_interrupt (%2.2X)\n", irq_status));
+ 
+ 	if (irq_status & RX_GOOD) d_link_rx_intr(dl);
+ 	  else if (!(irq_status & RX_BUSY)) d_link_put_command(RX_ENABLE);
+ 
+ 	/* Any transmission in progress? */
+ 	if (dl->free_tx_pages < TX_PAGES)
+ 		retrig = d_link_tx_intr(dl, irq_status);
+ 	  else
+ 		retrig = 0;
+ 
+ 	irq_status = d_link_read_status(dl);
+   } while ((irq_status & RX_GOOD) || ((++boguscount < 10) && retrig));
+ 
+   /* Enable adapter interrupts */
+   dl->interrupt = 0;
+   select_prn();
+ 
+   if (retrig) d_link_trigger_interrupt(dl);
+ 
+   sti();
+   return;
+ }
+ 
+ 
+ void
+ dlink_adapter_init(struct dlink *dl, int startp)
+ {
+   unsigned char *ptr;
+   int i;
+ 
+   cli();
+   dl->interrupt = 0;
+   dl->start = 1;
+   dl->dev->if_tbusy = 0;
+ 
+   select_nic();
+   dl->rx_page = 0; /* used by RESET */
+   d_link_put_command(RESET);
+   d_link_put_command(STOP_RESET);
+ 
+   if (startp < 0) {
+ 	if (request_irq(dl->irq, d_link_interrupt)) {
+ 		DPRINTF(("%s: unable to get IRQ%d !\n",
+ 				dl->dev->if_name, dl->irq));
+ 		return;
+ 	}
+ 	irq2dlink_map[dl->irq] = dl;
+   } else if (startp == 1) {
+ 	dl->tx_fifo_in = 0;
+ 	dl->tx_fifo_out = 0;
+ 	dl->free_tx_pages = TX_PAGES;
+ 
+ 	/* Set the ether address. */
+ 	d_link_setup_address(NODE_ADDRESS, RW_ADDR);
+ 	ptr = (dl->dev->mac_aget(dl->dev))->sa_data;
+ 	for (i = 0; i < ETH_ALEN; i++)
+ 		d_link_put_byte(ptr[i]);
+ 
+ 	/* Where to start saving incoming packets. */
+ 	dl->rx_page = RX_BP | RX_BASE_PAGE;
+ 	d_link_setup_address(MEM_4K, RW_ADDR);
+ 
+ 	/* Enable receiver */
+ 	d_link_put_command(RX_ENABLE);
+   } else {
+ 	free_irq(dl->irq);
+ 	irq2dlink_map[dl->irq] = NULL;
+ 	d_link_put_command(0);
+   }
+ 
+   select_prn();
+   sti();
+ }
+ 
+ 
+ /* Copy a buffer to the adapter transmit page memory. Start sending. */
+ int
+ dlink_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   int len, tickssofar;
+   int transmit_from;
+   struct dlink *dl;
+   unsigned char	*buffer = skb->raw;
+ 
+   /*
+    * If some higher layer thinks we've missed a tx-done interrupt we
+    * are passed NULL. Caution: dev_tint() handles the cli()/sti() itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	return(0);
+   }
+ 
+   dl = dl_ctrl[dev->if_unit];
+   if (dl->free_tx_pages <= 0) {	/* do timeouts, to avoid hangs */
+ 	tickssofar = jiffies - dev->if_txtimer;
+ 	if (tickssofar < 5) return(1);
+ 
+ 	printk("%s: transmit timed out (%d), %s?\n",
+ 		dev->if_name, tickssofar, "network cable problem");
+ 	/* Restart the adapter. */
+ 	dlink_adapter_init(dl, 1);
+   }
+ 
+   /* Start real output */
+   DPRINTF(("d_link_start_xmit:len=%d, page %d/%d\n",
+ 		skb->len, dl->tx_fifo_in, dl->free_tx_pages));
+ 
+   if ((len = skb->len) < ETH_ZLEN) len = ETH_ZLEN;
+ 
+   cli();
+   select_nic();
+ 
+   dl->tx_fifo[dl->tx_fifo_in] = transmit_from =
+ 			tx_page_adr(dl->tx_fifo_in) - len;
+   dl->tx_fifo_in = (dl->tx_fifo_in + 1) % TX_PAGES; /* next free tx page */
+ 
+   d_link_setup_address(transmit_from, RW_ADDR);
+   for ( ; len > 0; --len, ++buffer)
+ 	d_link_put_byte(*buffer);
+ 
+   if (dl->free_tx_pages-- == TX_PAGES) { /* no transmission going on */
+ 	dev->if_txtimer = jiffies;
+ 	dev->if_tbusy = 0;	/* allow more packets into adapter */
+ 
+ 	/* Send page and generate an interrupt */
+ 	d_link_setup_address(transmit_from, TX_ADDR);
+ 	d_link_put_command(TX_ENABLE);
+   } else {
+ 	dev->if_tbusy = !dl->free_tx_pages;
+ 	select_prn();
+   }
+ 	
+   sti();
+ 	
+   return(0);
+ }
+ 
+ 
+ /* Check if a D-Link Pocket Adapter is present. */
+ int
+ dlink_check_port(struct dlink *dl)
+ {
+   struct sockaddr sa;
+   int i;
+ 
+   dl->rx_page = 0;
+   select_nic();
+   (void) d_link_read_status(dl);
+   d_link_put_command(RESET);
+   d_link_put_command(STOP_RESET);
+   if (d_link_read_status(dl) & 0xF0) {
+ 	return(-ENODEV);
+   }
+ 
+   /* Maybe we found one, have to check if it is a D-Link adapter... */
+ 
+   /* Get the adapter ethernet address from the ROM */
+   d_link_setup_address(NODE_ADDRESS, RW_ADDR);
+   for (i = 0; i < dl->dev->if_macalen; i++) {
+ 	sa.sa_data[i] = d_link_read_byte(dl, READ_DATA);
+   }
+ 
+   /* Check magic code */
+   if ((((unsigned char) sa.sa_data[1]) == ((unsigned char) 0xDE)) &&
+       (((unsigned char) sa.sa_data[2]) == ((unsigned char) 0x15))) {
+ 	/* OK, install real address */
+ 	sa.sa_data[0] = (unsigned char) 0x00;
+ 	sa.sa_data[1] = (unsigned char) 0x80;
+ 	sa.sa_data[2] = (unsigned char) 0xC8;
+ 	sa.sa_data[3] &= (unsigned char) 0x0F;
+ 	sa.sa_data[3] |= (unsigned char) 0x70;
+ 
+ 	/* Attach the ETH service. */
+ 	eth_setup(dl->dev);
+ 	(void) dl->dev->mac_aset(dl->dev, &sa);
+ 
+ 	return(0);
+   }
+   return(-ENODEV);
+ }
*** original/net/drv/dlink/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/dlink/utils.c	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,80 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the D-LINK device driver.
+  *
+  *		Various functions.
+  *
+  * Version:	@(#)utils.c	0.6.0	11/29/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <stdarg.h>
+ #include "dlink.h"
+ #include "global.h"
+ 
+ 
+ extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+ 
+ static int		dlink_debug = 0;	/* D-LINK debug flag	*/
+ 
+ 
+ #ifdef DLINK_DEBUG
+ void
+ dlink_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+ 
+   if (dlink_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* DLINK_DEBUG */
+ 
+ 
+ int
+ dlink_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		dlink_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		dlink_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			dlink_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/drv/loop/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/Makefile	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,36 ----
+ #
+ # Makefile for the Linux LOOPBACK network device driver.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ OBJS	= main.o utils.o ioctl.o packet.o
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ loop.o:		$(OBJS)
+ 		ld -r -o loop.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/loop/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/global.h	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,37 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		LOOPBACK global definitions.
+  *
+  * Version:	@(#)global.h	1.2.2	11/29/93
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _LOOP__GLOBAL_H
+ #define _LOOP__GLOBAL_H
+ 
+ 
+ /* Packet I/O functions. */
+ extern int	loop_ioctl(struct inode *inode, struct file *file,
+ 			   unsigned int cmd, unsigned long arg);
+ extern int	loop_recv(struct ifnet *dev, struct sk_buff *skb, int len);
+ extern int	loop_xmit(struct ifnet *dev, struct sk_buff *skb);
+ extern void	loop_send(struct ifnet *dev, struct sk_buff *skb,
+ 			  struct sockaddr *target, int priority);
+ extern void	loop_dump(struct sk_buff *skb);
+ extern int	loop_mac1(struct ifnet *dev, struct sk_buff *skb, int type,
+ 			  struct sockaddr *src, struct sockaddr *dst);
+ 
+ /* Various. */
+ extern void	loop_dprintf(char *fmt, ...);
+ extern int	loop_dbg_ioctl(unsigned long arg, int level);
+ 
+ 
+ #endif	/* _LOOP_GLOBAL_H */
*** original/net/drv/loop/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/ioctl.c	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,68 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		LOOPBACK I/O control function handlers.
+  *
+  * Version:	@(#)ioctl.c	1.2.3	04/05/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include "loop.h"
+ #include "global.h"
+ 
+ 
+ /* This function handles all Loopback I/O control requests. */
+ int
+ loop_ioctl(struct inode *inode, struct file *file,
+ 	   unsigned int cmd, unsigned long arg)
+ {
+   struct ifnet *dev;
+   int err;
+ 
+   dev = (struct ifnet *) inode;
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(loop_dbg_ioctl(arg, 1));
+ 
+ 	case SIOCSIFFLAGS:
+ 		if (dev == NULL) return(-EINVAL);
+ 		dev->if_flags |= (IFF_LOOPBACK | IFF_BROADCAST);
+ 		return(0);
+ 
+ 	case SIOCSIFMTU:
+ 		if (dev == NULL) return(-EINVAL);
+ 		err = (int) arg;
+ 		if (err < 0 || err > 8000) return(-EINVAL);
+ 		dev->if_mtu = err;
+ 		return(0);
+ 
+ 	default:
+ 		break;
+   }
+ 
+   return(-EINVAL);
+ }
*** original/net/drv/loop/loop.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/loop.h	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,44 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		LOOPBACK contant definitions.
+  *
+  * Version:	@(#)loop.h	1.2.2	11/29/93
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _LOOP_H
+ #define _LOOP_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	LOOP_DEBUG
+ #define LOOP_COPY_PKT	0		/* copy loopback packets	*/
+ 
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  LOOP_DEBUG
+ #   define LOOP_DEBUG
+ #endif
+ 
+ #ifdef	LOOP_DEBUG
+ #   define DPRINTF(x)	loop_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ extern int	loop_init(struct ddi *ddi);
+ 
+ 
+ #endif	/* _LOOP_H */
*** original/net/drv/loop/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/main.c	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,133 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		LOOPBACK main program and setup functions.
+  *
+  * Version:	@(#)main.c	1.2.2	11/29/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <asm/io.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <linux/tty.h>
+ #include <linux/types.h>
+ #include <linux/ptrace.h>
+ #include <linux/string.h>
+ #include <linux/socket.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/if_loop.h>
+ #include "loop.h"
+ #include "global.h"
+ 
+ 
+ #define VERSION		"1.2.2"
+ 
+ 
+ static int
+ loop_open(struct inode * inode, struct file * file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor != 0) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ loop_close(struct inode * inode, struct file * file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor != 0) return;
+ }
+ 
+ 
+ static struct file_operations loop_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   loop_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   loop_open,	/* OPEN		*/
+   loop_close	/* CLOSE	*/
+ };
+ 
+ 
+ /* Initialize the LOOPBACK interface. */
+ int
+ loop_init(struct ddi *ddi)
+ {
+   struct ifnet *dev;
+ 
+ #ifdef LOOP_DEBUG
+   printk("%s: version %s: ", ddi->name, VERSION);
+ #endif
+ 
+   /* Register VFS devices. */
+   if (ddi->major != 0) {
+ 	if (register_chrdev(ddi->major, ddi->name, &loop_fops) < 0) {
+ #ifdef LOOP_DEBUG
+ 		printk("cannot register device %d!\n", ddi->major);
+ #endif
+ 		return(-EINVAL);
+ 	}
+ #ifdef LOOP_DEBUG
+ 	printk("device %d/%d: ", ddi->major, ddi->minor);
+ #endif
+   }
+ #ifdef LOOP_DEBUG
+   printk("OK\n");
+ #endif
+ 
+   /* Ask the DEV module for an interface descriptor. */
+   if ((dev = dev_create("lo")) == NULL) {
+ #ifdef LOOP_DEBUG
+ 	printk("%s: unable to get interface descriptor!\n", ddi->name);
+ #endif
+ 	return(-1);
+   }
+   dev->if_flags		= IFF_LOOPBACK;
+   dev->if_mtu		= LOOP_MTU;
+   dev->if_tbusy		= 0;
+   dev->open		= NULL;
+   dev->close		= NULL;
+   dev->ioctl		= loop_ioctl;
+   dev->xmit		= loop_xmit;
+   dev->send		= loop_send;
+   dev->recv		= loop_recv;
+   dev->mac1		= loop_mac1;
+   dev->mac2		= NULL;
+   dev->mac_aget		= NULL;
+   dev->mac_aset		= NULL;
+   dev->mac_bget		= NULL;
+   dev->mac_bset		= NULL;
+   dev->mac_print	= NULL;
+   dev->mac_sprint	= NULL;
+   dev->set_multi	= NULL;
+ 
+   dev->if_macaddr->ifa_addr.sa_family = ARPHRD_LOOPBACK;/* (255)	*/
+   dev->if_machlen	= LOOP_HLEN;
+   dev->if_macalen	= LOOP_ALEN;
+ 
+   return(0);
+ };
*** original/net/drv/loop/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/packet.c	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,164 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		LOOPBACK packet I/O functions.
+  *
+  * Version:	@(#)packet.c	1.2.5	03/13/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <asm/io.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <linux/tty.h>
+ #include <linux/types.h>
+ #include <linux/ptrace.h>
+ #include <linux/string.h>
+ #include <linux/socket.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_loop.h>
+ #include <linux/if_ether.h>
+ #include "loop.h"
+ #include "global.h"
+ 
+ 
+ /*
+  * Transmit a packet. 
+  * This is the `hardware' level entry point for this driver, which
+  * in a real device driver would copy the packet into the hardware
+  * that physically transmits the bytes, like the shared memory of
+  * an Ethernet controller card.
+  *
+  * The LOOPBACK device simple copies the packet to the DEV generic
+  * receive routine, which is used by all `hardware' device drivers
+  * to send their received packets to.  Really loopback... :-)
+  */
+ int
+ loop_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+ #if LOOP_COPY_PKT
+   struct sk_buff *skb2;
+ #endif
+   int done;
+ 
+   DPRINTF(("LOOP: xmit(%s, skb=0x%X)\n", dev->if_name, skb));
+   if (skb == NULL) return(0);
+ 
+   cli();
+   if (dev->if_tbusy != 0) {
+ 	sti();
+ 	return(1);
+   }
+   dev->if_tbusy = 1;
+   sti();
+ 
+   /* A true loopback: send packet to upper protocol switching layer. */
+   DPRINTF(("LOOP: xmit: sending...\n"));
+ #if LOOP_COPY_PKT
+   /* Copy the packet. */
+   skb2 = skb_duplicate(skb);
+   if (skb2 == NULL) {
+ 	DPRINTF(("LOOP: xmit: no memory for packet - dropping it !\n"));
+ 	dev->if_stats->tx_dropped++;
+ 	cli();
+ 	dev->if_tbusy = 0;
+ 	sti();
+ 	return(1);
+   }
+   skb2->free = 1;
+   skb2->lock = 0;
+   done = dev_rint(dev, skb2, skb2->len);
+ #else
+   done = dev_rint(dev, skb, skb->len);
+ #endif
+ 
+   /* Do until DEV says it's OK. */
+   while (done != 1) {
+ 	done = dev_rint(dev, NULL, 0);
+   }
+   cli();
+   dev->if_tbusy = 0;
+   sti();
+ 
+   DPRINTF(("LOOP: xmit: done\n"));
+   return(0);
+ }
+ 
+ 
+ /*
+  * Process an incoming packet.
+  * This function is called by the DEV bottom-half interrupt handler
+  * function ("net_bh") when it finds packets in the 'received' queue.
+  * Its purpose if to dump the MAC header of the packet, strip it from
+  * the rest of the packet, and set up the pointers and lengths to
+  * reflect this.  It returns the `frame ID type' word that the calling
+  * function uses to check which protocol handlers need to get this
+  * packet.
+  */
+ int
+ loop_recv(struct ifnet *dev, struct sk_buff *skb, int len)
+ {
+   unsigned short *type;
+ 
+   DPRINTF(("LOOP: %s recv: len=%d\n", dev->if_name, len));
+ #ifdef LOOP_DEBUG
+   loop_dump(skb);
+ #endif
+ 
+   /* Update the interface statistics. */
+   dev->if_stats->rx_packets++;
+ 
+   /* Loopback only has a small MAC header. */
+   type = (unsigned short *) (skb + 1);
+   skb->raw = (unsigned char *) (type + 1);
+   skb->len -= dev->if_machlen;
+   skb->qprev = NULL;
+   skb->qnext = NULL;
+ 
+   return((int) *type);
+ }
+ 
+ 
+ /*
+  * Send out a packet.
+  * This is the function called by the DEV generic queueing function to
+  * prepare a packet for physical transmission by the device driver entry
+  * point, which in our case is `loop_xmit'.  It can be used for dumping
+  * MAC headers, and for setting up pointers correctly.
+  */
+ void
+ loop_send(struct ifnet *dev, struct sk_buff *skb,
+ 	  struct sockaddr *target, int priority)
+ {
+   DPRINTF(("LOOP: %s sent: len=%d\n", dev->if_name, skb->len));
+ #ifdef LOOP_DEBUG
+   loop_dump(skb);
+ #endif
+ 
+ #if 0
+   skb->raw = (unsigned char *) (skb + 1);
+   skb->len += dev->if_machlen;
+ #endif
+   skb->arp = 1;	/* make sure we NEVER call ARP for this! */
+ 
+   /* Update the interface statistics. */
+   dev->if_stats->tx_packets++;
+ 
+   /* Call the DEV interface for the actual work. */
+   dev_queue(dev, skb, target, priority);
+ }
*** original/net/drv/loop/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/loop/utils.c	Mon May  9 13:13:05 1994
***************
*** 0 ****
--- 1,111 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *
+  *		LOOPBACK various utility functions.
+  *
+  * Version:	@(#)utils.c	1.2.2	11/29/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <stdarg.h>
+ #include "loop.h"
+ #include "global.h"
+ 
+ 
+ static int loop_debug = 0;		/* LOOPBACK debugging flag	*/
+ 
+ 
+ #ifdef LOOP_DEBUG
+ void
+ loop_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (loop_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* LOOP_DEBUG */
+ 
+ 
+ int
+ loop_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		loop_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		loop_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			loop_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /* Dump a loopback MAC header, which is only the `frame ID' word. */
+ #ifdef LOOP_DEBUG
+ void
+ loop_dump(struct sk_buff *skb)
+ {
+   unsigned short *ptr;
+ 
+   if (loop_debug == 0) return;
+ 
+   ptr = (unsigned short *) (skb + 1);
+   printk("      TYPE=%04X\n", *ptr);
+ }
+ #endif
+ 
+ 
+ /* Create the loopback MAC header. */
+ int
+ loop_mac1(struct ifnet *dev, struct sk_buff *skb, int type,
+ 	  struct sockaddr *src, struct sockaddr *dst)
+ {
+   unsigned short *ptr;
+ 
+   ptr = (unsigned short *) (skb + 1);
+   *ptr = (unsigned short) type;
+ 
+   DPRINTF(("LOOP: mac1(%s, 0x%X, 0x%X)\n", dev->if_name, skb, type));
+ 
+   /* Return the header length. */
+   return(dev->if_machlen);
+ }
*** original/net/drv/plip/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/Makefile	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,36 ----
+ #
+ # Makefile for the Linux PLIP device driver.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ OBJS	= main.o utils.o ioctl.o packet.o
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ plip.o:		$(OBJS)
+ 		ld -r -o plip.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/plip/README	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/README	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,221 ----
+ 
+ 
+ 
+ 
+ 
+ 			    P L I P
+ 
+ 
+ 	     The Parallel Line Internet Protocol Device
+ 
+ 
+ 		      Version 0.8.0 (03/14/94)
+ 
+ 
+ 		  Donald Becker (becker@super.org
+ 	I.D.A. Supercomputing Research Center, Bowie MD 20715
+ 
+ 		Tommy Thorn (tthorn@daimi.aau.dk)
+ 
+ 	 Fred N. van Kempen (waltje@uwalt.nl.mugnet.org)
+ 
+ 	  TANABE Hiroyasu (hiro@sanpo.t.u-tokyo.ac.jp)
+ 
+ 	  Johannes Stille (johannes@titan.westfalen.de)
+ 		
+ 
+ ABSTRACT
+ 	This document describes the parallel port packet pusher for
+ 	Linux.  This device interface allows a point-to-point
+ 	connection between two parallel ports to appear as a IP
+ 	network interface.
+ 
+ 
+ 
+ INTRODUCTION
+ 	This is the completely revised version of the PLIP driver.
+ 	If you have tried out a previous version, try again, this one
+ 	should actually work.
+ 
+ 	The name suggests that only IP can be used with this protocol:
+ 	that is not true.  However, until we get the driver fixed, and
+ 	the other implementations of PLIP (most notably: the MS-DOS
+ 	"packet" driver for PLIP) sorted out, IP is the only protocol
+ 	supported...
+ 
+ 	This is a testing release. If you have problems or questions
+ 	about the driver you cannot solve yourself, mail to
+ 	johannes@titan.westfalen.de. (But please NO configuration
+ 	questions such as "What broadcast address should my plip
+ 	interface have?") Bug reports, suggestions for enhancements etc.
+ 	are welcome.
+ 
+ 	If the driver works for you, please report your success to the
+ 	same address; please include cpu types and speeds, the cable
+ 	length, and the transfer rates you get, like this: [the author's
+ 	setup]
+ 	386-16 <-> 486-33,	10m,	9kB/s <-, 16kB/s ->	(with ftp)
+ 
+ 	This driver is intended to be compatible with the Crynwyr driver,
+ 	but it probably isn't yet. (anyone to check this?)
+ 
+ HISTORY
+ 	First versions by Donald Becker. Much improved by TANABE
+ 	Hiroyasu and Donald Becker (version 0.14). Adaption to net-2e
+ 	and DDI by Fred van Kempen (version 0.5.0) based on the
+ 	non-functional version 0.04. Integration of both versions,
+ 	some bug-fixes and a few (I hope) enhancements by Johannes
+ 	Stille (version 0.6.0).
+ 
+ 1.  PLIP hardware interconnection
+ 	PLIP uses several different data transfer methods.  The first
+ 	(and the only one implemented in the early version of the
+ 	code) uses a standard printer "null" cable to transfers data
+ 	four bits at a time using data bit outputs connected to status
+ 	bit inputs.
+ 
+ 	The second data transfer method relies on both machines having
+ 	bi-directional parallel ports, rather than output-only
+ 	``printer'' ports.  This allows byte-wide transfers and avoids
+ 	reconstructing nibbles into bytes, leading to much faster data
+ 	transfers.
+ 	This mode is not yet implemented, but it should be easy to do.
+ 	If you'd like to do it, I (Johannes Stille) might be able to
+ 	assist you quite much, so please send me mail.
+ 
+ 1.1  Port Configuration
+ 	For PLIP you can use any standard printer port if it is able
+ 	to trigger an interrupt. As printer interrupts are not used
+ 	by DOS, often printer port interrupts are disabled and/or
+ 	used by other cards. So if the driver doesn't work please
+ 	check whether your hardware is configured correctly.
+ 	Standard configuration is:
+ 
+ 	address 0x3BC, IRQ 7	for a printer port on a video card,
+ 	address 0x378, IRQ 7	for first printer port,
+ 	address 0x278, IRQ 5	for second printer port.
+ 
+ 	If you use another port address or IRQ, please change
+ 	"main.c" appropriately.
+ 
+ 1.2  Parallel Transfer Mode 0 Cable
+ 	The cable for the first transfer mode is a standard printer
+ 	"null" cable which transfers data four bits at a time using
+ 	data bit outputs of the first port (machine T) connected to
+ 	the status bit inputs of the second port (machine R).  There
+ 	are five status inputs, and they are used as four data inputs
+ 	and a clock (data strobe) input, arranged so that the data
+ 	input bits appear as contiguous bits with standard status
+ 	register implementation.
+ 
+ 	A cable that implements this protocol is available commercially
+ 	as a "Null Printer" or "Turbo Laplink" cable.  It can be
+ 	constructed with two DB-25 male connectors symmetrically
+ 	connected as follows:
+ 
+ 	GROUND		25 - 25
+ 	D0->ERROR	2 - 15		15 - 2
+ 	D1->SLCT	3 - 13		13 - 3
+ 	D2->PAPOUT	4 - 12		12 - 4
+ 	D3->ACK		5 - 10		10 - 5
+ 	D4->BUSY	6 - 11		11 - 6
+ 	D5		7*
+ 	D6		8*
+ 	D7		9*
+ 	STROBE output	1*
+ 	AUTOFD output	14*
+ 	INIT output	16*
+ 	SLCTIN output	17*
+ 
+ 	Do not connect the pins marked with an asterisk (`*').  They are
+ 	D5 (pin 7), D6 (pin 8) and D7 (pin 9).  STROBE is pin 1, FEED is
+ 	pin 14.
+ 
+ 	Extra grounds are on pins 18, 19, 20, 21, 22, 23 and 24.
+ 
+ 	If the cable you are using has a metallic shield it should be
+ 	connected to the metallic DB-25 shell at one end only.
+ 
+ 1.3  Parallel Transfer Mode 1
+ 	The second data transfer method relies on both machines having
+ 	bi-directional parallel ports, rather than output-only ``printer''
+ 	ports.  This allows byte-wide transfers, and avoids reconstructing
+ 	nibbles into bytes.  This cable should not be used on unidirectional
+ 	``printer'' (as opposed to ``parallel'') ports or when the machine
+ 	isn't configured for PLIP, as it will result in output driver
+ 	conflicts and the (unlikely) possibility of damage.
+ 
+ 	The cable for this tranfer mode should be constructed as follows:
+ 
+ 	STROBE->BUSY	1 - 11
+ 	D0->D0		2 - 2
+ 	D1->D1		3 - 3
+ 	D2->D2		4 - 4
+ 	D3->D3		5 - 5
+ 	D4->D4		6 - 6
+ 	D5->D5		7 - 7
+ 	D6->D6		8 - 8
+ 	D7->D7		9 - 9
+ 	INIT -> ACK	16 - 10
+ 	AUTOFD->PAPOUT	14 - 12
+ 	SLCT->SLCTIN	13 - 17
+ 	GND->ERROR	18 - 15
+ 	GROUND		25 - 25
+ 
+ 	Do not connect the pins marked with an asterisk (`*').
+ 	Extra grounds are on pins 18, 19, 20, 21, 22, 23 and 24.
+ 
+ 	Once again, if the cable you are using has a metallic shield it
+ 	should be connected to the metallic DB-25 shell at one end only.
+ 
+ 
+ 2.  Driver Configuration
+ 	There are a few compile-time parameters for the PLIP driver
+ 	you can change in the file net/drv/plip/global.h.
+ 	They are all timeout values measured in microseconds. If you
+ 	have a especially slow computer you may have to increase them
+ 	especially on the *other* end. On the other hand, high timeouts
+ 	cost performance because you always loose quite many packets
+ 	(for which you then wait longer). (Try twice "ping -f" at the
+ 	same time, and you will see these packet losses.)
+ 
+ 	This driver might not work very well when used with debugging
+ 	kmalloc/kfree, depending on your CPU speed. You might have to
+ 	increase the timeouts and/or to lower the MTU.
+ 
+ 3.  Protocols used.
+ 	PLIP is basically a low-level packet pusher, with no regards
+ 	as to WHAT it is sending.  With the new NET-2 device driver
+ 	setup, this means that one can use PLIP for just about any
+ 	kind of packets, as long as both sides agree.
+ 
+ 
+ 3.1.  PLIP Mode 0 tranfer protocol
+ 	The PLIP driver is compatible with the "Crynwr" parallel port
+ 	transfer standard in Mode 0.  That standard specifies the
+ 	following protocol:
+ 
+ 	send header nibble '8'
+ 	count-low octet
+ 	count-high octet
+ 	... data octets
+ 	checksum octet
+ 
+ 	Each octet is sent as:
+ 
+ 		<wait for rx. '1'> <send 0x10 + (octet & 0x0F) >
+ 		<wait for rx. '0'> <send 0x00 + ((octet >> 4) & 0x0F) >
+ 
+ 	To start a transfer the transmitting machine outputs a nibble
+ 	0x08.  The raises the ACK line, triggering an interrupt in the
+ 	receiving machine.  The receiving machine disables
+ 
+ 	Restated:
+ 
+ 	(OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise)
+ 
+ 	Send_Byte:
+ 		OUT := low nibble, OUT.4 := 1
+ 		WAIT FOR IN.4 = 1
+ 		OUT := high nibble, OUT.4 := 0
+ 		WAIT FOR IN.4 = 0
*** original/net/drv/plip/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/global.h	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,62 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		Define the PLIP device driver internal functions.
+  *
+  * Version:	@(#)global.h	0.8.1	04/05/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #ifndef _PLIP_GLOBAL_H
+ #define _PLIP_GLOBAL_H
+ 
+ 
+ #define PLIP_HEADER_TYPE1 0xFD
+ #define PLIP_HEADER_TYPE2 0xFC
+ 
+ #define PAR_DATA	0
+ #define PAR_STATUS	1
+ #define PAR_CONTROL	2
+ 
+ /* all timeout values in microseconds */
+ #define	NIBBLE_TIMEOUT	2000
+ #define INT_ACK_TIMEOUT	2000
+ #define TRIGGER_TIMEOUT	 200
+ #define	SEND_TIMEOUT	3000
+ #define	FINISH_TIMEOUT	2500
+ #define PROBE_TIMEOUT	  50
+ 
+ 
+ struct plip {
+   int			ioaddr;		/* I/O address to use		*/
+   int			irq;		/* IRQ channel to use		*/
+ 
+   unsigned char		interrupt;	/* we are handling an intr	*/
+ 
+   /* Various fields. */
+   struct ifnet		*dev;		/* easy for intr handling	*/
+ };
+ 
+ 
+ struct devmap {
+   int	ioaddr;
+   int	irq;
+   int	inuse;
+ };
+ 
+ 
+ extern struct plip	*irq2plip_map[];
+ extern struct plip	*pl_ctrl[];		/* PLIP control blocks	*/
+ 
+ extern int		plip_check_port(struct plip *pl);
+ extern void		plip_clear(struct plip *pl);
+ extern void		plip_interrupt(int reg_ptr);
+ extern int		plip_xmit(struct ifnet *dev, struct sk_buff *skb);
+ extern int		plip_ioctl(struct inode *inode, struct file *file,
+ 				   unsigned int cmd, unsigned long arg);
+ extern int		plip_dbg_ioctl(void *arg, int level);
+ extern void		plip_dprintf(int level, char *fmt, ...);
+ 
+ 
+ #endif	/* _PLIP_GLOBAL_H */
*** original/net/drv/plip/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/ioctl.c	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,108 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		PLIP I/O control request handler.
+  *
+  * Version:	@(#)ioctl.c	0.8.1	04/05/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "plip.h"
+ #include "global.h"
+ 
+ 
+ /* Check if the new MTU setting is OK. */
+ static int
+ plip_check_mtu(struct ifnet *dev, int new_mtu)
+ {
+   if ((new_mtu < ETH_ZLEN) || (new_mtu >= ETH_FRAME_LEN)) return(-EINVAL);
+   dev->if_mtu = new_mtu;
+ 
+   return(0);
+ }
+ 
+ 
+ /* Perform I/O control on an active PLIP channel. */
+ int
+ plip_ioctl(struct inode *inode, struct file *file,
+ 	   unsigned int cmd, unsigned long arg)
+ {
+   struct plip *pl;
+   struct ifnet *dev;
+   int err;
+   
+   struct ddi tmp_ddi;
+ 
+   DPRINTF((1, "PLIP: ioctl(0x%X, 0x%X, 0x%X)\n", inode, cmd, arg));
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(plip_dbg_ioctl((void *) arg, 1));
+ 
+ 	case DDIOCGCONF:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memset(&tmp_ddi, 0, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= PLIP_MAX_DEV) return(-ENODEV);
+ 		pl = pl_ctrl[dev->if_unit];
+ 		if (pl == NULL) return(-ENODEV);
+ 
+ 		tmp_ddi.io = pl->ioaddr;
+ 		tmp_ddi.irq = pl->irq;
+ 		memcpy_tofs((void *)arg, &tmp_ddi, sizeof(struct ddi));
+ 
+ 		return(0);
+ 
+ 	case DDIOCSCONF:
+ 		err = verify_area(VERIFY_READ, (void *) arg,
+ 				  sizeof(struct ddi));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&tmp_ddi, (void *)arg, sizeof(struct ddi));
+ 
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= PLIP_MAX_DEV) return(-ENODEV);
+ 		pl = pl_ctrl[dev->if_unit];
+ 		if (pl == NULL) return(-ENODEV);
+ 
+ 		pl->ioaddr = tmp_ddi.io;
+ 		pl->irq = tmp_ddi.irq;
+ 
+ 		return(0);
+ 
+ 	case SIOCSIFMTU:
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= PLIP_MAX_DEV) return(-ENODEV);
+ 		if (pl_ctrl[dev->if_unit] == NULL) return(-ENODEV);
+ 		return(plip_check_mtu(dev, (int) arg));
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
*** original/net/drv/plip/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/main.c	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,297 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		Main program of the PLIP driver.
+  *
+  * Version:	@(#)main.c	0.8.0	03/14/94
+  *
+  * Authors:	Donald J. Becker, <becker@super.org>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "plip.h"
+ #include "global.h"
+ 
+ 
+ #define VERSION	"0.8.4"
+ 
+ 
+ struct plip	*pl_ctrl[PLIP_MAX_DEV];		/* PLIP control blocks	*/
+ static struct devmap	plip_map[] = {
+   { 0x3BC,	7,	0 },
+   { 0x378,	7,	0 },
+   { 0x278,	5,	0 },
+   { 0,		0,	0 }
+ };
+   
+ 
+ static int
+ plip_fopen(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= PLIP_MAX_DEV) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ plip_fclose(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= PLIP_MAX_DEV) return;
+ }
+ 
+ 
+ static struct file_operations plip_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   plip_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   plip_fopen,	/* OPEN		*/
+   plip_fclose	/* CLOSE	*/
+ };
+ 
+ 
+ /*
+  * Open/initialize the board.
+  * This routine gets exclusive access to the parallel port
+  * by allocating its IRQ line.
+  */
+ static int
+ plip_open(struct ifnet *dev)
+ {
+   struct plip *pl;
+ 
+   DPRINTF((8, "plip_open: dev 0x%X - %s, unit %d, next 0x%X\n",
+ 			dev, dev->if_name, dev->if_unit, dev->if_next));
+ 
+   pl = pl_ctrl[dev->if_unit];
+   if (pl->ioaddr == DDI_IOAUTO || pl->ioaddr == DDI_IOANY) {
+ 	DPRINTF((1, "%s: device has not been configured yet!\n",
+ 					pl->dev->if_name));
+ 	return(-ENODEV);
+   }
+ 
+   if (request_irq(pl->irq, plip_interrupt) != 0) {
+ 	DPRINTF((1, "%s: could not allocate IRQ%d !\n",
+ 					dev->if_name, pl->irq));
+ 	return(-EAGAIN);
+   }
+ 
+   irq2plip_map[pl->irq] = pl;
+   plip_clear(pl);
+   return(0);
+ }
+ 
+ 
+ /* The inverse routine to plip_open(). */
+ static int
+ plip_close(struct ifnet *dev)
+ {
+   struct plip *pl;
+ 
+   DPRINTF((8, "plip_close: dev 0x%X - %s, unit %d, next 0x%X\n",
+ 			dev, dev->if_name, dev->if_unit, dev->if_next));
+ 
+   pl = pl_ctrl[dev->if_unit];
+   if (pl->ioaddr == DDI_IOAUTO || pl->ioaddr == DDI_IOANY) {
+ 	DPRINTF((1, "%s: device has not been configured yet!\n",
+ 					pl->dev->if_name));
+ 	return(-ENODEV);
+   }
+ 
+   plip_clear(pl);
+   free_irq(pl->irq);
+   irq2plip_map[pl->irq] = NULL;
+   outb(0x00, pl->ioaddr + PAR_CONTROL);	/* release the interrupt */
+   return(0);
+ }
+ 
+ 
+ /* Use the next possible I/O address. */
+ static struct devmap *
+ plip_getaddr(struct plip *pl)
+ {
+   struct devmap *dp;
+ 
+   dp = plip_map;
+   while(dp->ioaddr != 0) {
+ 	if (dp->inuse == 0) {
+ 		pl->ioaddr = dp->ioaddr;
+ 		pl->irq    = dp->irq;
+ 		dp->inuse = 1;
+ 		return(dp);
+ 	}
+ 	dp++;
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Mark this address as "in use". */
+ static void
+ plip_setaddr(struct plip *pl)
+ {
+   struct devmap *dp;
+ 
+   dp = plip_map;
+   while(dp->ioaddr != 0) {
+ 	if (dp->ioaddr == pl->ioaddr) {
+ 		dp->inuse = 1;
+ 		return;
+ 	}
+ 	dp++;
+   }
+ }
+ 
+ 
+ static int
+ plip_probe(struct plip *pl)
+ {
+   struct devmap *dp;
+   int ret;
+ 
+   DPRINTF((2, " plip_probe, pl=0x%X, addr 0x%X, irq %d ",
+ 				pl, pl->ioaddr, pl->irq));
+ 
+   dp = NULL;
+   switch(pl->ioaddr) {
+ 	case DDI_IOAUTO:	/* dynamic auto-probe */
+ 		while(1) {
+ 			if ((dp = plip_getaddr(pl)) == 0) {
+ 				DPRINTF((2, " - no port found!\n"));
+ 				return(-ENODEV);
+ 			}
+ 			if ((ret = plip_check_port(pl)) == 0) break;
+ 		}
+ 		break;
+ 
+ 	case DDI_IOANY:		/* runtime configuration */
+ 		ret = 0;
+ 		break;
+ 
+ 	default:		/* a specific address was config'd */
+ 		ret = plip_check_port(pl);
+ 		if (ret == 0) {
+ 			DPRINTF((2, " - port found at 0x%03X\n", pl->ioaddr));
+ 			plip_setaddr(pl);
+ 		} else {
+ 			DPRINTF((2, " - no port found at 0x%X !\n", pl->ioaddr));
+ 		}
+ 		break;
+   }
+ 
+   return(ret);
+ }
+ 
+ 
+ int
+ plip_init(struct ddi *ddi)
+ {
+   static int plip_unit_nr = 0;
+   struct sockaddr sa;
+   struct ifnet *dev;
+   struct plip *pl;
+   int i, unit;
+ 
+   if (plip_unit_nr == 0) {
+ 	printk("%s: version %s: ", ddi->name, VERSION);
+ 
+ 	/* Register VFS device. */
+ 	if (ddi->major != 0) {
+ 		if (register_chrdev(ddi->major, ddi->name, &plip_fops) < 0) {
+ 			printk("cannot register device %d!\n", ddi->major);
+ 			return(-EINVAL);
+ 		}
+ 		printk("device %d/%d: OK\n", ddi->major, ddi->minor);
+ 	}
+   }
+   
+   /* Sanity check. */
+   if (ddi->nunits > PLIP_MAX_DEV) return(-EINVAL);
+ 
+   for (unit = 0; unit < ddi->nunits; unit++) {
+ 
+ 	/* Allocate a local control block. */
+ 	pl = (struct plip *) kmalloc(sizeof(struct plip), GFP_KERNEL);
+ 	if (pl == NULL) {
+ 		printk("PLIP: Error: no memory for device control block!\n");
+ 		return(-ENOMEM);
+ 	}
+ 
+ 	memset(pl, 0, sizeof(struct plip));
+ 	pl->ioaddr = ddi->io;
+ 	pl->irq = ddi->irq;
+ 	pl_ctrl[plip_unit_nr] = pl;
+ 	if (plip_probe(pl) != 0) {
+ 		kfree(pl);
+ 		continue;
+ 	}
+ 
+ 	/* Ok, this is possible.  Create a network interface. */
+ 	if ((dev = dev_create(NULL)) == NULL) {
+ 		printk("%s: no memory for interface descriptor!\n", ddi->name);
+ 		kfree(pl);
+ 		return(-ENOMEM);
+ 	}
+ 
+ 	/* Set up the interface name. */
+ 	sprintf(dev->if_name, "plip%d", plip_unit_nr);
+ 	dev->if_unit = plip_unit_nr++;
+ 	pl->dev = dev;
+ 	printk("%s: PLIP", dev->if_name);
+ 
+ 	/* Attach the ETH service. */
+ 	(void) eth_setup(dev);
+ 
+ 	for (i = 0; i < dev->if_macalen; i++) sa.sa_data[i] = i;
+ 	dev->mac_aset(dev, &sa);
+ 
+ 	dev->if_flags = 0;
+ 	dev->open = plip_open;
+ 	dev->close = plip_close;
+ 	dev->ioctl = plip_ioctl;
+ 	dev->xmit = plip_xmit;
+ 	dev->set_multi = NULL;
+ 
+ 	printk(" (");
+ 	if (pl->ioaddr == DDI_IOAUTO) printk("[AUTOPROBE]");
+ 	  else if (pl->ioaddr == DDI_IOANY) printk("[USERCFG]");
+ 	  else printk("IO=0x%X, IRQ=%d, ADDR=%s",
+ 		pl->ioaddr, pl->irq, dev->mac_sprint(dev->mac_aget(dev)));
+ 	printk("): OK\n");
+   }
+ 
+   /* All done... */
+   return(0);
+ }
*** original/net/drv/plip/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/packet.c	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,674 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		Packet I/O handler of the PLIP driver.
+  *
+  * Version:	@(#)packet.c	0.8.0	03/14/94
+  *
+  * Authors:	Donald J. Becker, <becker@super.org>
+  *		Tommy Thorn, <tthorn@daimi.aau.dk>
+  *		TANABE Hiroyasu, <hiro@sanpo.t.u-tokyo.ac.jp>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/fcntl.h>
+ #include <linux/interrupt.h>
+ #include <linux/string.h>
+ #include <linux/ptrace.h>
+ #include <linux/timer.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include <linux/in.h>
+ #include <errno.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include "plip.h"
+ #include "global.h"
+ #include "timer.h"
+ 
+ 
+ struct plip		*irq2plip_map[16];
+ 
+ 
+ void
+ plip_clear(struct plip *pl)
+ {
+   pl->interrupt = 0;
+   pl->dev->if_tbusy = 0;
+   outb(0x00, pl->ioaddr + PAR_DATA);
+   outb(0x10, pl->ioaddr + PAR_CONTROL);	/* Enable the rx interrupt. */
+ }
+ 
+ 
+ static void
+ plip_receiver_error(struct plip *pl)
+ {
+   DPRINTF((6, "%s: receiving error\n", pl->dev->if_name));
+   pl->interrupt = 0;
+   pl->dev->if_tbusy = 0;
+   outb(0x02, pl->ioaddr + PAR_DATA);
+   outb(0x10, pl->ioaddr + PAR_CONTROL);	/* enable the RX interrupt */
+ }
+ 
+ 
+ static int	/* 0 == success, 1 == failure */
+ wait_clear(struct plip *pl)
+ {
+   int starttime = gettimerctr();
+ 
+   while ((inb(pl->ioaddr + PAR_STATUS) & 0xe8) != 0x80) {
+ 	if (time_up(starttime, FINISH_TIMEOUT)) {
+ 		DPRINTF((7, "%s: remote end not reset\n", pl->dev->if_name));
+ 		return(1);
+ 	}
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ plip_rebuild_enethdr(struct plip *pl, struct ethhdr *eth,
+                      unsigned char dest, unsigned char source,
+                      unsigned short type)
+ {
+     eth->h_proto = type;
+     memcpy(eth->h_dest, pl->dev->if_macaddr->ifa_addr.sa_data, ETH_ALEN-1);
+     eth->h_dest[ETH_ALEN-1] = dest;
+     memcpy(eth->h_source, pl->dev->if_macaddr->ifa_addr.sa_data, ETH_ALEN-1);
+     eth->h_source[ETH_ALEN-1] = source;
+ 
+     return(0);
+ }
+ 
+ 
+ /*
+  * Read a full byte from a PLIP-configured parallel port.
+  * Return -1 for failure.
+  */
+ static inline int
+ get_byte(struct plip *pl)
+ {
+   unsigned char val, oldval;
+   unsigned char low_nibble;
+   int starttime;
+ 
+   val = inb(pl->ioaddr + PAR_STATUS);
+   starttime = gettimerctr();
+   do {
+ 	oldval = val;
+ 	val = inb(pl->ioaddr + PAR_STATUS);
+ 	if (oldval != val) continue;		/* it's unstable */
+ 	if (time_up(starttime, NIBBLE_TIMEOUT)) {
+ 		DPRINTF((7, "%s: timeout get 1\n", pl->dev->if_name));
+ 		return(-1);
+ 	}
+   } while (val & 0x80);
+ 
+   val = inb(pl->ioaddr + PAR_STATUS);
+   low_nibble = (val >> 3) & 0x0f;
+   outb(0x11, pl->ioaddr + PAR_DATA);
+   starttime = gettimerctr();
+   do {
+ 	oldval = val;
+ 	val = inb(pl->ioaddr + PAR_STATUS);
+ 	if (oldval != val) continue;		/* it's unstable */
+ 	if (time_up(starttime, NIBBLE_TIMEOUT)) {
+ 		DPRINTF((7, "%s: timeout get 2\n", pl->dev->if_name));
+ 		return(-1);
+ 	}
+   } while(! (val & 0x80));
+ 
+   val = inb(pl->ioaddr + PAR_STATUS);
+   DPRINTF((9, "%02x ", low_nibble | ((val << 1) & 0xf0)));
+   outb(0x01, pl->ioaddr + PAR_DATA);
+ 
+   return(low_nibble | ((val << 1) & 0xf0));
+ }
+ 
+ 
+ /*
+  * Write a full byte to a PLIP-configured parallel port.
+  * Return 0 for success, or -1 for failure.
+  */
+ static inline int
+ send_byte(struct plip *pl, unsigned char val)
+ {
+   int starttime;
+   unsigned char hsk = 0, oldhsk;
+ 
+   DPRINTF((9, " S%02x", val));
+   outb(val & 0x0f, pl->ioaddr + PAR_DATA);
+ 
+   /* This makes data bits more stable. */
+   outb(0x10 | val, pl->ioaddr + PAR_DATA);
+   starttime = gettimerctr();
+   do {
+ 	oldhsk = hsk;
+ 	hsk = inb(pl->ioaddr + PAR_STATUS);
+ 	if (hsk != oldhsk) continue;
+ 	if (time_up(starttime, NIBBLE_TIMEOUT)) {
+ 		DPRINTF((7, "%s: timeout send 1\n", pl->dev->if_name));
+ 		return(-1);
+ 	}
+   } while (hsk & 0x80);
+ 
+   outb(0x10 | (val >> 4), pl->ioaddr);
+   outb(val >> 4, pl->ioaddr);
+   starttime = gettimerctr();
+   do {
+ 	oldhsk = hsk;
+ 	hsk = inb(pl->ioaddr + PAR_STATUS);
+ 	if (hsk != oldhsk) continue;
+ 	if (time_up(starttime, NIBBLE_TIMEOUT)) {
+ 		DPRINTF((7, "%s: timeout send 2\n", pl->dev->if_name));
+ 		return(-1);
+ 	}
+   } while ((hsk & 0x80) == 0);
+ 
+   return(0);
+ }
+ 
+ 
+ /* 0 == success, 1 == failure */
+ static int
+ plip_send_enethdr(struct plip *pl, struct ethhdr *eth)
+ {
+   return(send_byte(pl, eth->h_dest[ETH_ALEN-1]) ||
+ 	 send_byte(pl, eth->h_source[ETH_ALEN-1]) ||
+ 	 send_byte(pl, eth->h_proto >> 8) || send_byte(pl, eth->h_proto));
+ }
+ 
+ 
+ /* 0 == success, 1 == failure */
+ static int
+ plip_receive_packet(struct plip *pl)
+ {
+   int plip_type;
+   unsigned length;
+   int checksum = 0;
+   struct sk_buff *skb;
+   struct ethhdr eth;
+   int byte_in;
+ 	
+   outb(1, pl->ioaddr + PAR_DATA);		 /* Ack: 'Ready' */
+ 
+   /* Get header octet and length of packet. */
+   plip_type = get_byte(pl);
+   if (plip_type < 0) {
+ 	plip_receiver_error(pl);
+ 	return(1); /* probably wrong interrupt */
+   }
+   if ((byte_in = get_byte(pl)) < 0) {
+ 	plip_receiver_error(pl);
+ 	wait_clear(pl);
+ 	return(1);
+   }
+   length = byte_in << 8;
+   if ((byte_in = get_byte(pl)) < 0) {
+ 	plip_receiver_error(pl);
+ 	wait_clear(pl);
+ 	return(1);
+   }
+   length |= byte_in;
+   DPRINTF((8, "length = %d\n", length));
+   if (length > pl->dev->if_mtu + pl->dev->if_machlen || length < 8) {
+ 	DPRINTF((3, "%s: bogus packet size 0x%04x.\n",
+ 				pl->dev->if_name, length));
+ 	plip_receiver_error(pl);
+ 	wait_clear(pl);
+ 	return(1);
+   }
+ 
+   switch(plip_type) {
+ 	  case PLIP_HEADER_TYPE1:
+ 		{
+ 			int i;
+ 			unsigned char *eth_p = (unsigned char*)&eth;
+ 
+ 			for ( i = 0; i < sizeof(eth); i++, eth_p++) {
+ 				if ((byte_in = get_byte(pl)) < 0) {
+ 					plip_receiver_error(pl);
+ 					wait_clear(pl);
+ 					return(1);
+ 				}
+ 				*eth_p = byte_in;
+ 			}
+ 		}
+ 		break;
+ 
+ 	  case PLIP_HEADER_TYPE2:
+ 		{
+ 			unsigned char h_dest, h_source;
+ 			unsigned short type;
+ 
+ 			if ((byte_in = get_byte(pl)) < 0) {
+ 				plip_receiver_error(pl);
+ 				wait_clear(pl);
+ 				return(1);
+ 			}
+ 			h_dest = byte_in;
+ 			if ((byte_in = get_byte(pl)) < 0) {
+ 				plip_receiver_error(pl);
+ 				wait_clear(pl);
+ 				return(1);
+ 			}
+ 			h_source = byte_in;
+ 			if ((byte_in = get_byte(pl)) < 0) {
+ 				plip_receiver_error(pl);
+ 				wait_clear(pl);
+ 				return(1);
+ 			}
+ 			type = byte_in << 8;
+ 			if ((byte_in = get_byte(pl)) < 0) {
+ 				plip_receiver_error(pl);
+ 				wait_clear(pl);
+ 				return(1);
+ 			}
+ 			type |= byte_in;
+ 			plip_rebuild_enethdr(pl, &eth, h_dest, h_source, type);
+ 		}
+ 		break;
+ 
+ 	  default:
+ 		DPRINTF((3, "%s: wrong header octet %02x\n",
+ 					pl->dev->if_name, plip_type));
+ 		plip_receiver_error(pl);
+ 		wait_clear(pl);
+ 		return(1);
+   }
+ 
+ 
+   /* Allocate an skb buffer and read the packet. */
+   skb = skb_create(length);
+   if (skb == NULL) {
+ 	DPRINTF((1, "%s: Couldn't allocate a sk_buff of size %d.\n",
+ 						pl->dev->if_name, length));
+ 	pl->dev->if_stats->rx_dropped++;
+ 	return(1);
+   }
+ 	
+   /* Phase of receiving the data. */
+   {
+ 	/* 'skb->raw' points to the start of sk_buff data area. */
+ 	unsigned char *buf = (unsigned char*) (skb->raw);
+ 	unsigned char *eth_p = (unsigned char *)&eth;
+ 	int i;
+ 
+ 	for ( i = 0; i < sizeof(eth); i++) {
+ 		checksum += *eth_p;
+ 		*buf++ = *eth_p++;
+ 	}
+ 	for ( i = 0; i < length - sizeof(eth); i++) {
+ 		int new_byte = get_byte(pl);
+ 		if (new_byte < 0) {
+ 			plip_receiver_error(pl);
+ 			wait_clear(pl);
+ 			skb_free(skb);
+ 			return(1);
+ 		}
+ 		checksum += new_byte;
+ 		*buf++ = new_byte;
+ 	}
+ 	checksum &= 0xff;
+ 	if (checksum != get_byte(pl)) {
+ 		DPRINTF((3, "%s: checksum error\n", pl->dev->if_name));
+ 		plip_receiver_error(pl);
+ 		wait_clear(pl);
+ 		skb_free(skb);
+ 		return(1);
+ 	} else {
+ 		if(dev_rint(pl->dev, skb, length)) {
+ 			DPRINTF((1, "%s: rcv buff full.\n", pl->dev->if_name));
+ 			plip_receiver_error(pl);
+ 			pl->dev->if_stats->rx_dropped++;
+ 			wait_clear(pl);
+ 			return(1);
+ 		}
+ 	}
+   }
+ 	
+   plip_clear(pl);
+   wait_clear(pl);
+   pl->dev->if_stats->rx_packets++;
+   return(0);
+ }
+ 
+ 
+ /* The typical workload of the driver: handle the parallel port interrupts. */
+ void
+ plip_interrupt(int reg_ptr)
+ {
+   int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+   struct plip *pl= irq2plip_map[irq];
+ 
+   if (pl == NULL) {
+ 	DPRINTF((1, "PLIP: interrupt: IRQ%d for unknown device.\n", irq));
+ 	return;
+   }
+   cli();
+   if (pl->dev->if_tbusy || pl->interrupt) {
+ 	sti();
+ 	DPRINTF((7, "%s: interrupt, device busy\n", pl->dev->if_name));
+ 	return;
+   }
+   pl->interrupt = 1;
+   outb(0x00, pl->ioaddr + PAR_CONTROL);  /* disable the RX interrupt */
+   sti(); /* allow other interrupts */
+   DPRINTF((8, "%s: interrupt\n", pl->dev->if_name));
+ 
+   if (plip_receive_packet(pl)) {
+ 	/* Got some error while receiving data. */
+ 	pl->dev->if_stats->rx_errors++;
+   }
+ }
+ 
+ 
+ /*
+  * plip_send_start
+  * trigger remoto rx interrupt and establish a connection.
+  *
+  * return value
+  * 0 : establish the connection
+  * -1 : connection failed.
+  */
+ static int
+ plip_send_start(struct plip *pl, struct ethhdr *eth)
+ {
+   int starttime;
+   int status = 1;
+   int lasttrigger;
+ 
+   /* This starts the packet protocol by triggering a remote IRQ. */
+   starttime = gettimerctr();
+   lasttrigger = gettimerctr();
+   if (inb(pl->ioaddr + PAR_STATUS) & 0x40) {
+ 	DPRINTF((7, "%s: would-be collision\n", pl->dev->if_name));
+ 	if (plip_receive_packet(pl))
+ 		pl->dev->if_stats->rx_errors++;
+   }
+ 
+   while ((inb(pl->ioaddr + PAR_STATUS) & 0x08) == 0) {
+ 	outb(0x00, pl->ioaddr + PAR_CONTROL); /* Disable my rx intr. */
+ 	if (inb(pl->ioaddr + PAR_STATUS) & 0x40) {
+ 		DPRINTF((7, "%s: collision\n", pl->dev->if_name));
+ 		outb(0x01, pl->ioaddr + PAR_DATA);
+ 		if (plip_receive_packet(pl)) {
+ 			DPRINTF((7, "collision read failed\n"));
+ 			pl->dev->if_stats->rx_errors++;
+ 		} else {
+ 			DPRINTF((7, "collision read success\n"));
+ 			starttime = gettimerctr();
+ 		}
+ 	}
+ 	outb(status << 3, pl->ioaddr + PAR_DATA);
+ 
+ 	/* Trigger remote rx intr. */
+ 	if (time_up(lasttrigger, TRIGGER_TIMEOUT)) {
+ 		/* trigger again */
+ 		DPRINTF((7,"%s: retrigger, status %d, in %02x\n",
+ 			pl->dev->if_name, status,
+ 			inb(pl->ioaddr + PAR_STATUS)));
+ 		status = !status;
+ 		lasttrigger = gettimerctr();
+ 	}
+ 
+ 	if (time_up(starttime,INT_ACK_TIMEOUT)) {
+ 		plip_clear(pl);
+ 		DPRINTF((5, "%s: Connect failed in send_packet().\n",
+ 							pl->dev->if_name));
+ 		return(-1);
+ 	}
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /* Write a buffer of data to a PLIP port. */
+ static int
+ plip_send_packet(struct plip *pl, unsigned char *buf, int length)
+ {
+   int plip_type;
+ 
+   DPRINTF((8, "%s: plip_send_packet(%d) %02x %02x %02x %02x %02x...",
+ 	pl->dev->if_name, length, buf[0], buf[1], buf[2], buf[3], buf[4]));
+ 
+   if (length > pl->dev->if_mtu + pl->dev->if_machlen) {
+ 	printk("%s: packet too big, %d.\n", pl->dev->if_name, length);
+ 	return(0);	/* retrying is useless */
+   }
+ 
+   {
+ 	/* phase of checking remote status */
+ 	int i;
+ 	int starttime = gettimerctr();
+ 
+ 	while ((i = (inb(pl->ioaddr+PAR_STATUS) & 0xe8)) != 0x80) {
+ 		if (i == 0x78) {
+ 			/*
+ 			 * Probably cable is not connected.
+ 			 * This status should result in a network error
+ 			 * return of 'Network unreachable' but I don't
+ 			 * know how to do this.
+ 			 */
+ #if 0
+ 			plip_send_unreach(pl);
+ #endif
+ 			pl->dev->if_stats->tx_errors++;
+ 			plip_clear(pl);
+ 			return(1);
+ 		}
+ 		if (time_up(starttime, SEND_TIMEOUT)) {
+ 			/* Remote end is not ready. */
+ 			pl->dev->if_stats->tx_errors++;
+ 			plip_clear(pl);
+ 			DPRINTF((5, "%s: remote end is not ready.\n",
+ 							pl->dev->if_name));
+ 			return(1);	/* failed to send the packet */
+ 		}
+ 	}
+   }
+   DPRINTF((9, "plip got connection\n"));
+ 
+   /* phase of making a connection */
+   if (plip_send_start(pl, (struct ethhdr *)buf) < 0) {
+ 	DPRINTF((5, "%s: handshake failed\n", pl->dev->if_name));
+ 	pl->dev->if_stats->tx_errors++;
+ 	plip_clear(pl);
+ 	return(1);
+   }
+   DPRINTF((9, "plip: handshake OK\n"));
+ 
+   /*
+    * Select PLIP type.
+    * Use stripped ethernet header if each first 5 octet
+    * of eth address is same.
+    */
+   {
+ 	int i;
+ 	struct ethhdr *eth = (struct ethhdr *)buf;
+ 
+ 	plip_type = PLIP_HEADER_TYPE2;
+ 	for ( i = 0; i < ETH_ALEN - 1; i++) {
+ 		if (eth->h_dest[i] != eth->h_source[i])
+ 				plip_type = PLIP_HEADER_TYPE1;
+ 	}
+   }
+ 
+   /* Send header octet. */
+   if (send_byte(pl, plip_type)) {
+ 	DPRINTF((3, "%s: send error\n", pl->dev->if_name));
+ 	pl->dev->if_stats->tx_errors++;
+ 	plip_clear(pl);
+ 	wait_clear(pl);
+ 	return(1);
+   }
+ 
+   /* send packet's length */
+   /*
+    * in original plip (before v0.1), it was sent with little endian.
+    * but in internet, network byteorder is big endian,
+    * so changed to use big endian.
+    * maybe using 'ntos()' is better.
+    */
+   if (send_byte(pl, length >> 8)) {
+ 	DPRINTF((3, "%s: send error\n", pl->dev->if_name));
+ 	pl->dev->if_stats->tx_errors++;
+ 	plip_clear(pl);
+ 	wait_clear(pl);
+ 	return(1);
+   }
+   if (send_byte(pl, length)) {
+ 	DPRINTF((3, "%s: send error\n", pl->dev->if_name));
+ 	pl->dev->if_stats->tx_errors++;
+ 	plip_clear(pl);
+ 	wait_clear(pl);
+ 	return(1);
+   }
+ 
+   /* Phase of sending data. */
+   {
+ 	int i;
+ 	int checksum = 0;
+ 
+ 	if (plip_type == PLIP_HEADER_TYPE2) {
+ 		if (plip_send_enethdr(pl, (struct ethhdr*)buf)) {
+ 			DPRINTF((3, "%s: send error\n", pl->dev->if_name));
+ 			pl->dev->if_stats->tx_errors++;
+ 			plip_clear(pl);
+ 			wait_clear(pl);
+ 			return(1);
+ 		}
+ 	}
+ 	for (i = 0; i < sizeof(struct ethhdr); i++ ) {
+ 		if (plip_type == PLIP_HEADER_TYPE1) {
+ 			if (send_byte(pl, *buf)) {
+ 				DPRINTF((3, "%s: send error\n",
+ 						pl->dev->if_name));
+ 				pl->dev->if_stats->tx_errors++;
+ 				plip_clear(pl);
+ 				wait_clear(pl);
+ 				return(1);
+ 			}
+ 		}
+ 		checksum += *buf++;
+ 	}
+ 
+ 	for (i = 0; i < length - sizeof(struct ethhdr); i++) {
+ 		checksum += buf[i];
+ 		if (send_byte(pl, buf[i])) {
+ 			DPRINTF((3, "%s: send error\n", pl->dev->if_name));
+ 			pl->dev->if_stats->tx_errors++;
+ 			plip_clear(pl);
+ 			wait_clear(pl);
+ 			return(1);
+ 		}
+ 	}
+ 	if (send_byte(pl, checksum & 0xff)) {
+ 		DPRINTF((3, "%s: send error\n", pl->dev->if_name));
+ 		pl->dev->if_stats->tx_errors++;
+ 		plip_clear(pl);
+ 		wait_clear(pl);
+ 		return(1);
+ 	}
+   }
+ 
+   plip_clear(pl);
+   if (wait_clear(pl)) {
+ 	pl->dev->if_stats->tx_errors++;
+ 	return(1);
+   }
+ 
+   if (inb(pl->ioaddr + PAR_STATUS) & 0x10) {
+ 	/* receiver reports error */
+ 	pl->dev->if_stats->tx_errors++;
+ 	return(1);
+   } else {
+ 	pl->dev->if_stats->tx_packets++;
+   }
+ 
+   DPRINTF((8, "%s: plip_send_packet(%d) done.\n", pl->dev->if_name, length));
+   return(0);
+ }
+ 
+ 
+ /* Write a complete packet to the PLIP port. */
+ int
+ plip_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct plip *pl;
+   int ret_val;
+ 
+   pl = pl_ctrl[dev->if_unit];
+ 
+   /* Do timeouts, to avoid hangs. */
+   cli();
+   if (dev->if_tbusy || pl->interrupt) {
+ 	int tickssofar = jiffies - dev->if_txtimer;
+ 	sti();
+ 	if (tickssofar < 50) return(1);
+ 	printk("%s: transmit timed out\n", dev->if_name);
+ 
+ 	/* Try to restart the adapter. */
+ 	plip_clear(pl);
+ #if PLIP_DEV_RETRY
+ 	return(1);
+ #else
+ 	return(0);
+ #endif
+   }
+   dev->if_tbusy = 1;
+   sti();
+ 
+   /*
+    * If some higher layer thinks we've missed an tx-done interrupt
+    * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+    * itself.
+    */
+   if (skb == NULL) {
+ 	dev_tint(dev);
+ 	plip_clear(pl);
+ 	return(0);
+   }
+   dev->if_txtimer = jiffies;
+ 
+   ret_val = plip_send_packet(pl, (unsigned char *) skb->raw, skb->len);
+   if (ret_val) DPRINTF((6, "%s: lost packet\n", dev->if_name));
+   dev->if_tbusy = 0;
+   mark_bh(NET_BH);
+ #if PLIP_DEV_RETRY
+   return(ret_val);
+ #else
+   return(0);
+ #endif
+ }
+ 
+ 
+ /* Check if a usable parallel port is present. */
+ int
+ plip_check_port(struct plip *pl)
+ {
+   int starttime;
+ 
+   starttime = gettimerctr();
+ 
+   outb(0x00, pl->ioaddr + PAR_DATA);
+   while (inb(pl->ioaddr + PAR_DATA) != 0x00) {
+   	if (time_up(starttime, PROBE_TIMEOUT)) return(-ENODEV);
+   }
+ 
+   outb(0xff, pl->ioaddr + PAR_DATA);
+   while (inb(pl->ioaddr + PAR_DATA) != 0xff) {
+   	if (time_up(starttime, PROBE_TIMEOUT)) return(-ENODEV);
+   }
+ 
+   outb(0x00, pl->ioaddr + PAR_DATA);
+   return(0);
+ }
*** original/net/drv/plip/plip.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/plip.h	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,48 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		Define the PLIP device driver interface and constants.
+  *
+  * Version:	@(#)plip.h	0.8.0	03/14/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #ifndef _PLIP_H
+ #define _PLIP_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #define PLIP_MAX_DEV	3			/* max. # of unit	*/
+ 
+ 
+ /*
+  * If sending a packet failed, have the device layer retry it.
+  * This can give problems as long as the number of retries is
+  * not limited if the other end is not always up and running
+  * This seems a little buggy: I get some duplicate packets.
+  */
+ #define PLIP_DEV_RETRY	0			/* allow retrying pkts	*/
+ 
+ 
+ #define	PLIP_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  PLIP_DEBUG
+ #   define PLIP_DEBUG
+ #endif
+ 
+ #ifdef	PLIP_DEBUG
+ #   define DPRINTF(x)	plip_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ extern int	plip_init(struct ddi *ddi);
+ 
+ 
+ #endif	/* _PLIP_H */
*** original/net/drv/plip/timer.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/timer.h	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,50 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		Timer routines of the PLIP driver.
+  *
+  * Version:	@(#)timer.h	0.8.0	03/14/94
+  *
+  * Authors:	Donald J. Becker, <becker@super.org>
+  *		Tommy Thorn, <tthorn@daimi.aau.dk>
+  *		TANABE Hiroyasu, <hiro@sanpo.t.u-tokyo.ac.jp>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  */
+ #ifndef _TIMEOUT_H
+ #define _TIMEOUT_H
+ 
+ 
+ #define LATCH	((1193180 + HZ/2)/HZ)
+ 
+ 
+ static inline int
+ gettimerctr(void)
+ {
+   long count;
+ 
+   cli();
+   outb_p(0x00, 0x43);	/* latch the count */
+   count = inb_p(0x40);	/* read the latched count */
+   count |= inb_p(0x40) << 8;
+   sti();
+   return(count);
+ }
+ 
+ 
+ /* true if it's been at least timeout microseconds since gettimectr() 
+  * returned start. 
+  * timeout must be less than 10 ms, stay under 9500 for safety!
+  */ 
+ static inline int
+ time_up(int start, int timeout)
+ {
+   int count = gettimerctr();
+ 
+   if ((count -= start) > 0) count -= LATCH;
+   return(count < - timeout * (HZ * LATCH / 1000000));
+ }
+ 
+ 
+ #endif
*** original/net/drv/plip/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/plip/utils.c	Mon May  9 13:13:06 1994
***************
*** 0 ****
--- 1,80 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PLIP protocol.
+  *
+  *		Various functions.
+  *
+  * Version:	@(#)utils.c	0.8.0	03/14/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <stdarg.h>
+ #include "plip.h"
+ #include "global.h"
+ 
+ 
+ static int plip_debug = 0;			/* PLIP debugging flag	*/
+ 
+ 
+ #ifdef PLIP_DEBUG
+ void
+ plip_dprintf(int level, char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (plip_debug < level) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* PLIP_DEBUG */
+ 
+ 
+ int
+ plip_dbg_ioctl(void *arg, int val)
+ {
+   int level;
+ 
+   if (! suser()) return(-EPERM);
+   verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+   level = get_fs_long((int *)arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		plip_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		plip_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			plip_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/drv/ppp/COPYRIGHT	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/COPYRIGHT	Tue May 10 23:06:08 1994
***************
*** 0 ****
--- 1,48 ----
+ 
+ Routines to compress and uncompress tcp packets (for transmission
+ over low speed serial lines).
+ 
+ Copyright (c) 1989 Regents of the University of California.
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by the University of California, Berkeley.  The name of the
+ University may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ 
+ 	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ 	- Initial distribution.
+ 
+ 
+ 	modified for KA9Q Internet Software Package by
+ 	Katie Stevens (dkstevens@ucdavis.edu)
+ 	University of California, Davis
+ 	Computing Services
+  		- 01-31-90	initial adaptation (from 1.19)
+  		PPP.05	02-15-90 [ks]
+  		PPP.08	05-02-90 [ks]	use PPP protocol field to signal
+ 					compression
+  		PPP.15	09-90	 [ks]	improve mbuf handling
+  		PPP.16	11-02	 [karn]	substantially rewritten to use
+ 					NOS facilities
+ 
+ 	- Feb 1991	Bill_Simpson@um.cc.umich.edu
+  		variable number of conversation slots
+  		allow zero or one slots
+  		separate routines
+  		status display
+ 
+ 	Chuck Hedrick <hedrick@klinzhai.rutgers.edu>
+  		Adapted for Linux NET-1.
+ 
+ 	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ 		Adapted for Linux NET-2, cleanup and
+ 		integration into the Linux SLIP driver.
+ 
*** original/net/drv/ppp/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/Makefile	Tue May 10 23:06:08 1994
***************
*** 0 ****
--- 1,36 ----
+ #
+ # Makefile for the Linux PPP device driver.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ OBJS	= main.o utils.o fcs.o compress.o ioctl.o tty.o packet.o
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ ppp.o:		$(OBJS)
+ 		ld -r -o ppp.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/ppp/README	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/README	Tue May 10 23:06:08 1994
***************
*** 0 ****
--- 1,29 ----
+ /*
+    PPP for Linux
+ */
+ 
+ /*
+    Sources:
+ 
+    slip.c
+ 
+    RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of
+    Multi-protocol Datagrams over Point-to-Point Links
+ 
+    RFC1332: IPCP
+ 
+    ppp-2.0
+ 
+    Flags for this module (any combination is acceptable for testing.):
+ 
+    NET02D	      -	Define if using Net-2-Debugged in kernels earler
+    			than v1.1.4.
+ 
+    NEW_TTY_DRIVERS    -	Define if using new Ted Ts'o's alpha TTY drivers
+    			from tsx-11.mit.edu. From Ted Ts'o.
+ 
+    OPTIMIZE_FLAG_TIME -	Number of jiffies to force sending of leading flag
+ 			character. This is normally set to ((HZ * 3) / 2).
+ 			This is 1.5 seconds. If not defined then the leading
+ 			flag is always sent.  
+ */
*** original/net/drv/ppp/VERSION	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/VERSION	Thu May 12 13:31:29 1994
***************
*** 0 ****
--- 1 ----
+ This driver is updated up to PPP-0.2.5.
*** original/net/drv/ppp/compress.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/compress.c	Thu May 12 13:31:29 1994
***************
*** 0 ****
--- 1,811 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Van Jacobsen Header Compression functions.
+  *
+  * Version:	@(#)compress.c	1.7.1	04/25/94
+  *
+  * Authors:	Van Jacobson, <van@helios.ee.lbl.gov>, Dec 31, 1989
+  *		See the `COPYRIGHT' file in this directory for the
+  *		legal status and version info of this code.
+  *
+  *		Katie Stevens, <dkstevens@ucdavis.edu>, 01-31-90
+  *		Phil Karn, <karn@ka9q.ampr.org>, 11-02-90
+  *		Bill Simpson, <Bill_Simpson@um.cc.umich.edu>, 02-91
+  *		Chuck Hedrick, <hedrick@klinzhai.rutgers.edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Jim Seagrave, <jes@grendel.demon.co.uk>
+  *		Steve Bour, <jsbour@ualr.edu>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in_systm.h>
+ #include <linux/in.h>		/* for IPPROTO_TCP, ntohs() et al */
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/ip.h>
+ #include <linux/tty.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "ppp.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ /* Put a short in host order into a char array in network order. */
+ static unsigned char *
+ put16(unsigned char *cp, unsigned short int x)
+ {
+   *cp++ = (x >> 8);
+   *cp++ = x;
+ 
+   return(cp);
+ }
+ 
+ 
+ /* Pull a 16-bit integer in host order from buffer in network byte order. */
+ static short int
+ pull16(unsigned char **cpp)
+ {
+   short int rval;
+ 
+   rval = *(*cpp)++;
+   rval <<= 8;
+   rval |= *(*cpp)++;
+   return(rval);
+ }
+ 
+ 
+ /* Encode a number. */
+ static unsigned char *
+ encode(unsigned char *cp, unsigned int n)
+ {
+   if (n >= 256 || n == 0) {
+ 	*cp++ = 0;
+ 	cp = put16(cp, n);
+   } else {
+ 	*cp++ = n;
+   }
+   return(cp);
+ }
+ 
+ 
+ /* Decode a number. */
+ static long int
+ decode(unsigned char **cpp)
+ {
+   long int x;
+ 
+   x = (long int) *(*cpp)++;
+   if (x == 0) {
+ 	return(pull16(cpp) & 0xFFFF);	/* pull16 returns -1 on error */
+   } else {
+ 	return(x & 0xFF);		/* -1 if PULLCHAR returned error */
+   }
+ }
+ 
+ 
+ /* Dump a VJ header. */
+ #ifdef PPP_DEBUG
+ void
+ pphc_print(unsigned char *cp, int len)
+ {
+   unsigned char *ptr;
+   short int csum;
+ 
+   ptr = (cp + 2);
+   csum = pull16(&ptr);
+   printk("PPP/VJ: type=0x%02X changes=0x%02X sum=0x%X len=%d\n",
+ 		(*cp & 0377), (*(cp + 1) & 0377), (int) csum, len);
+ }
+ #endif	/* PPP_DEBUG */
+ 
+ 
+ /*
+  * This routine does all the checksum computations that don't
+  * require anything special (like copying or special headers.)
+  */
+ static unsigned short
+ pphc_ip_compute_csum(unsigned char *buff, int len)
+ {
+   unsigned long sum = 0;
+ 
+   /* Do the first multiple of 4 bytes and convert to 16 bits. */
+   if (len > 3) {
+ 	__asm__("clc\n"
+ 	        "1:\t"
+ 	    	"lodsl\n\t"
+ 	    	"adcl %%eax, %%ebx\n\t"
+ 	    	"loop 1b\n\t"
+ 	    	"adcl $0, %%ebx\n\t"
+ 	    	"movl %%ebx, %%eax\n\t"
+ 	    	"shrl $16, %%eax\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum) , "=S" (buff)
+ 	        : "0" (sum), "c" (len >> 2) ,"1" (buff)
+ 	        : "ax", "cx", "si", "bx" );
+   }
+   if (len & 2) {
+ 	__asm__("lodsw\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum), "=S" (buff)
+ 	        : "0" (sum), "1" (buff)
+ 	        : "bx", "ax", "si");
+   }
+   if (len & 1) {
+ 	__asm__("lodsb\n\t"
+ 	    	"movb $0, %%ah\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum), "=S" (buff)
+ 	        : "0" (sum), "1" (buff)
+ 	        : "bx", "ax", "si");
+   }
+   sum =~sum;
+   return(sum & 0xFFFF);
+ }
+ 
+ 
+ /* Check the header of an incoming IP datagram. */
+ static int
+ pphc_ip_csum(struct ip *iph)
+ {
+   if (pphc_ip_compute_csum((unsigned char *)iph,
+ 		      (iph->ip_hl * sizeof(unsigned long))) == 0) return(0);
+   return(1);
+ }
+ 
+ 
+ void
+ pphc_i_status(struct compress *comp)
+ {
+   if (comp != NULL) {
+ 	printk("\t%ld Cmp, %ld Uncmp, %ld Bad, %ld Tossed\n",
+ 		comp->pp->stats.rcomp, comp->pp->stats.runcomp,
+ 		comp->pp->stats.rerrors, comp->pp->stats.tossed);
+   }
+ }
+ 
+ 
+ void
+ pphc_o_status(struct compress *comp)
+ {
+   if (comp != NULL) {
+ 	printk("\t%ld Cmp, %ld Uncmp, %ld AsIs, %ld NotTCP\n",
+ 		comp->pp->stats.scomp, comp->pp->stats.suncomp,
+ 		0 /* FIXME: ??? */, comp->pp->stats.sothers);
+ #if 0
+ 	printk("\t%10ld Searches, %10ld Misses\n",
+ 		comp->pp->stats.searches, comp->pp->stats.misses);
+ #endif
+   }
+ }
+ 
+ 
+ int
+ pphc_toss(struct compress *comp)
+ {
+   if (comp == NULL) return(0);
+ 
+   comp->flags |= SLF_TOSS;
+   return(0);
+ }
+ 
+ 
+ /* Free a compression data structure. */
+ void
+ pphc_free(struct compress *comp)
+ {
+   if (comp == NULL) return;
+ 
+   if (comp->rstate != NULL) kfree(comp->rstate);
+ 
+   if (comp->tstate != NULL) kfree(comp->tstate);
+ 
+   kfree_s(comp, sizeof(struct compress));
+ }
+ 
+ 
+ /*
+  * Initialize compression data structure
+  * 'slots' must be in range 0 to 255 (zero meaning no compression)
+  */
+ struct compress *
+ pphc_init(struct ppp *pp, int rslots, int tslots)
+ {
+   struct compress *comp;
+   struct cstate *ts;
+   short i;
+ 
+   comp = (struct compress *) kmalloc(sizeof(struct compress), GFP_KERNEL);
+   if (comp == NULL) return(NULL);
+ 
+   memset(comp, 0, sizeof(struct compress));
+   if (rslots > 0 && rslots < 256) {
+ 	comp->rstate = (struct cstate *)
+ 			kmalloc(rslots * sizeof(struct cstate), GFP_KERNEL);
+ 	if (comp->rstate == NULL) return(NULL);
+ 	memset(comp->rstate, 0, rslots * sizeof(struct cstate));
+ 	comp->rslot_limit = rslots - 1;
+   }
+ 
+   if (tslots > 0 && tslots < 256) {
+ 	comp->tstate = (struct cstate *)
+ 			kmalloc(tslots * sizeof(struct cstate), GFP_KERNEL);
+ 	if (comp->tstate == NULL) return(NULL);
+ 	memset(comp->tstate, 0, rslots * sizeof(struct cstate));
+ 	comp->tslot_limit = tslots - 1;
+   }
+   comp->xmit_oldest = 0;
+   comp->xmit_current = 255;
+   comp->recv_current = 255;
+   comp->pp = pp;
+ 
+   /*
+    * don't accept any packets with implicit index until we get
+    * one with an explicit index.  Otherwise the uncompress code
+    * will try to use connection 255, which is almost certainly
+    * out of range.
+    */
+   comp->flags |= SLF_TOSS;
+ 
+   if (tslots > 0) {
+ 	ts = comp->tstate;
+ 	for(i = comp->tslot_limit; i > 0; --i) {
+ 		ts[i].connid = i;
+ 		ts[i].next = &(ts[i - 1]);
+ 	}
+ 	ts[0].next = &(ts[comp->tslot_limit]);
+ 	ts[0].connid = 0;
+   }
+   return(comp);
+ }
+ 
+ 
+ /* 
+  * icp and isize are the original packet.
+  * ocp is a place to put a copy if necessary.
+  * cpp is initially a pointer to icp.  If the copy is used,
+  *    change it to ocp.
+  */
+ int
+ pphc_compress(struct compress *comp, unsigned char *icp, int isize,
+ 	      unsigned char *ocp, unsigned char **cpp, int compress_cid)
+ 
+ {
+   unsigned char new_seq[16];
+   struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
+   struct cstate *lcs = ocs;
+   struct cstate *cs = lcs->next;
+   unsigned char *cp = new_seq;
+   struct tcphdr *th, *oth;
+   struct ip *ip;
+   unsigned long deltaS, deltaA;
+   short changes = 0;
+   int hlen;
+ 
+   ip = (struct ip *) icp;
+ 
+   /* Bail if this packet isn't TCP, or is an IP fragment */
+   if (ip->ip_p != IPPROTO_TCP ||
+       (ntohs(ip->ip_off) & IP_OFFSET) || (ip->ip_off & 32)) {
+ 	DPRINTF(("comp: noncomp 1 %d %d %d\n", ip->ip_p, 
+ 					ntohs(ip->ip_off), ip->ip_off));
+ #if 0
+ 	/* Send as regular IP */
+ 	if (ip->ip_p != IPPROTO_TCP) comp->pp->stats.sothers++;
+ 	  else comp->pp->stats.tcp++;
+ #endif
+ 	return(isize);
+   }
+ 
+   /* Extract TCP header */
+   th = (struct tcphdr *)
+ 	(((unsigned char *)ip) + (ip->ip_hl * sizeof(unsigned long)));
+   hlen = (ip->ip_hl * sizeof(unsigned long)) +
+ 	 (th->doff * sizeof(unsigned long));
+ 
+   /*
+    * Bail if the TCP packet isn't `compressible' (i.e.,
+    * ACK isn't set or some other control bit is set).
+    */
+   if (th->syn || th->fin || th->rst || !(th->ack)) {
+ 	DPRINTF(("comp: noncomp 2 %x %x %d %d %d %d\n", ip, th, 
+ 					th->syn, th->fin, th->rst, th->ack));
+ 	/* TCP connection stuff; send as regular IP */
+ #if 0
+ 	comp->pp->stats.tcp++;
+ #endif
+ 	return(isize);
+   }
+ 
+   /*
+    * Packet is compressible -- we're going to send either a COMPRESSED_TCP
+    * or UNCOMPRESSED_TCP packet.  Either way, we need to locate (or create)
+    * the connection state.
+    *
+    * States are kept in a circularly linked list with xmit_oldest pointing
+    * to the end of the list.  The list is kept in lru order by moving a
+    * state to the head of the list whenever it is referenced.  Since the
+    * list is short and, empirically, the connection we want is almost al-
+    * ways near the front, we locate states via linear search.  If we don't
+    * find a state for the datagram, the oldest state is (re-)used.
+    */
+   for ( ; ; ) {
+ 	if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr &&
+ 	    ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr &&
+ 	    th->source == cs->cs_tcp.source &&
+ 	    th->dest == cs->cs_tcp.dest) goto found;
+ 
+ 	/* If current equal oldest, at end of list. */
+ 	if (cs == ocs) break;
+ 	lcs = cs;
+ 	cs = cs->next;
+ #if 0
+ 	comp->pp->stats.searches++;
+ #endif
+   };
+ 
+   /*
+    * Didn't find it -- re-use oldest cstate.  Send an
+    * uncompressed packet that tells the other side what
+    * connection number we're using for this conversation.
+    *
+    * Note that since the state list is circular, the oldest
+    * state points to the newest and we only need to set
+    * xmit_oldest to update the lru linkage.
+    */
+ #if 0
+   comp->pp->stats.misses++;
+ #endif
+   comp->xmit_oldest = lcs->connid;
+   DPRINTF(("comp: not found\n"));
+   goto uncompressed;
+ 
+ found:
+   /* Found it -- move to the front on the connection list. */
+   if (lcs == ocs) {
+ 	/* found at most recently used */
+   } else if (cs == ocs) {
+ 	/* found at least recently used */
+ 	comp->xmit_oldest = lcs->connid;
+   } else {
+ 	/* more than 2 elements */
+ 	lcs->next = cs->next;
+ 	cs->next = ocs->next;
+ 	ocs->next = cs;
+   }
+ 
+   /*
+    * Make sure that only what we expect to change changed.
+    * Check the following:
+    * IP protocol version, header length & type of service.
+    * The "Don't fragment" bit.
+    * The time-to-live field.
+    * The TCP header length.
+    * IP options, if any.
+    * TCP options, if any.
+    * If any of these things are different between the previous &
+    * current datagram, we send the current datagram `uncompressed'.
+    */
+   oth = &cs->cs_tcp;
+ 
+   if (
+       ip->ip_v != cs->cs_ip.ip_v ||
+       ip->ip_hl != cs->cs_ip.ip_hl ||
+       ip->ip_tos != cs->cs_ip.ip_tos ||
+       (ip->ip_off & 64) != (cs->cs_ip.ip_off & 64) ||
+       ip->ip_ttl != cs->cs_ip.ip_ttl ||
+       th->doff != cs->cs_tcp.doff ||
+       (ip->ip_hl > 5 &&
+        memcmp(ip + 1, cs->cs_ipopt, ((ip->ip_hl) - 5) * sizeof(unsigned long))
+ 									!= 0) ||
+       (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) *
+ 						sizeof(unsigned long) != 0))
+     ) {
+ 	DPRINTF(("comp: incompat\n"));
+ 	goto uncompressed;
+   }
+ 	
+   /*
+    * Figure out which of the changing fields changed.  The
+    * receiver expects changes in the order: urgent, window,
+    * ack, seq (the order minimizes the number of temporaries
+    * needed in this section of code).
+    */
+   if (th->urg) {
+ 	deltaS = ntohs(th->urg_ptr);
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_U;
+   } else if (th->urg_ptr != oth->urg_ptr) {
+ 	/*
+ 	 * Argh! URG not set but urp changed -- a sensible
+ 	 * implementation should never do this but RFC793
+ 	 * doesn't prohibit the change so we have to deal
+ 	 * with it.
+ 	 */
+ 	DPRINTF(("comp: urg incompat\n"));
+ 	goto uncompressed;
+   }
+   if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) {
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_W;
+   }
+   if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) {
+ 	if (deltaA > 0x0000FFFF) goto uncompressed;
+ 	cp = encode(cp, deltaA);
+ 	changes |= NEW_A;
+   }
+   if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) {
+ 	if (deltaS > 0x0000FFFF) goto uncompressed;
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_S;
+   }
+ 	
+   switch(changes) {
+ 	case 0:
+ 		/*
+ 		 * Nothing changed. If this packet contains data and the
+ 		 * last one didn't, this is probably a data packet following
+ 		 * an ack (normal on an interactive connection) and we send
+ 		 * it compressed.  Otherwise it's probably a retransmit,
+ 		 * retransmitted ack or window probe.  Send it uncompressed
+ 		 * in case the other side missed the compressed version.
+ 		 */
+ 		if (ip->ip_len != cs->cs_ip.ip_len &&
+ 		    ntohs(cs->cs_ip.ip_len) == hlen) break;
+ 		DPRINTF(("comp: retrans\n"));
+ 		goto uncompressed;
+ 		break;
+ 
+ 	case SPECIAL_I:
+ 	case SPECIAL_D:
+ 		/*
+ 		 * Actual changes match one of our special case
+ 		 * encodings -- send packet uncompressed.
+  		 */
+ 		DPRINTF(("comp: special\n"));
+ 		goto uncompressed;
+ 
+ 	case NEW_S|NEW_A:
+ 		if (deltaS == deltaA &&
+ 		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ 			/* Special case for echoed terminal traffic. */
+ 			changes = SPECIAL_I;
+ 			cp = new_seq;
+ 		}
+ 		break;
+ 
+ 	case NEW_S:
+ 		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ 			/* Special case for data xfer. */
+ 			changes = SPECIAL_D;
+ 			cp = new_seq;
+ 		}
+ 		break;
+   }
+ 
+   deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
+   if (deltaS != 1) {
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_I;
+   }
+   if (th->psh) changes |= TCP_PUSH_BIT;
+ 
+   /*
+    * Grab the cksum before we overwrite it below.  Then
+    * update our state with this packet's header.
+    */
+   deltaA = ntohs(th->check);
+   memcpy(&cs->cs_ip, ip, sizeof(struct ip));		/* FIXME: 20 */
+   memcpy(&cs->cs_tcp, th, sizeof(struct tcphdr));	/* FIXME: 20 */
+ 
+   /*
+    * We want to use the original packet as our compressed packet.
+    *
+    * (cp - new_seq) is the number of bytes we need for compressed
+    * sequence numbers.  In addition we need one byte for the change
+    * mask, one for the connection id and two for the tcp checksum.
+    * So, (cp - new_seq) + 4 bytes of header are needed.
+    */
+   deltaS = cp - new_seq;
+   if (compress_cid == 0 || comp->xmit_current != cs->connid) {
+ 	cp = ocp;
+ 	*cpp = ocp;
+ 	*cp++ = changes | NEW_C;
+ 	*cp++ = cs->connid;
+ 	comp->xmit_current = cs->connid;
+   } else {
+ 	cp = ocp;
+ 	*cpp = ocp;
+ 	*cp++ = changes;
+   }
+   cp = put16(cp, (short)deltaA);	/* Write TCP checksum */
+ 
+   /* deltaS is now the size of the change section of the compressed header. */
+   DPRINTF(("comp: %x %x %x %d %d\n", icp, cp, new_seq, hlen, deltaS));
+   memcpy(cp, new_seq, deltaS);	/* Write list of deltas */
+   memcpy(cp + deltaS, icp + hlen, isize - hlen);
+   comp->pp->stats.scomp++;
+   ocp[0] |= PPP_TYPE_COMP_TCP;
+   return(isize - hlen + deltaS + (cp - ocp));
+ 
+ uncompressed:
+   /*
+    * Update connection state cs & send uncompressed packet (i.e.,
+    * a regular ip/tcp packet but with the 'conversation id' we hope
+    * to use on future compressed packets in the protocol field).
+    */
+   memcpy(&cs->cs_ip, ip, sizeof(struct ip));		/* FIXME: 20 */
+   memcpy(&cs->cs_tcp, th, sizeof(struct tcphdr));	/* FIXME: 20 */
+   if (ip->ip_hl > 5)
+ 	memcpy(cs->cs_ipopt, ip+1, ((ip->ip_hl) - 5) * sizeof(unsigned long));
+   if (th->doff > 5)
+ 	memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * sizeof(unsigned long));
+   comp->xmit_current = cs->connid;
+   comp->pp->stats.suncomp++;
+   memcpy(ocp, icp, isize);
+   *cpp = ocp;
+   ocp[9] = cs->connid;
+   ocp[0] |= PPP_TYPE_UNCOMP_TCP;
+   return(isize);
+ }
+ 
+ 
+ int
+ pphc_uncompress(struct compress *comp, unsigned char *icp, int isize)
+ {
+   struct tcphdr *thp;
+   struct ip *ip;
+   struct cstate *cs;
+   unsigned char *cp = icp;
+   int changes;
+   int len, hdrlen;
+   long x;
+ 
+   /* We've got a compressed packet; read the change byte. */
+   comp->pp->stats.rcomp++;
+   if (isize < 3) {
+ 	comp->pp->stats.rerrors++;
+ 	DPRINTF(("uncomp: runt\n"));
+ 	return(0);
+   }
+   changes = *cp++;
+   if (changes & NEW_C) {
+ 	/*
+ 	 * Make sure the state index is in range, then grab the state.
+ 	 * If we have a good state index, clear the 'discard' flag.
+ 	 */
+ 	x = *cp++;	/* Read conn index */
+ 	if (x < 0 || x > comp->rslot_limit) goto bad;
+ 
+ 	comp->flags &=~ SLF_TOSS;
+ 	comp->recv_current = x;
+   } else {
+ 	/*
+ 	 * This packet has an implicit state index.  If
+ 	 * we've had a line error since the last time we
+ 	 * got an explicit state index, we have to toss
+ 	 * the packet.
+ 	 */
+ 	if (comp->flags & SLF_TOSS) {
+ 		comp->pp->stats.tossed++;
+ 		DPRINTF(("uncomp: toss\n"));
+ 		return(0);
+ 	}
+   }
+   cs = &comp->rstate[comp->recv_current];
+   thp = &cs->cs_tcp;
+   ip = &cs->cs_ip;
+ 
+   if ((x = pull16(&cp)) == -1) {	/* Read the TCP checksum */
+ 	DPRINTF(("uncomp: bad tcp chk\n"));
+ 	goto bad;
+   }
+   thp->check = htons(x);
+   thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
+ 
+   /* 
+    * we can use the same number for the length of the saved header and
+    * the current one, because the packet wouldn't have been sent
+    * as compressed unless the options were the same as the previous one
+    */
+   hdrlen = (ip->ip_hl * sizeof(unsigned long)) +
+ 	   (thp->doff * sizeof(unsigned long));
+   switch(changes & SPECIALS_MASK) {
+ 	case SPECIAL_I:		/* Echoed terminal traffic */
+ 		{
+ 			short i;
+ 
+ 			i = ntohs(ip->ip_len) - hdrlen;
+ 			thp->ack_seq = htonl(ntohl(thp->ack_seq) + i);
+ 			thp->seq = htonl(ntohl(thp->seq) + i);
+ 		}
+ 		break;
+ 
+ 	case SPECIAL_D:		/* Unidirectional data */
+ 		thp->seq = htonl(ntohl(thp->seq) +
+ 				 ntohs(ip->ip_len) - hdrlen);
+ 		break;
+ 
+ 	default:
+ 		if (changes & NEW_U) {
+ 			thp->urg = 1;
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad U\n"));
+ 				goto bad;
+ 			}
+ 			thp->urg_ptr = htons(x);
+ 		} else {
+ 			thp->urg = 0;
+ 		}
+ 		if (changes & NEW_W) {
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad W\n"));
+ 				goto bad;
+ 			}	
+ 			thp->window = htons(ntohs(thp->window) + x);
+ 		}
+ 		if (changes & NEW_A) {
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad A\n"));
+ 				goto bad;
+ 			}
+ 			thp->ack_seq = htonl(ntohl(thp->ack_seq) + x);
+ 		}
+ 		if (changes & NEW_S) {
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad S\n"));
+ 				goto bad;
+ 			}
+ 			thp->seq = htonl(ntohl(thp->seq) + x);
+ 		}
+ 		break;
+   }
+ 
+   if (changes & NEW_I) {
+ 	if ((x = decode(&cp)) == -1) {
+ 		DPRINTF(("uncomp: bad I\n"));
+ 		goto bad;
+ 	}
+ 	ip->ip_id = htons(ntohs(ip->ip_id) + x);
+   } else {
+ 	ip->ip_id = htons(ntohs(ip->ip_id) + 1);
+   }
+ 
+   /*
+    * At this point, cp points to the first byte of data in the
+    * packet.  Put the reconstructed TCP and IP headers back on the
+    * packet.  Recalculate IP checksum (but not TCP checksum).
+    */
+   len = isize - (cp - icp);
+   if (len < 0) goto bad;
+   len += hdrlen;
+   ip->ip_len = htons(len);
+   ip->ip_sum = 0;
+   DPRINTF(("uncomp: %d %d %d %d\n", cp - icp, hdrlen, isize, len));
+ 
+   /*
+    * Note from ChuckH to FredK: "this routine'd better behave well
+    * when confronted with overlapping copies!" -FvK
+    */
+   memmove(icp + hdrlen, cp, len - hdrlen);
+ 
+   /* Copy in the IP header (and options). */
+   cp = icp;
+   memcpy(cp, ip, sizeof(struct ip));		/* FIXME: 20 */
+   cp += sizeof(struct ip);			/* FIXME: 20 */
+   if (ip->ip_hl > 5) {
+ 	memcpy(cp, cs->cs_ipopt, ((ip->ip_hl) - 5) * sizeof(unsigned long));
+ 	cp += ((ip->ip_hl) - 5) * sizeof(unsigned long);
+   }
+ 
+ #if 1	/* FIXME: Check This Soon */
+   ip = (struct ip *) icp;
+   ip->ip_sum = pphc_ip_compute_csum((unsigned char *)ip,
+ 		      (ip->ip_hl * sizeof(unsigned long)));
+ #else
+   ((struct ip *)icp)->ip_sum = pphc_ip_csum((struct ip *)icp);
+ #endif
+ 
+   /* Copy in the TCP header (and options). */
+   memcpy(cp, thp, sizeof(struct tcphdr));	/* FIXME: 20 */
+   cp += sizeof(struct tcphdr);
+   if (thp->doff > 5) {
+ 	memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * sizeof(unsigned long));
+ 	cp += ((thp->doff) - 5) * sizeof(unsigned long);
+   }
+ 
+   DPRINTF(("\runcomp: change %x len %d\n", changes, len));
+   return(len);
+ 
+ bad:
+   comp->pp->stats.rerrors++;
+   return(pphc_toss(comp));
+ }
+ 
+ 
+ int
+ pphc_remember(struct compress *comp, unsigned char *icp, int isize)
+ {
+   struct ip *ip;
+   struct tcphdr *thp;
+   struct cstate *cs;
+   unsigned char index;
+   short ip_len;
+ 
+   if (isize < sizeof(struct ip)) {
+ 	/* The packet is shorter than a legal IP header. */
+ 	comp->pp->stats.runts++;
+ 	return(pphc_toss(comp));
+   }
+ 
+   /* Clear the IP header "version" bit field. */
+   icp[0] &= 0x4F;
+ 
+   /* Sneak a peek at the IP header's IHL field to find its length. */
+   ip_len = (icp[0] & 0x0F) << 2;
+   if (ip_len < sizeof(struct ip)) {
+ 	/* The IP header length field is too small. */
+ 	comp->pp->stats.runts++;
+ 	return(pphc_toss(comp));
+   }
+   index = icp[9];
+   icp[9] = IPPROTO_TCP;
+   ip = (struct ip *) icp;
+ 
+   if (pphc_ip_csum(ip)) {
+ 	/* Bad IP header checksum; discard. */
+ 	comp->pp->stats.rerrors++;
+ 	return(pphc_toss(comp));
+   }
+ 
+   thp = (struct tcphdr *)
+ 	(((unsigned char *)ip) + (ip->ip_hl * sizeof(unsigned long)));
+   if (index > comp->rslot_limit) {
+ 	comp->pp->stats.rerrors++;
+ 	return(pphc_toss(comp));
+   }
+ 
+   /* Update local state. */
+   cs = &comp->rstate[comp->recv_current = index];
+   comp->flags &=~ SLF_TOSS;
+ 
+   /* Save IP header (plus options). */
+   memcpy(&cs->cs_ip, ip, sizeof(struct ip));		/* FIXME: 20 */
+   if (ip->ip_hl > 5)
+ 	memcpy(cs->cs_ipopt, ip+1, ((ip->ip_hl) - 5) * sizeof(unsigned long));
+ 
+   /* Save TCP header (plus options). */
+   memcpy(&cs->cs_tcp, thp, sizeof(struct tcphdr));	/* FIXME: 20 */
+   if (thp->doff > 5)
+ 	memcpy(cs->cs_tcpopt, thp+1, ((thp->doff) - 5) * sizeof(unsigned long));
+ 
+   cs->cs_hsize = (ip->ip_hl * 2) + (thp->doff * 2);
+ 
+   /*
+    * Put headers back on packet.
+    * Neither header checksum is recalculated.
+    */
+   comp->pp->stats.runcomp++;
+ 
+   return(isize);
+ }
*** original/net/drv/ppp/compress.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/compress.h	Thu May 12 13:31:29 1994
***************
*** 0 ****
--- 1,106 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Van Jacobsen Header Compression definitions.
+  *
+  * Version:	@(#)compress.h	1.7.0	04/02/94
+  *
+  * Authors:	Van Jacobson, <van@helios.ee.lbl.gov>, Dec 31, 1989
+  *		See the `COPYRIGHT' file in this directory for the
+  *		legal status and version info of this code.
+  *
+  *		Katie Stevens, <dkstevens@ucdavis.edu>, 01-31-90
+  *		Phil Karn, <karn@ka9q.ampr.org>, 11-02-90
+  *		Bill Simpson, <Bill_Simpson@um.cc.umich.edu>, 02-91
+  *		Chuck Hedrick, <hedrick@klinzhai.rutgers.edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Jim Seagrave, <jes@grendel.demon.co.uk>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <linux/ip.h>
+ #include <linux/ip_tcp.h>
+ 
+ 
+ /* PPP compression masks for len/vers byte. */
+ #define PPP_TYPE_ERROR		0x00
+ #define PPP_TYPE_IP		0x40
+ #define PPP_TYPE_UNCOMP_TCP	0x70
+ #define PPP_TYPE_COMP_TCP	0x80
+ 
+ /* Bits in first octet of compressed packet. */
+ #define NEW_C		0x40			/* flag bits for what	*/
+ #define NEW_I		0x20			/* changed in a packet	*/
+ #define NEW_S		0x08
+ #define NEW_A		0x04
+ #define NEW_W		0x02
+ #define NEW_U		0x01
+ 
+ /* Reserved, special-case values of above. */
+ #define SPECIAL_I	(NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+ #define SPECIAL_D	(NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+ #define SPECIALS_MASK	(NEW_S|NEW_A|NEW_W|NEW_U)
+ 
+ #define TCP_PUSH_BIT	0x10
+ 
+ 
+ #define SLF_TOSS	0x01			/* keep tossing until id rcv */
+ 
+ 
+ /* All the state data for one serial line (we need one of these per line). */
+ struct compress {
+   struct cstate	*tstate;		/* transmit connection states	*/
+   struct cstate *rstate;		/* receive connection states	*/
+ 
+   unsigned char tslot_limit;		/* highest xmit slot id (0-l)	*/
+   unsigned char rslot_limit;		/* highest recv slot id (0-l)	*/
+ 
+   unsigned char	xmit_oldest;		/* oldest xmit in ring		*/
+   unsigned char xmit_current;		/* most recent xmit id		*/
+   unsigned char recv_current;		/* most recent rcvd id		*/
+ 
+   unsigned char	flags;
+ 
+   struct ppp	*pp;			/* back-pointer to PPP cb	*/
+ };
+ 
+ 
+ /*
+  * "state" data for each active tcp conversation on the wire.  This is
+  * basically a copy of the entire IP/TCP header from the last packet
+  * we saw from the conversation together with a small identifier
+  * the transmit & receive ends of the line use to locate saved header.
+  */
+ struct cstate {
+   unsigned char	connid;		/* connection id number (xmit)		*/
+   struct cstate	*next;		/* next in ring (xmit)			*/
+   struct ip	cs_ip;		/* IP hdr from most recent packet	*/
+   struct tcphdr	cs_tcp;		/* TCP hdr from most recent packet	*/
+   unsigned char	cs_ipopt[64];
+   unsigned char	cs_tcpopt[64];
+   int		cs_hsize;
+ };
+ 
+ 
+ extern struct compress	*pphc_init(struct ppp *pp, int rslots, int tslots);
+ extern void		pphc_free(struct compress *comp);
+ extern int		pphc_compress(struct compress *comp,
+ 				      unsigned char *icp, int isize,
+ 				      unsigned char *ocp,
+ 				      unsigned char **cpp,
+ 				      int compress_cid);
+ extern int		pphc_uncompress(struct compress *comp,
+ 					unsigned char *icp, int isize);
+ extern int		pphc_remember(struct compress *comp,
+ 				      unsigned char *icp, int isize);
+ extern int		pphc_toss(struct compress *comp);
+ extern void		pphc_print(unsigned char *ptr, int len);
+ extern void		pphc_i_status(struct compress *comp);
+ extern void		pphc_o_status(struct compress *comp);
*** original/net/drv/ppp/fcs.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/fcs.c	Thu May 12 13:31:29 1994
***************
*** 0 ****
--- 1,114 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		FCS (Frame Check Sequence) support.
+  *
+  * Version:	@(#)fcs.h	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/interrupt.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/tty.h>
+ #include <linux/signal.h>
+ #include <linux/ddi.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/ip.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/segment.h>
+ #include "ppp.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ unsigned short ppp_fcstab[256] = {
+   0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+   0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+   0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+   0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+   0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+   0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+   0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+   0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+   0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+   0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+   0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+   0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+   0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+   0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+   0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+   0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+   0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+   0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+   0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+   0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+   0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+   0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+   0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+   0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+   0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+   0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+   0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+   0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+   0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+   0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+   0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+   0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+ };
+ 
+ 
+ void
+ ppp_add_fcs(struct ppp *pp)
+ {
+   unsigned short fcs = pp->fcs;
+ 
+   fcs ^= 0xffff;
+   pp_stuff_char(pp, fcs & 0x00ff);
+   pp_stuff_char(pp, (fcs & 0xff00) >> 8);
+   ASSERT(pp->fcs == PPP_FCS_GOOD);
+   DPRINTF(("PPP: add_fcs: fcs is %lX\n", (long) (unsigned long) fcs));
+ }
+ 
+ 
+ int
+ ppp_check_fcs(struct ppp *pp)
+ {
+   unsigned short fcs = PPP_FCS_INIT, msgfcs;
+   unsigned char *c = pp->rbuff;
+   int i;
+ 
+   for (i = 0; i < pp->rcount - 2; i++, c++)
+ 	fcs = (fcs >> 8) ^ ppp_fcstab[(fcs ^ *c) & 0xff];
+ 
+   fcs ^= 0xffff;
+   msgfcs = (c[1] << 8) + c[0];
+   DPRINTF(("PPP: check_fcs: got %lX want %lX\n",
+ 	(long) (unsigned long) msgfcs, (long) (unsigned long) fcs));
+   return(fcs == msgfcs);
+ }
*** original/net/drv/ppp/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/global.h	Fri May 13 17:07:36 1994
***************
*** 0 ****
--- 1,195 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Define the PPP device driver internal functions.
+  *
+  * Version:	@(#)global.h	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		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.
+  */
+ #ifndef _PPP_GLOBAL_H
+ #define _PPP_GLOBAL_H
+ 
+ 
+ #define OPTIMIZE_FLAG_TIME	((HZ * 3)/2)
+ 
+ 
+ #define ASSERT(p)		if (!p) DPRINTF(("assertion failed: " #p))
+ 
+ 
+ 
+ #define CHECK_PPP(a)		if (! pp->inuse) { \
+ 					printk("PPP: *WARNING* using inactive interface in file %s, line %d !\n", \
+ 						__FILE__, __LINE__); \
+ 					return(a); \
+ 				}
+ #define CHECK_PPP_VOID()	if (! pp->inuse) { \
+ 					printk("PPP: *WARNING* using inactive interface in file %s, line %d !\n", \
+ 						__FILE__, __LINE__); \
+ 					return; \
+ 				}
+ 
+ #define PUTC(c, label)		*pp->us_rbuff_head++ = c; \
+ 				if (pp->us_rbuff_head == pp->us_rbuff_end) \
+ 					pp->us_rbuff_head = pp->us_rbuff; \
+ 				if (pp->us_rbuff_head == pp->us_rbuff_tail) \
+ 					goto label;
+ 
+ #define GETC(c)			c = *pp->us_rbuff_tail++; \
+ 				if (pp->us_rbuff_tail == pp->us_rbuff_end) \
+ 					pp->us_rbuff_tail = pp->us_rbuff;
+ 
+ #define in_xmap(pp, c)		(pp->xmit_async_map[(c) >> 5] & \
+ 							(1 << ((c) & 0x1f)))
+ #define in_rmap(pp, c)		((((unsigned int) (unsigned char) (c)) < 0x20) \
+ 					&& pp->recv_async_map & (1 << (c)))
+ 
+ #define bset(p, b)		((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
+ 
+ 
+ struct ppp {
+   int			magic;		/* magic value for structure	*/
+   unsigned int		flags;		/* miscellany			*/
+   char			inuse;		/* are we allocated?		*/
+   char			sending;	/* "channel busy" indicator	*/
+   char			escape;		/* 0x20 if prev char was PPP_ESC*/
+   char			toss;		/* toss this frame		*/
+ 
+   unsigned long		xmit_async_map[8];	/* 1 bit means that given
+ 					 * control character is quoted
+ 					 * on output			*/
+   unsigned long		recv_async_map;		/* 1 bit means that given
+ 					 * control character is ignored
+ 					 * on input			*/
+   int                   mtu;            /* maximum xmit frame size      */
+   int                   mru;            /* maximum receive frame size   */
+   unsigned short	fcs;		/* FCS field of current frame	*/
+ 
+   unsigned long		mem_start,	/* memory pointers		*/
+ 			mem_end,
+ 			rmem_start,
+ 			rmem_end;
+ 
+   /* Various fields. */
+   int			line;		/* PPP channel number		*/
+   struct tty_struct	*tty;		/* ptr to TTY structure		*/
+   struct ifnet		*dev;		/* easy for intr handling	*/
+   unsigned long		last_xmit;	/* time of last transmission	*/
+ 
+   /* VJ Header Compression ("CSLIP") control block. */
+   struct compress	*comp;
+ 
+   /* These are pointers to the malloc()ed frame buffers.
+      These buffers are used while processing a packet.	If a packet
+      has to hang around for the user process to read it, it lingers in
+      the user buffers below. */
+   unsigned char		*rbuff;		/* receiver buffer		*/
+   unsigned char		*xbuff;		/* transmitter buffer		*/
+   unsigned char		*cbuff;		/* compression buffer		*/
+ 
+   /* These are the various pointers into the buffers. */
+   unsigned char		*rhead;		/* RECV buffer pointer (head)	*/
+   unsigned char		*rend;		/* RECV buffer pointer (end)	*/
+   int			rcount;		/* PPP receive counter		*/
+   unsigned char		*xhead;		/* XMIT buffer pointer (head)	*/
+   unsigned char		*xtail;		/* XMIT buffer pointer (end)	*/
+ 
+   /* Structures for interfacing with the user process. */
+ #define RBUFSIZE	4000
+   unsigned char		*us_rbuff;	/* circular incoming packet buf.*/
+   unsigned char		*us_rbuff_end;	/* end of allocated space	*/
+   unsigned char		*us_rbuff_head; /* head of waiting packets	*/
+   unsigned char		*us_rbuff_tail; /* tail of waiting packets	*/
+   unsigned char		us_rbuff_lock;	/* lock: bit 0 head bit 1 tail	*/
+   int			inp_sig;	/* input ready signal for pgrp	*/
+   int			inp_sig_pid;	/* process to get notified	*/
+ 
+   /* items to support the select() function */
+   struct wait_queue	*write_wait;	/* queue for reading processes	*/
+   struct wait_queue	*read_wait;	/* queue for writing processes	*/
+ 
+   /* PPP interface statistics. */
+   struct ppp_stats	stats;		/* statistic information	*/
+ 
+   /* PPP demand dial information. */
+   struct ppp_ddinfo	ddinfo;		/* demand dial information	*/
+ };
+ 
+ 
+ extern int		ppp_debug;	/* PPP debug level flag		*/
+ extern int		ppp_debug_netpackets;
+ #if CHECK_CHARS
+ extern unsigned long	paritytab;
+ #endif
+ extern struct ppp	*ppp_ctrl[];	/* PPP control blocks		*/
+ extern unsigned short	ppp_fcstab[];	/* PPP FCS table		*/
+ 
+ 
+ /* Fcs.c */
+ extern void		ppp_add_fcs(struct ppp *);
+ extern int		ppp_check_fcs(struct ppp *);
+ 
+ /* Main.c */
+ extern void		pp_stuff_char(struct ppp *pp, unsigned char c);
+ extern void		pp_enqueue(struct ppp *pp, unsigned char c);
+ extern int		pp_us_queue(struct ppp *pp, unsigned short proto,
+ 				    unsigned char *buf, int len);
+ extern void		pp_changedmtu(struct ppp *, int new_mtu, int new_mru);
+ extern struct ppp	*pp_find(struct tty_struct *tty);
+ extern int		pp_open(struct ifnet *);
+ extern int		pp_close(struct ifnet *);
+ extern int		pp_lock(struct ppp *);
+ extern void		pp_unlock(struct ppp *);
+ extern void		pp_release(struct ppp *);
+ 
+ /* Tty.c */
+ #if NEW_TTY_DRIVERS
+ extern void		ppp_receive_buf(struct tty_struct *tty,
+ 					unsigned char *cp, char *fp,
+ 					int count);
+ #else
+ extern void		ppp_handler(struct tty_struct *);
+ #endif
+ extern void		ppp_kick_tty(struct ppp *);
+ extern void		ppp_close(struct tty_struct *);
+ extern int		ppp_open(struct tty_struct *);
+ extern int		ppp_ioctl(struct inode *inode, struct file *file,
+ 				   unsigned int cmd, unsigned long arg);
+ extern int		ppp_read(struct tty_struct *tty, struct file *file,
+ 				 unsigned char *buf, unsigned int nr);
+ extern int		ppp_write(struct tty_struct *tty, struct file *file,
+ 				  unsigned char *buf, unsigned int nr);
+ 
+ extern void		ppp_write_wakeup(struct tty_struct *tty);
+ extern int		ppp_select(struct tty_struct *tty, struct inode *inode,
+ 				   struct file *filp, int sel_type,
+ 				   select_table * wait);
+ 
+ /* Packet.c */
+ extern void		ppp_output_done(void *pp);
+ extern void		ppp_doframe(struct ppp *);
+ extern int		ppp_xmit(struct ifnet *dev, struct sk_buff *skb);
+ extern int		ppp_recv(struct ifnet *dev, struct sk_buff *skb,
+ 				  int len);
+ extern void		ppp_send(struct ifnet *dev, struct sk_buff *skb,
+ 				  struct sockaddr *target, int priority);
+ 
+ /* Utils.c */
+ #ifdef PPP_DEBUG
+ extern void		ppp_dprintf(char *fmt, ...);
+ extern int		ppp_dbg_ioctl(unsigned long arg, int level);
+ extern void		ppp_print_buffer(const char *name, char *buf,
+ 					int count, int seg);
+ #endif
+ 
+ #endif	/* _PPP_GLOBAL_H */
*** original/net/drv/ppp/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/ioctl.c	Thu May 12 13:31:29 1994
***************
*** 0 ****
--- 1,336 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		PPP I/O control request handler.
+  *
+  * Version:	@(#)ioctl.h	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/ip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "ppp.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ /* Check if the new MTU setting is OK. */
+ static int
+ ppp_check_mtu(struct ifnet *dev, int new_mtu)
+ {
+   if ((new_mtu < 0) || (new_mtu >= 8000)) return(-EINVAL);
+   dev->if_mtu = new_mtu;
+ 
+   /* The driver will see the new MTU on the next write() ! */
+   return(0);
+ }
+ 
+ 
+ /* Get or set the current PPP statistics. */
+ static int
+ ppp_do_stats(struct ppp *pp, unsigned long arg, unsigned cmd)
+ {
+   int i;
+ 
+   switch(cmd) {
+ 	case SIOCGIFSTATS:
+ 		i = verify_area(VERIFY_WRITE, (char *) arg,
+ 				sizeof(struct ppp_stats));
+ 		if (i < 0) return(i);
+ 		memcpy_tofs((char *) arg, &pp->stats,
+ 			    sizeof(struct ppp_stats));
+ 		return(0);
+ 
+ 	case SIOCSIFSTATS:
+ 		i = verify_area(VERIFY_READ, (char *) arg,
+ 				sizeof(struct ppp_stats));
+ 		if (i < 0) return(i);
+ 		memcpy_fromfs(&pp->stats, (char *) arg,
+ 			    sizeof(struct ppp_stats));
+ 		return(0);
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
+ 
+ 
+ /* Perform I/O control on an active PPP channel. */
+ int
+ ppp_ioctl(struct inode *inode, struct file *file,
+ 	  unsigned int cmd, unsigned long arg)
+ {
+   struct ppp_ddinfo cur_ddinfo;
+   unsigned long temp_tbl[8];
+   unsigned long cur_jiffies;
+   register int i = 0;
+   struct ifnet *dev;
+   struct ppp *pp;
+   int err;
+ 
+   DPRINTF(("PPP: ioctl(0x%lX, 0x%lX, 0x%lX)\n", inode, cmd, arg));
+ 
+ #if NEW_TTY_DRIVER
+   /*
+    * These events must be passed to the real serial driver for them to
+    * operate. Return the magic error condition to allow this to happen.
+    */
+   if (cmd == TIOCGETD ||
+       cmd == TIOCSETD ||
+       cmd == TCGETS   ||  /* allow stty to read, but not set, */
+       cmd == TCGETA)      /* the serial port. */
+ 	return(-ENOIOCTLCMD);
+ #endif
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(ppp_dbg_ioctl(arg, 1));
+ 
+ 	case SIOCSIFMTU:
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= PPP_MAX_DEV) return(-ENODEV);
+ 		if (ppp_ctrl[dev->if_unit] == NULL) return(-ENODEV);
+ 		return(ppp_check_mtu(dev, (int) arg));
+ 
+ 	case SIOCGIFENCAP:		/* "get ENCAP mode"	*/
+ 		return(-EINVAL);
+ 
+ 	case SIOCSIFENCAP:		/* "set ENCAP mode"	*/
+ 		if (! suser()) return(-EPERM);
+ 		return(-EINVAL);
+ 
+ 	case SIOCSIFSTATS:		/* "set stats" */
+ 		if (! suser()) return(-EPERM);
+ 		/*FALLTHROUGH*/
+ 
+ 	case PPPIOCSDEBUG:
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		ppp_debug = (int) get_fs_long(arg);
+ 		ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8;
+ 		ppp_debug &= 0xff;
+ 		DPRINTF(("PPP: ioctl: set debug level %d, netpacket %d\n", 
+ 					ppp_debug, ppp_debug_netpackets));
+ 		return(0);
+ 
+ 	case PPPIOCGDEBUG:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		put_fs_long((long) (ppp_debug | (ppp_debug_netpackets << 8)),
+ 									arg);
+ 		DPRINTF(("PPP: ioctl: get debug level %d\n", 
+ 				ppp_debug | (ppp_debug_netpackets << 8)));
+ 		return(0);
+ 
+ 	case SIOCGIFNAME:
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		err = verify_area(VERIFY_WRITE, (int *) arg,
+ 				(strlen(pp->dev->if_name) + 1));
+ 		if (err < 0) return(err);
+ 		memcpy_tofs((int *) arg, pp->dev->if_name,
+ 			    strlen(pp->dev->if_name) + 1);
+ 		return(0);
+ 
+ 	case PPPIOCSMRU:
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		i = (int) get_fs_long(arg);
+ 		DPRINTF(("PPP: ioctl: set mru to %d\n", i));
+ 		if (pp->mru != i) pp_changedmtu(pp, pp->mtu, i);
+ 		return(0);
+ 
+ 	case SIOCGIFSTATS:		/* "get stats" */
+ 	case PPPIOCGSTAT:
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		return(ppp_do_stats(pp, arg, cmd));
+ 
+ 	case PPPIOCGFLAGS:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		i = (pp->flags & SC_MASK);
+ #if (CHECK_CHARS == 0)
+ 		/* Don't generate errors if we don't check chars. */
+ 		i |= (SC_RCV_B7_1 | SC_RCV_B7_0 | SC_RCV_ODDP | SC_RCV_EVNP);
+ #endif
+ 		put_fs_long((long) i, arg);
+ 		DPRINTF(("PPP: ioctl: get flags: addr %lX flags %lX\n",
+ 				(unsigned long) arg, (unsigned long) i));
+ 		return(0);
+ 
+ 	case PPPIOCSFLAGS:
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		i = (int) get_fs_long(arg);
+ 		pp->flags ^= ((pp->flags ^ i) & SC_MASK);
+ 		DPRINTF(("PPP: ioctl: set flags to %lX\n", (unsigned long) i));
+ 		return(0);
+ 
+ 	case PPPIOCGASYNCMAP:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		put_fs_long(pp->xmit_async_map[0], arg);
+ 		DPRINTF(("PPP: ioctl: get asyncmap: addr %lX asyncmap %lX\n",
+ 						arg, pp->xmit_async_map[0]));
+ 		return(0);
+ 
+ 	case PPPIOCSASYNCMAP:
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		memset(pp->xmit_async_map, 0, sizeof(pp->xmit_async_map));
+ 		pp->xmit_async_map[0] = get_fs_long(arg);
+ 		bset(pp->xmit_async_map, PPP_FLAG);
+ 		bset(pp->xmit_async_map, PPP_ESC);
+ 		DPRINTF(("PPP: ioctl: set asyncmap %lX\n",
+ 					pp->xmit_async_map[0]));
+ 		return(0);
+ 
+ 	case PPPIOCRASYNCMAP:
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		pp->recv_async_map = get_fs_long(arg);
+ 		DPRINTF(("PPP: ioctl: set recv asyncmap %lX\n",
+ 					pp->recv_async_map));
+ 		return(0);
+   
+ 	case PPPIOCGUNIT:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		put_fs_long((long) pp->line, arg);
+ 		DPRINTF(("PPP: ioctl: get unit: %d\n", pp->line));
+ 		return(0);
+ 
+ 	case PPPIOCSINPSIG:
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		pp->inp_sig = (int) get_fs_long(arg);
+ 		pp->inp_sig_pid = current->pid;
+ 		DPRINTF(("PPP: ioctl: set input signal %d\n", pp->inp_sig));
+ 		return(0);
+ 
+ 	case PPPIOCGTIME:
+ 		err = verify_area(VERIFY_WRITE, (void *) arg,
+ 				    sizeof(struct ppp_ddinfo));
+ 		if (err < 0) return(err);
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		cur_jiffies = jiffies;
+ 
+ 		/* Change absolute times to relative times. */
+ 		cur_ddinfo.ip_sjiffies =
+ 			cur_jiffies - pp->ddinfo.ip_sjiffies;
+ 		cur_ddinfo.ip_rjiffies =
+ 			cur_jiffies - pp->ddinfo.ip_rjiffies;
+ 		cur_ddinfo.nip_sjiffies =
+ 			cur_jiffies - pp->ddinfo.nip_sjiffies;
+ 		cur_ddinfo.nip_rjiffies =
+ 			cur_jiffies - pp->ddinfo.nip_rjiffies;
+     
+ 		memcpy_tofs((void *) arg, &cur_ddinfo,
+ 			    sizeof(struct ppp_ddinfo));
+ 		DPRINTF(("PPP: ioctl: read demand dial info\n"));
+ 		return(0);
+ 
+ 	case PPPIOCGXASYNCMAP:
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		err = verify_area(VERIFY_WRITE, (void *) arg,
+ 				  sizeof(pp->xmit_async_map));
+ 		if (err < 0) return(err);
+ 		memcpy_tofs((void *) arg, pp->xmit_async_map,
+ 			    sizeof(pp->xmit_async_map));
+ 		DPRINTF(("PPP: ioctl: get xasyncmap: addr %lX\n", arg));
+ 		return(0);
+ 
+ 	case PPPIOCSXASYNCMAP:
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		err = verify_area(VERIFY_READ, (void *) arg,
+ 				  sizeof(pp->xmit_async_map));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(temp_tbl, (void *) arg,
+ 			      sizeof(pp->xmit_async_map));
+ 		temp_tbl[1] = 0x00000000;	/* must not escape 0x20-0x3f */
+ 		temp_tbl[2] &= ~0x40000000;	/* must not escape 0x5e	*/
+ 		temp_tbl[3] |= 0x60000000;	/* must escape 0x7d and 0x7e */
+ 
+ 		if ((temp_tbl[2] & temp_tbl[3]) != 0 ||
+ 		    (temp_tbl[4] & temp_tbl[5]) != 0 ||
+ 		    (temp_tbl[6] & temp_tbl[7]) != 0) return(-EINVAL);
+ 		memcpy(pp->xmit_async_map, temp_tbl,
+ 		       sizeof(pp->xmit_async_map));
+ 		DPRINTF(("PPP: ioctl: set xasyncmap\n"));
+ 		return(0);
+ 
+ 	case PPPIOCSMAXCID:
+ 		if ((pp = pp_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		err = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		i = (int) get_fs_long(arg) + 1;
+ 		DPRINTF(("PPP: ioctl: set maxcid to %d\n", i));
+ 		if (pp->comp != NULL) pphc_free(pp->comp);
+ 		pp->comp = pphc_init(pp, i, i);
+ 		if (pp->comp == NULL) {
+ 			DPRINTF(("PPP: no space for compression buffers!\n"));
+ 			pp_release(pp);
+ 			return(-ENOMEM);
+ 		}
+ 		return(0);
+ 
+ 	default:
+ 		break;
+   }
+ 
+   return(-EINVAL);
+ }
*** original/net/drv/ppp/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/main.c	Fri May 13 17:00:57 1994
***************
*** 0 ****
--- 1,650 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Main program of the PPP device driver.
+  *
+  * Version:	@(#)main.c	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/bitops.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/ip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "ppp.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ #define VERSION		"0.2.5"
+ 
+ 
+ static struct tty_ldisc	ppp_ldisc;
+ struct ppp		*ppp_ctrl[PPP_MAX_DEV];
+ 
+ 
+ /* Initialize a PPP control block for use. */
+ static void
+ pp_initialize(struct ppp *pp)
+ {
+   pp->magic = PPP_MAGIC;
+   pp->sending = 0;
+   pp->toss = 0xFE;
+   pp->escape = 0;
+ 
+   pp->flags = 0;
+   pp->mtu = PPP_MTU;
+   pp->mru = PPP_MRU;
+   pp->fcs = 0;
+ 
+   memset(pp->xmit_async_map, 0, sizeof(pp->xmit_async_map));
+   pp->xmit_async_map[0] = 0xffffffff;
+   pp->xmit_async_map[3] = 0x60000000;
+   pp->recv_async_map = 0x00000000;
+ 
+   pp->comp = NULL;
+   pp->rbuff = NULL;
+   pp->xbuff = NULL;
+   pp->cbuff = NULL;
+ 
+   pp->rhead = NULL;
+   pp->rend = NULL;
+   pp->rcount = 0;
+   pp->xhead = NULL;
+   pp->xtail = NULL;
+ 
+   pp->us_rbuff = NULL;
+   pp->us_rbuff_end = NULL;
+   pp->us_rbuff_head = NULL;
+   pp->us_rbuff_tail = NULL;
+   pp->read_wait = NULL;
+   pp->write_wait = NULL;
+   pp->us_rbuff_lock = 0;
+ 
+   pp->inp_sig = 0;
+   pp->inp_sig_pid = 0;
+ 
+ #ifdef OPTIMIZE_FLAG_TIME /* ensure flag will always be sent first time */
+   pp->last_xmit	= jiffies - OPTIMIZE_FLAG_TIME;
+ #else
+   pp->last_xmit	= 0;
+ #endif
+ 
+   /* Clear statistics. */
+   memset(&pp->stats, 0, sizeof(struct ppp_stats));
+ 
+   /* Reset the demand dial information. */
+   pp->ddinfo.ip_sjiffies  =
+   pp->ddinfo.ip_rjiffies  =
+   pp->ddinfo.nip_sjiffies =
+   pp->ddinfo.nip_rjiffies = jiffies;
+ }
+ 
+ 
+ /*
+  * Stuff a character into the transmit buffer, using PPP's way of
+  * escaping special characters.  Slso, update pp->fcs to take
+  * account of new character.
+  */
+ inline void
+ pp_stuff_char(struct ppp *pp, unsigned char c)
+ {
+   int curpt = pp->xhead - pp->xbuff;
+ 
+   if ((curpt < 0) || (curpt > 3000)) {
+ 	DPRINTF(("PPP: stuff_char: %lX %lX %d\n",
+ 		(unsigned long) pp->xbuff, (unsigned long) pp->xhead, curpt));
+   }
+   if (in_xmap(pp, c)) {
+ 	*pp->xhead++ = PPP_ESC;
+ 	*pp->xhead++ = c ^ PPP_TRANS;
+   } else {
+ 	*pp->xhead++ = c;
+   }
+   pp->fcs = (pp->fcs >> 8) ^ ppp_fcstab[(pp->fcs ^ c) & 0xff];
+ }
+ 
+ 
+ inline void
+ pp_enqueue(struct ppp *pp, unsigned char c)
+ {
+   unsigned long flags;
+ 
+   save_flags(flags);
+   cli();
+   if (pp->rhead < pp->rend) {
+ 	*pp->rhead = c;
+ 	pp->rhead++;
+ 	pp->rcount++;
+   } else {
+ 	pp->stats.roverrun++;
+   }
+   restore_flags(flags);
+ }
+ 
+ 
+ int
+ pp_us_queue(struct ppp *pp, unsigned short proto, unsigned char *buf, int len)
+ {
+   unsigned char *saved_head;
+   int totlen;
+ 
+   totlen = (len + 2);		/* including protocol ID */
+ 
+   if (set_bit(1, &pp->us_rbuff_lock)) {
+ 	DPRINTF(("PPP: us_queue: can't get lock\n"));
+ 	return(0);
+   }
+   saved_head = pp->us_rbuff_head;
+ 
+   /* Set up the packet header. */
+   PUTC((totlen & 0xff00) >> 8, failure);
+   PUTC(totlen & 0x00ff, failure);
+   PUTC((proto & 0xff00) >> 8, failure);
+   PUTC(proto & 0x00ff, failure);
+ 
+   /* Copy the packet data into the buffer. */
+   while (len-- > 0) {
+ 	PUTC(*buf++, failure);
+   }
+ 
+   DPRINTF(("PPP: successfully queued %u bytes\n", totlen));
+   clear_bit(1, &pp->us_rbuff_lock);
+   wake_up_interruptible(&pp->read_wait);
+ 
+ #if NEW_TTY_DRIVERS
+   kill_fasync(pp->tty->fasync, SIGIO);
+ #endif
+ 
+  if (pp->inp_sig && pp->inp_sig_pid) {
+ 	if (kill_proc(pp->inp_sig_pid, pp->inp_sig, 1) != 0) {
+ 		/* process is gone */
+ 		DPRINTF(("PPP: process that requested notification is gone\n"));
+ 		pp->inp_sig = 0;
+ 		pp->inp_sig_pid = 0;
+ 	}
+ 	return(1);
+   }
+ 
+ failure:
+   pp->us_rbuff_head = saved_head;
+   clear_bit(1, &pp->us_rbuff_lock);
+ 
+   DPRINTF(("PPP: us_queue: ran out of buffer space.\n"));
+   return(0);
+ }
+ 
+ 
+ /*
+  * Marks a PPP interface 'busy'.  User processes will wait,
+  * if they try to write, and the network code will refrain
+  * from sending return nonzero if succeeded in acquiring lock.
+  */
+ int
+ pp_lock(struct ppp *pp)
+ {
+   int flags, locked;
+ 
+   save_flags(flags);
+   cli();
+   locked = pp->sending;
+   pp->sending = 1;
+   if (pp->dev->if_flags & IFF_UP) pp->dev->if_tbusy = 1;
+   restore_flags(flags);
+ 
+   return(locked == 0);
+ }
+ 
+ 
+ void
+ pp_unlock(struct ppp *pp)
+ {
+   int flags;
+ 
+   save_flags(flags);
+   cli();
+   pp->sending = 0;
+   if (pp->dev->if_flags & IFF_UP) pp->dev->if_tbusy = 0;
+   restore_flags(flags);
+ }
+ 
+ 
+ /* Find a PPP channel from its `tty' link. */
+ struct ppp *
+ pp_find(struct tty_struct *tty)
+ {
+   int i;
+ 
+   if (tty == NULL) return(NULL);
+   for (i = 0; i < PPP_MAX_DEV; i++) {
+ 	if (ppp_ctrl[i] == NULL) continue;
+ 	if (ppp_ctrl[i]->tty == tty) return(ppp_ctrl[i]);
+   }
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * MTU has been changed by the IP layer. Unfortunately we are not told
+  * about this, but we spot it ourselves and fix things up. We could be
+  * in an upcall from the tty driver, or in an IP packet queue.
+  */
+ void
+ pp_changedmtu(struct ppp *pp, int new_mtu, int new_mru)
+ {
+   unsigned char *new_rbuff, *new_xbuff, *new_cbuff;
+   unsigned char *old_rbuff, *old_xbuff, *old_cbuff;
+   struct ifnet *dev;
+   int mtu, mru;
+ 
+   /* Allocate the buffer from the kernel for the data. */
+   dev = pp->dev;
+   mru = new_mru;
+   mtu = new_mtu;
+ 
+   /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
+   if (mru < PPP_MRU) mru = PPP_MRU;
+   mtu = (mtu * 2) + 20;
+   mru = (mru * 2) + 20;
+ 
+   DPRINTF(("PPP: channel %s mtu=%d mru=%d\n", dev->if_name, new_mtu, new_mru));
+   new_xbuff = (unsigned char *) kmalloc(mtu + 4, GFP_KERNEL);
+   new_rbuff = (unsigned char *) kmalloc(mru + 4, GFP_KERNEL);
+   new_cbuff = (unsigned char *) kmalloc(mru + 4, GFP_KERNEL);
+ 
+   /* If the buffers failed to allocate then complain. */
+   if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL) {
+ 	DPRINTF(("PPP: failed to allocate new buffers\n"));
+ 
+ 	/* Release new buffer pointers if the updates were not performed. */
+ 	if (new_rbuff != NULL) kfree(new_rbuff);
+ 	if (new_xbuff != NULL) kfree(new_xbuff);
+ 	if (new_cbuff != NULL) kfree(new_cbuff);
+   } else {
+ 	/* Update the pointers to the new buffer structures. */
+ 	cli();
+ 	old_xbuff = pp->xbuff;
+ 	old_rbuff = pp->rbuff;
+ 	old_cbuff = pp->cbuff;
+ 
+ 	pp->xbuff = new_xbuff;
+ 	pp->rbuff = new_rbuff;
+ 	pp->cbuff = new_cbuff;
+ 
+ 	pp->mem_start = (unsigned long) new_xbuff;
+ 	pp->mem_end = (unsigned long) (pp->mem_start + mtu);
+ 
+ 	pp->rmem_start = (unsigned long) new_rbuff;
+ 	pp->rend = (unsigned char *)
+ 	pp->rmem_end = (unsigned long) (pp->rmem_start + mru);
+ 
+ 	pp->rhead = new_rbuff;
+ 
+ 	/* Update the parameters for the new buffer sizes. */
+ 	pp->toss = 0xFE;
+ 	pp->escape = 0;
+ 	pp->sending = 0;
+ 	pp->rcount = 0;
+ 	pp->mtu = dev->if_mtu = new_mtu;
+ 	pp->mru = new_mru;
+ 
+ 	sti();
+ 
+ 	/* Release old buffer pointers. */
+ 	if (old_rbuff != NULL) kfree(old_rbuff);
+ 	if (old_xbuff != NULL) kfree(old_xbuff);
+ 	if (old_cbuff != NULL) kfree(old_cbuff);
+   }
+ }
+ 
+ 
+ /* Find a free PPP channel, and link in this `tty' line. */
+ static struct ppp *
+ pp_alloc(void)
+ {
+   int i;
+ 
+   for (i = 0; i < PPP_MAX_DEV; i++) {
+ 	if (ppp_ctrl[i] == NULL) break;
+ 	if (! set_bit(0, &ppp_ctrl[i]->inuse)) {
+ 
+ 		/* UPping the device here sounds fishy to me -FvK */
+ 		ppp_ctrl[i]->dev->if_flags |= (IFF_UP | IFF_RUNNING);
+ 		ppp_ctrl[i]->tty = NULL;
+ 		return(ppp_ctrl[i]);
+ 	}
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Called when we abandon the PPP line discipline */
+ void
+ pp_release(struct ppp *pp)
+ {
+ #if NEW_TTY_DRIVERS
+   if (pp->tty != NULL && pp->tty->disc_data == ppp)
+ 		pp->tty->disc_data = NULL; /* Break the tty->ppp link */
+ #endif
+ 
+   kfree(pp->xbuff);
+   kfree(pp->cbuff);
+   kfree(pp->rbuff);
+   kfree(pp->us_rbuff);
+ 
+   pp->xbuff = NULL;
+   pp->cbuff = NULL;
+   pp->rbuff = NULL;
+   pp->us_rbuff = NULL;
+ 
+   if (pp->comp) {
+ 	pphc_free(pp->comp);
+ 	pp->comp = NULL;
+   }
+ 
+   /* Close down the NET interface. */
+   (void) dev_close(pp->dev);
+   pp->dev->if_flags |= IFF_POINTOPOINT;
+ 
+   pp->inuse = 0;
+   pp->tty = NULL;
+ }
+ 
+ 
+ /*
+  * Called when PPP interface goes "up".
+  * Here this just means we start passing IP packets. */
+ int
+ pp_open(struct ifnet *dev)
+ {
+   struct ppp *pp = ppp_ctrl[dev->if_unit];
+ 
+   if (pp->tty == NULL) {
+ 	DPRINTF(("PPP: %s not connected to a TTY! can't go open!\n",
+ 							dev->if_name));
+ 	return(-ENXIO);
+   }
+ 
+   /* Reset POINTOPOINT every time, since dev_close zaps it! */
+   dev->if_flags |= IFF_POINTOPOINT;
+ 
+   DPRINTF(("PPP: channel %s going up for IP packets!\n", dev->if_name));
+ 
+   CHECK_PPP(-ENXIO);
+   return(0);
+ }
+ 
+ 
+ int
+ pp_close(struct ifnet *dev)
+ {
+   struct ppp *pp = ppp_ctrl[dev->if_unit];
+ 
+   if (pp->tty == NULL) {
+ 	DPRINTF(("PPP: %s not connected to a TTY! can't go down!\n",
+ 							dev->if_name));
+ 	return(-ENXIO);
+   }
+ 
+   DPRINTF(("PPP: channel %s going down for IP packets!\n", dev->if_name));
+ 
+   CHECK_PPP(-ENXIO);
+   return(0);
+ }
+ 
+ 
+ /* Called when PPP line discipline is selected on a tty. */
+ int
+ ppp_open(struct tty_struct *tty)
+ {
+   struct ppp *pp = pp_find(tty);
+ 
+   if (pp != NULL) {
+ 	DPRINTF(("PPP: open: gack! tty already associated to %s!\n",
+ 		pp->magic == PPP_MAGIC ? pp->dev->if_name : "unknown"));
+ 		return(-EEXIST);
+   }
+ 
+   pp = pp_alloc();
+   if (pp == NULL) {
+ 	DPRINTF(("PPP: open: couldn't allocate ppp channel\n"));
+ 	return(-ENFILE);
+   }
+ 
+   /* Make sure the channel is actually open. */
+   pp_initialize(pp);
+ 
+   pp->tty = tty;
+ 
+ #if NEW_TTY_DRIVERS
+   tty->disc_data = pp;
+   if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty);
+   if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
+ #else
+   tty_read_flush(tty);
+   tty_write_flush(tty);
+ #endif
+ 
+   if ((pp->comp = pphc_init(pp, 16, 16)) == NULL) {
+ 	DPRINTF(("PPP: no space for compression buffers!\n"));
+ 	pp_release(pp);
+ 	return(-ENOMEM);
+   }
+ 
+   /* Define the buffers for operation. */
+   pp_changedmtu(pp, pp->dev->if_mtu, pp->mru);
+   if (pp->rbuff == NULL) {
+ 	pp_release(pp);
+ 	return(-ENOMEM);
+   }
+ 
+   /* Allocate a user-level receive buffer. */
+   pp->us_rbuff = kmalloc(RBUFSIZE, GFP_KERNEL);
+   if (pp->us_rbuff == NULL) {
+ 	DPRINTF(("PPP: no space for user receive buffer\n"));
+ 	pp_release(pp);
+ 	return(-ENOMEM);
+   }
+ 
+   pp->us_rbuff_head = pp->us_rbuff;
+   pp->us_rbuff_tail = pp->us_rbuff;
+   pp->us_rbuff_end = (pp->us_rbuff + RBUFSIZE);
+ 
+   DPRINTF(("PPP: channel %s open\n", pp->dev->if_name));
+ 
+ #if NEW_TTY_DRIVERS
+   return(0);
+ #else
+   return(pp->line);
+ #endif
+ }
+ 
+ 
+ void
+ ppp_close(struct tty_struct *tty)
+ {
+   struct ppp *pp = pp_find(tty);
+ 
+   if (pp == NULL || pp->magic != PPP_MAGIC) {
+ 	DPRINTF(("PPP: trying to close unopened tty!\n"));
+   } else {
+ 	CHECK_PPP_VOID();
+ 	pp_release(pp);
+ 	DPRINTF(("PPP: channel %s closing.\n", pp->dev->if_name));
+   }
+ }
+ 
+ 
+ static int
+ ppp_fopen(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= PPP_MAX_DEV) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ ppp_fclose(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= PPP_MAX_DEV) return;
+ }
+ 
+ 
+ static struct file_operations ppp_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   ppp_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   ppp_fopen,	/* OPEN		*/
+   ppp_fclose	/* CLOSE	*/
+ };
+ 
+ 
+ int
+ ppp_init(struct ddi *ddi)
+ {
+   struct ifnet *dev;
+   struct ppp *pp;
+   int i;
+ 
+   printk("%s: version %s", ddi->name, VERSION);
+ #if NEW_TTY_DRIVERS
+   printk(" (NEW_TTY_DRV)");
+ #endif
+   printk(": ");
+ 
+   /* Check for sanity here... */
+   if (ddi->nunits >= PPP_MAX_DEV) {
+ 	printk("too many channels requested!\n");
+ 	return(-1);
+   }
+ 
+   /* Register VFS device. */
+   if (ddi->major != 0) {
+ 	if (register_chrdev(ddi->major, ddi->name, &ppp_fops) < 0) {
+ 		printk("cannot register device %d!\n", ddi->major);
+ 		return(-EINVAL);
+ 	}
+ 	printk("device %d/%d: ", ddi->major, ddi->minor);
+   }
+   printk("%d channels: ", ddi->nunits);
+ 
+   /* Fill in our LDISC request block. */
+   (void) memset(&ppp_ldisc, 0, sizeof(ppp_ldisc));
+   ppp_ldisc.flags = 0;
+   ppp_ldisc.open = ppp_open;
+   ppp_ldisc.close = ppp_close;
+   ppp_ldisc.read = ppp_read;
+   ppp_ldisc.write  = ppp_write;
+   ppp_ldisc.select = ppp_select;
+   ppp_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ 			   unsigned int, unsigned long)) ppp_ioctl;
+ #if NEW_TTY_DRIVERS
+   ppp_ldisc.magic = TTY_LDISC_MAGIC;
+   ppp_ldisc.receive_buf = ppp_receive_buf;
+   ppp_ldisc.write_wakeup = ppp_write_wakeup;
+ #else
+   ppp_ldisc.handler = ppp_handler;
+ #endif
+ 
+   if (tty_register_ldisc(N_PPP, &ppp_ldisc) != 0) {
+ 	printk("cannot register line discipline %d !\n", N_PPP);
+ 	return(-ENODEV);
+   }
+   printk("OK\n");
+   
+   /* Set up all our channels. */
+   for(i = 0; i < ddi->nunits; i++) {
+ 	pp = (struct ppp *) kmalloc(sizeof(struct ppp), GFP_KERNEL);
+ 	if (pp == NULL) {
+ 		printk("PPP: no memory for control block #%d !\n", i);
+ 		return(-ENOMEM);
+ 	}
+ 	memset(pp, 0, sizeof(struct ppp));
+ 
+ 	if ((dev = dev_create(NULL)) == NULL) {
+ 		printk("PPP: no memory for interface descriptor #%d !\n", i);
+ 		kfree_s(pp, sizeof(struct ppp));
+ 		return(-ENOMEM);
+ 	}
+ 	ppp_ctrl[i] = pp;
+ 	pp->line = i;
+ 	pp->dev = dev;
+ 	pp_initialize(pp);
+ 	pp->inuse = 0;
+ 	pp->tty = NULL;
+ 
+ 	sprintf(dev->if_name, PPP_IFNAME, i);
+ 	dev->if_flags = IFF_POINTOPOINT;
+ 	dev->if_mtu = PPP_MTU;
+ 	dev->if_unit = pp->line;
+ 	dev->if_tbusy = 0;
+ 	dev->if_machlen	= PPP_HLEN;
+ 	dev->if_macalen	= PPP_ALEN;
+ 	dev->if_macaddr->ifa_addr.sa_family = ARPHRD_PPP;
+ 
+ 	dev->mac1 = NULL;
+ 	dev->mac2 = NULL;
+ 	dev->mac_aget = NULL;
+ 	dev->mac_aset = NULL;
+ 	dev->mac_bget = NULL;
+ 	dev->mac_bset = NULL;
+ 	dev->mac_print = NULL;
+ 	dev->mac_sprint = NULL;
+ 	dev->set_multi = NULL;
+ 
+ 	dev->open = pp_open;
+ 	dev->close = pp_close;
+ 	dev->ioctl = (int (*)(struct inode *, struct file *,
+ 			   unsigned int, unsigned long)) ppp_ioctl;
+ 	dev->xmit = ppp_xmit;
+ 	dev->send = ppp_send;
+ 	dev->recv = ppp_recv;
+   }
+ 
+   /* Clear out the remaining PPP channels. */
+   while (i < PPP_MAX_DEV)
+ 	ppp_ctrl[i++] = NULL;
+ 
+   /* All done... */
+   return(0);
+ }
*** original/net/drv/ppp/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/packet.c	Fri May 13 17:55:45 1994
***************
*** 0 ****
--- 1,433 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		PPP packet handling functions.
+  *
+  * Version:	@(#)packet.c	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/bitops.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/if_ether.h>		/* for ETH_P_IP */
+ #include <linux/ip.h>
+ #include <linux/tty.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "ppp.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ /*
+  * Examine packet at C, attempt to pass up to net layer. 
+  * PROTO is the protocol field from the PPP frame.
+  * Return 1 if could handle it, 0 otherwise.
+  */
+ static int
+ ppp_do_ip(struct ppp *pp, unsigned short proto, unsigned char *c, int count)
+ {
+   struct sk_buff *skb;
+   int flags, done;
+ 
+   DPRINTF(("PPP: do_ip: proto %X len %u first byte %X\n",
+ 				(int) proto, count, (int) c[0]));
+ 
+   if (ppp_debug_netpackets) {
+ 	DPRINTF((" %s <-- proto %X len %u\n",
+ 		pp->dev->if_name, (int) proto, count));
+   }
+     
+   if (proto == PROTO_IP) {
+ 	pp->stats.runcomp++;
+ 	goto sendit;
+   }
+ 
+   if ((proto == PROTO_VJCOMP) && !(pp->flags & SC_REJ_COMP_TCP)) {
+ 	/* Get space for uncompressing the header. */
+ 	done = 0;
+ 	save_flags(flags);
+ 	cli();
+ 	if ((pp->rhead + 80) < pp->rend) {
+ 		pp->rhead += 80;
+ 		pp->rcount += 80;
+ 		done = 1;
+ 	}
+ 	restore_flags(flags);
+ 
+ 	if (! done) {
+ 		DPRINTF(("PPP: no space to decompress VJ compressed TCP header.\n"));
+ 		pp->stats.roverrun++;
+ 		pp->dev->if_stats->rx_dropped++;
+ 		return(1);
+ 	}
+ 
+ 	count = pphc_uncompress(pp->comp, c, count);
+ 	if (count <= 0) {
+ 		pp->stats.rerrors++;
+ 		pp->dev->if_stats->rx_dropped++;
+ 		DPRINTF(("PPP: error in VJ decompression\n"));
+ 		return(1);
+ 	}
+ 	pp->stats.rcomp++;
+ 	goto sendit;
+   }
+   
+   if ((proto == PROTO_VJUNCOMP) && !(pp->flags & SC_REJ_COMP_TCP)) {
+ 	if (pphc_remember(pp->comp, c, count) <= 0) {
+ 		pp->stats.rerrors++;
+ 		pp->dev->if_stats->rx_dropped++;
+ 		DPRINTF(("PPP: error in VJ memorizing\n"));
+ 		return(1);
+ 	}
+ 	pp->stats.runcomp++;
+ 	goto sendit;
+   }
+ 
+   /* Not ours... */
+   pp->dev->if_stats->rx_dropped++;
+   return(0);
+ 
+ sendit:
+ #ifdef PPP_DEBUG
+   if (ppp_debug_netpackets) {
+ 	struct ip *iph = (struct ip *) c;
+ 
+ 	DPRINTF(("%s <--    src %lX dst %lX len %u\n",
+ 		pp->dev->if_name, iph->ip_src.s_addr,
+ 		iph->ip_dst.s_addr, count));
+   }
+ #endif
+ 
+   /* Allocate skb and copy data. */
+   if ((skb = skb_create(count)) == NULL) {
+ 	printk("%s: Could not allocate skb of size %u !\n",
+ 			pp->dev->if_name, count);
+ 	pp->dev->if_stats->rx_dropped++;
+ 	return(0);
+   }
+ 
+   /* Copy the packet to the new buffer. */
+   memcpy(skb->raw, c, count);
+ 
+   /* Bump the datagram to the upper layers... */
+   DPRINTF(("PPP: packet is %u at 0x%lX\n", count, (unsigned long) skb));
+   do {
+         done = dev_rint(pp->dev, skb, count);
+         if (done == 0 || done == 1) break;
+   } while(1);
+ 
+   /* Update the statistics. */
+   pp->dev->if_stats->rx_packets++;
+   if (done == 1) pp->dev->if_stats->rx_dropped++;
+ 
+   return(1);
+ }
+ 
+ 
+ /*
+  * On entry, a received frame is in pp->rbuff
+  * check it and dispose as appropriate.
+  */
+ void
+ ppp_doframe(struct ppp *pp)
+ {
+   u_char *c = pp->rbuff;
+   u_short proto;
+   int count = pp->rcount;
+ 
+   /* Forget it if we've already noticed an error. */
+   if (pp->toss) {
+ 	DPRINTF(("PPP: toss: tossing frame, reason = %d\n", pp->toss));
+ 	pp->stats.rerrors++;
+ 	pp->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+ 
+   /* Do this before printing buffer to avoid generating copious output. */
+   if (count == 0) return;
+ 
+ #ifdef PPP_DEBUG
+   if (ppp_debug >= 3)
+ 	ppp_print_buffer("receive frame", c, count, KERNEL_DS);
+ #endif
+ 
+   if (count < 4) {
+ 	DPRINTF(("PPP: got runt PPP frame, %u chars\n", count));
+ 	pp->stats.runts++;
+ 	pp->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+ 
+   /* Check PPP error detection field. */
+   if (! ppp_check_fcs(pp)) {
+ 	DPRINTF(("PPP: frame with bad fcs\n"));
+ 	pp->stats.rerrors++;
+ 	pp->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+   count -= 2;		/* ignore last two characters (== FCS). */
+ 
+   /*
+    * Now we have a good frame.
+    * Figure out the protocol field.
+    */
+   if ((c[0] == PPP_ADDRESS) && (c[1] == PPP_CONTROL)) {
+ 	c += 2;			/* ADDR/CTRL not compressed, so skip */
+ 	count -= 2;
+   }
+ 
+   proto = (u_short) *c++;		/* PROTO compressed */
+   if (proto & 1) {
+ 	count--;
+   } else {
+ 	proto = (proto << 8) | (u_short) *c++;	/* PROTO uncompressed */
+ 	count -= 2;
+   }
+ 
+   /* Send the frame to the network if the PPP interface is up. */
+   if (pp->dev->if_flags & IFF_UP) {
+ 	if (ppp_do_ip(pp, proto, c, count)) {
+ 		pp->ddinfo.ip_rjiffies = jiffies;
+ 		return;
+ 	}
+   }
+ 
+   /*
+    * If we got here, it has to go to a user process doing a
+    * read, so queue it.
+    *
+    * User process expects to get whole frame (for some reason),
+    * so use count+2 so as to include FCS field.
+    */
+   if (pp_us_queue(pp, proto, c, count+2)) {
+ 	pp->ddinfo.nip_rjiffies = jiffies;
+ 	pp->dev->if_stats->rx_packets++;
+ 	pp->stats.rothers++;
+ 	return;
+   }
+ 
+   /* Couldn't cope. */
+   DPRINTF(("PPP: dropping packet on the floor: nobody could take it.\n"));
+   pp->stats.tossed++;
+   pp->dev->if_stats->rx_dropped++;
+ }
+ 
+ 
+ /* We finished some buffer. */
+ void
+ ppp_output_done(void *pp)
+ {
+   /* Unlock the transmitter queue. */
+   pp_unlock((struct ppp *) pp);
+ 
+   /*
+    * If the device is still up then enable the transmitter
+    * of the next frame.
+    */
+   if (((struct ppp *) pp)->dev->if_flags & IFF_UP)
+ 	dev_tint(((struct ppp *) pp)->dev);
+ 
+   /* Enable any blocked process pending transmission. */
+   wake_up_interruptible(&((struct ppp *) pp)->write_wait);
+ }
+ 
+ 
+ /* Process an incoming packet. */
+ int
+ ppp_recv(struct ifnet *dev, struct sk_buff *skb, int len)
+ {
+   DPRINTF(("PPP: %s recv: len=%d\n", dev->if_name, len));
+ 
+   /* PPP does not have a MAC header. */
+   skb->raw = (unsigned char *) (skb + 1);
+   skb->len -= dev->if_machlen;
+ 
+   return(ETH_P_IP);
+ }
+ 
+ 
+ /* Send out a packet. */
+ void
+ ppp_send(struct ifnet *dev, struct sk_buff *skb,
+ 	  struct sockaddr *target, int priority)
+ {
+   DPRINTF(("PPP: %s sent: len=%d\n", dev->if_name, skb->len));
+ 
+   skb->raw = (unsigned char *) (skb + 1);
+ #if 0
+   skb->len += dev->if_machlen;
+ #endif
+ 
+   /* Call the DEV interface for the actual work. */
+   dev_queue(dev, skb, target, priority);
+ }
+ 
+ 
+ /* Encapsulate a packet and kick it into a TTY queue. */
+ int
+ ppp_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct tty_struct *tty;
+   struct ppp *pp;
+   unsigned char *p;
+   unsigned short proto;
+   int len;
+ 
+   if (skb == NULL) {
+ 	DPRINTF(("PPP: xmit: null skb !\n"));
+ 	return(0);
+   }
+ 
+   /* Find the correct PPP channel to use. */
+   pp = ppp_ctrl[dev->if_unit];
+   tty = pp->tty;
+   p = skb->raw;
+   len = skb->len;
+   proto = PROTO_IP;
+ 
+   DPRINTF(("PPP: xmit [%s]: skb %lX busy %d\n",
+ 		dev->if_name, (unsigned long) skb, pp->sending));
+ 
+   CHECK_PPP(0);
+ 
+   if (tty == NULL) {
+ 	DPRINTF(("PPP: xmit: %s not connected to a TTY!\n", dev->if_name));
+ 	pp->dev->if_stats->tx_dropped++;
+ 	goto done;
+   }
+ 
+   if (! (dev->if_flags & IFF_UP)) {
+ 	DPRINTF(("PPP: xmit: packet sent on interface %s, which is down for IP\n",
+ 					dev->if_name));
+ 	pp->dev->if_stats->tx_dropped++;
+ 	goto done;
+   }
+ 
+   /* Don't believe what the skb says- get the frame size from the header. */
+   if (len < sizeof(struct ip)) {
+ 	DPRINTF(("PPP: xmit: given runt packet, ignoring\n"));
+ 	pp->dev->if_stats->tx_dropped++;
+ 	return(1);
+   }
+   len = ntohs(((struct ip *) skb->raw)->ip_len);
+ 
+   /* If doing demand dial then divert the first frame to pppd. */
+   if (pp->flags & SC_IP_DOWN) {
+ 	if (pp->flags & SC_IP_FLUSH == 0) {
+ 		if (pp_us_queue(pp, proto, p, len)) pp->flags |= SC_IP_FLUSH;
+ 	}
+ #if 1	/* FIXME: not really, as it will only be delayed for a while... */
+ 	pp->dev->if_stats->tx_dropped++;
+ #endif
+ 	goto done;
+   }
+ 
+   /* Attempt to acquire send lock. */
+   if (pp->sending || !pp_lock(pp)) {
+ 	DPRINTF(("PPP: xmit: BUSY\n"));
+ 	pp->stats.sbusy++;
+ 	pp->dev->if_stats->tx_dropped++;
+ 	return(1);
+   }
+   pp->xhead = pp->xbuff;
+ 
+   /* Try to compress, if VJ compression mode is on. */
+   if (pp->flags & SC_COMP_TCP) {
+ 	/* NOTE: last 0 argument says never to compress connection ID. */
+ 	len = pphc_compress(pp->comp, p, len, pp->cbuff, &p, 0);
+ 	if (p[0] & PPP_TYPE_COMP_TCP) proto = PROTO_VJCOMP;
+ 	  else {
+ 		if (p[0] >= PPP_TYPE_UNCOMP_TCP) {
+ 			proto = PROTO_VJUNCOMP;
+ 			p[0] = (p[0] & 0x0f) | 0x40; 
+ 		}
+ 	}
+   }
+ 
+   /* Increment appropriate counter. */
+   if (proto == PROTO_VJCOMP) ++pp->stats.scomp;
+     else ++pp->stats.suncomp;
+       
+ #ifdef PPP_DEBUG
+   if (ppp_debug_netpackets) {
+ 	struct ip *iph = (struct ip *) (skb + 1);
+ 
+ 	DPRINTF(("%s ==> proto %X len %d src %lX dst %lX proto %d\n",
+ 		dev->if_name, (int) proto, (int) len,
+ 		(unsigned long) iph->ip_src.s_addr,
+ 		(unsigned long) iph->ip_dst.s_addr,
+ 		(int) iph->ip_p));
+   }
+ #endif
+ 
+   /* start of frame:   FLAG  ALL_STATIONS  CONTROL  <protohi> <protolo> */
+ #ifdef OPTIMIZE_FLAG_TIME
+   if (jiffies - pp->last_xmit > OPTIMIZE_FLAG_TIME) *pp->xhead++ = PPP_FLAG;
+   pp->last_xmit = jiffies;
+ #else      
+   *pp->xhead++ = PPP_FLAG;
+ #endif
+ 
+   pp->fcs = PPP_FCS_INIT;
+   if (! (pp->flags & SC_COMP_AC)) { 
+ 	pp_stuff_char(pp, PPP_ADDRESS);
+ 	pp_stuff_char(pp, PPP_CONTROL);
+   }
+ 
+   if (! (pp->flags & SC_COMP_PROT) || (proto & 0xff00)) {
+ 	pp_stuff_char(pp, proto >> 8);
+   }
+   pp_stuff_char(pp, proto & 0xff);
+ 
+   /* Data part. */
+   while (len-- > 0)
+ 	pp_stuff_char(pp, *p++);
+ 
+   /* RCS and flag. */
+   ppp_add_fcs(pp);
+   *pp->xhead++ = PPP_FLAG;
+ 
+   /* Update the time for demand dial function. */
+   pp->ddinfo.ip_sjiffies = jiffies;
+ 
+   /* Send it! */
+   DPRINTF(("PPP: xmit: sending %d bytes\n", pp->xhead - pp->xbuff));
+ #ifdef PPP_DEBUG
+   if (ppp_debug >= 6)
+ 	ppp_print_buffer("xmit buffer", pp->xbuff,
+ 			 pp->xhead - pp->xbuff, KERNEL_DS);
+ #endif
+   ppp_kick_tty(pp);
+   pp->dev->if_stats->tx_packets++;
+ 
+  done:
+   return(0);
+ }
*** original/net/drv/ppp/ppp.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/ppp.h	Fri May 13 17:57:30 1994
***************
*** 0 ****
--- 1,51 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		PPP general driver definitions.
+  *
+  * Version:	@(#)ppp.h	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		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.
+  */
+ #ifndef _PPP_H
+ #define _PPP_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	PPP_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  PPP_DEBUG
+ #   define PPP_DEBUG
+ #endif
+ 
+ #ifdef	PPP_DEBUG
+ #   define DPRINTF(x)	ppp_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ /* PPP configuration. */
+ #define PPP_MAX_DEV	64		/* should be enough for now :-)	*/
+ #define NET_PPP_MAJOR	31
+ #define CHECK_CHARS	0		/* check character values	*/
+ 
+ 
+ extern int		ppp_init(struct ddi *ddi);
+ 
+ 
+ #endif	/* _PPP_H */
*** original/net/drv/ppp/tty.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/tty.c	Fri May 13 17:05:40 1994
***************
*** 0 ****
--- 1,543 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		The following functions form support user programs which
+  *		read and write data on a TTY with the PPP line discipline.
+  *		Reading is done from a circular queue, filled by the lower
+  *		TTY levels.
+  *
+  * Version:	@(#)tty.c	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/interrupt.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/ptrace.h>
+ #include <linux/ioport.h>
+ #include <linux/tty.h>
+ #include <linux/signal.h>
+ #include <linux/ddi.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/ip.h>
+ #include <asm/system.h>
+ #include <asm/bitops.h>
+ #include <asm/segment.h>
+ #include "ppp.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ #if NEW_TTY_DRIVERS
+ void
+ ppp_kick_tty(struct ppp *pp)
+ {
+   register int count, actual;
+ 
+   count = pp->xhead - pp->xbuff;
+ 
+   actual = pp->tty->driver.write(pp->tty, 0, pp->xbuff, count);
+   pp->stats.sbytes += actual;
+   if (actual == count) {
+ 	ppp_output_done(pp);
+   } else {
+ 	pp->xtail = pp->xbuff + actual;
+ 	pp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+   }
+ }
+ 
+ 
+ void
+ ppp_write_wakeup(struct tty_struct *tty)
+ {
+   register int count, actual;
+   struct ppp *pp = pp_find(tty);
+ 
+   if (ppp == NULL || pp->magic != PPP_MAGIC) {
+  	printk("PPP: write_wakeup called but couldn't find PPP struct.\n");
+ 	return;
+   }
+ 
+   if (pp->xtail == NULL || (pp->flags & SC_XMIT_BUSY)) return;
+ 
+   cli();
+   if (pp->flags & SC_XMIT_BUSY) return;
+   pp->flags |= SC_XMIT_BUSY;
+   sti();
+           
+   count = pp->xhead - pp->xtail;
+   actual = tty->driver.write(tty, 0, pp->xtail, count);
+   pp->stats.sbytes += actual;
+   if (actual == count) {
+ 	pp->xtail = NULL;
+ 	tty->flags &= ~TTY_DO_WRITE_WAKEUP;
+ 	ppp_output_done(pp);
+   } else {
+ 	pp->xtail += actual;
+   }
+   pp->flags &= ~SC_XMIT_BUSY;
+ }
+ 
+ 
+ void
+ ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
+ 		char *fp, int count)
+ {
+   register struct ppp *pp = pp_find(tty);
+   unsigned char c;
+  
+   if (pp == NULL || pp->magic != PPP_MAGIC) {
+ 	DPRINTF(("PPP: handler called but couldn't find PPP struct.\n"));
+ 	return;
+   }
+ 
+   CHECK_PPP_VOID();
+ 
+ #ifdef PPP_DEBUG
+   if (ppp_debug >= 5)
+ 	ppp_print_buffer("receive buffer", cp, count, KERNEL_DS);
+ #endif
+  
+   while (count-- > 0) {
+ 	c = *cp++;
+ 
+ 	if (fp) {
+ 		if (*fp && pp->toss == 0) pp->toss = *fp;
+ 		fp++;
+ 	}
+ 
+ #if CHECK_CHARS
+ 	if (c & 0x80)
+ 		pp->flags |= SC_RCV_B7_1;
+ 	  else
+ 		pp->flags |= SC_RCV_B7_0;
+ 	if (paritytab[c >> 5] & (1 << (c & 0x1F)))
+ 		pp->flags |= SC_RCV_ODDP;
+ 	  else
+ 		pp->flags |= SC_RCV_EVNP;
+ #endif
+ 
+ 	switch(c) {
+ 		case PPP_ESC:	/* PPP_ESC: invert 0x20 in next character */
+ 			pp->escape = PPP_TRANS;
+ 			break;
+ 
+ 		case PPP_FLAG:	/* PPP_FLAG: end of frame */
+ 			/* PPP_ESC just before PPP_FLAG is "cancel". */
+ 			if (pp->escape) pp->toss = 0xFF;
+ 
+ 			/* Pass frame on to next layers. */
+ 			if (! (pp->toss & 0x80)) ppp_doframe(pp);
+ 
+ 			/* State Reset. */
+ 			pp->rcount = 0;
+ 			pp->rhead = pp->rbuff;
+ 			pp->escape = 0;
+ 			pp->toss = 0;
+ 			break;
+ 
+ 		default:	/* regular character */
+ 			if (! in_rmap(pp, c)) {
+ 				if (pp->toss == 0)
+ 					ppp_enqueue(pp, c ^ pp->escape);
+ 				pp->escape = 0;
+ 			}
+ 	}
+   }
+ }
+ #else	/* ! NEW_TTY_DRIVERS */
+ #ifdef PPP_DEBUG
+ static void
+ ppp_dump_inqueue(struct tty_struct *tty)
+ {
+   int  head = tty->read_q.head,
+        tail = tty->read_q.tail,
+        i, count;
+   char buffer[8];
+ 
+   DPRINTF(("INQUEUE: head %lX tail %lX imode %X:\n",
+ 	(unsigned long) head, (unsigned long) tail, 
+ 	(unsigned int) tty->termios->c_iflag));
+ 
+   i = tail;
+   count = 0;
+   while (i != head) {
+ 	buffer[count] = tty->read_q.buf[i];
+ 	if (++count == 8) {
+ 		ppp_print_buffer(NULL, buffer, 8, KERNEL_DS);
+ 		count = 0;
+ 	}
+ 	i = (i + 1) & (TTY_BUF_SIZE - 1);
+   }
+   ppp_print_buffer(NULL, buffer, count, KERNEL_DS);
+ }
+ #endif	/* PPP_DEBUG */
+ 
+ 
+ /*
+  * recover frame by undoing PPP escape mechanism;
+  * copies N chars of input data from C into PPP->rbuff
+  * calls ppp_doframe to dispose of any frames it finds
+  */
+ static void
+ ppp_unesc(struct ppp *pp, unsigned char *c, int n)
+ {
+   int i;
+ 
+   for (i = 0; i < n; i++, c++) {
+ 	DPRINTF(("PPP: (%X)", (unsigned int) *c));
+ 
+ #if CHECK_CHARS
+ 	if (*c & 0x80)
+ 		pp->flags |= SC_RCV_B7_1;
+ 	  else
+ 		pp->flags |= SC_RCV_B7_0;
+ 	if (paritytab[*c >> 5] & (1 << (*c & 0x1F)))
+ 		pp->flags |= SC_RCV_ODDP;
+ 	  else
+ 		pp->flags |= SC_RCV_EVNP;
+ #endif
+ 
+ 	switch(*c) {
+ 		case PPP_ESC:		/* PPP_ESC: invert 0x20 in next character */
+ 			pp->escape = PPP_TRANS;
+ 			break;
+ 
+ 		case PPP_FLAG:		/* PPP_FLAG: end of frame */
+ 			/* PPP_ESC just before PPP_FLAG is illegal. */
+ 			if (pp->escape) pp->toss = 0xFF;
+ 
+ 			/* Pass frame on to next layers. */
+ 			if (! (pp->toss & 0x80)) ppp_doframe(pp);
+ 
+ 			/* State Reset. */
+ 			pp->rcount = 0;
+ 			pp->rhead = pp->rbuff;
+ 			pp->escape = 0;
+ 			pp->toss = 0;
+ 			break;
+ 
+ 		default:			/* regular character */
+ 			if (! in_rmap(pp, *c)) {
+ 				if (pp->toss == 0)
+ 					pp_enqueue(pp, *c ^ pp->escape);
+ 				pp->escape = 0;
+ 			}
+ 			break;
+ 	}
+   }
+ }
+ 
+ 
+ /*
+  * Called by lower levels of TTY driver when data becomes
+  * available.  All incoming data comes through this function.
+  */
+ void
+ ppp_handler(struct tty_struct *tty)
+ {
+   unsigned char buff[128];
+   struct ppp *pp = pp_find(tty);
+   int n, error;
+ 
+   if (pp == NULL || pp->magic != PPP_MAGIC) {
+ 	printk("PPP: handler called but couldn't find PPP struct.\n");
+ 	return;
+   }
+ 
+   CHECK_PPP_VOID();
+ 
+ #ifdef PPP_DEBUG
+   if (ppp_debug >= 5)
+ 	ppp_dump_inqueue(pp->tty);
+ #endif
+ 
+   do {
+ 	n = tty_read_raw_data(tty, buff, 128);
+ 	if (n == 0) break;	/* nothing there */
+ 
+ #ifdef PPP_DEBUG
+ 	if (ppp_debug >= 5)
+ 		ppp_print_buffer("receive buffer",
+ 			buff, (n > 0) ? n : -n, KERNEL_DS);
+ #endif
+ 
+ 	if (n < 0) {
+ 		/*
+ 		 * Last character is error flag.
+ 		 * Process the previous characters, then set toss flag.
+ 		 */
+ 		n = (-n) - 1;
+ 		error = buff[n];
+ 	} else error = 0;
+ 	pp->stats.rbytes += n;
+ 	ppp_unesc(pp, buff, n);
+ 	if (error) pp->toss = error;
+   } while(1);
+ }
+ 
+ 
+ void
+ ppp_kick_tty(struct ppp *pp)
+ {
+   register int count = pp->xhead - pp->xbuff;
+   register int answer;
+ 
+   pp->stats.sbytes += count;
+   answer = tty_write_data(pp->tty, pp->xbuff, count,
+ 			  ppp_output_done, (void *) pp);
+ 
+   if (answer == 0) {
+ 	ppp_output_done(pp);		/* should not happen */
+   } else {
+ 	if (answer < 0) {
+ 		pp->stats.serrors++;
+ 		ppp_output_done(pp);	/* unlock the transmitter */
+ 	}
+   }
+ }
+ #endif	/* NEW_TTY_DRIVERS */
+ 
+ 
+ /*
+  * Read a PPP frame from the us_rbuff circular
+  * buffer, waiting if necessary.
+  */
+ int
+ ppp_read(struct tty_struct *tty, struct file *file,
+ 	 unsigned char *buf, unsigned int nr)
+ {
+   struct ppp *pp = pp_find(tty);
+   unsigned char c;
+   int len, i;
+ 
+   if (pp == NULL || pp->magic != PPP_MAGIC) {
+ 	DPRINTF(("PPP: read: cannnot find PPP channel\n"));
+ 	return(-EIO);
+   }
+ 
+   CHECK_PPP(-ENXIO);
+ 
+   DPRINTF(("PPP: read: called %lX num %u\n", (unsigned long) buf, nr));
+   do {
+ 	/* Try to acquire read lock. */
+ 	if (set_bit(0, &pp->us_rbuff_lock) == 0) {
+ 		/* Got lock. */
+ 		if (pp->us_rbuff_head == pp->us_rbuff_tail) {
+ 			/* No data. */
+ 			DPRINTF(("PPP: read: no data\n"));
+ 			clear_bit(0, &pp->us_rbuff_lock);
+ 			if (pp->inp_sig) {
+ 				DPRINTF(("PPP: read: EWOULDBLOCK\n"));
+ 				return(-EWOULDBLOCK);
+ 			} else goto wait;
+ 		}
+ 
+ 		/* Reset the time of the last read operation. */
+ 		pp->ddinfo.nip_rjiffies = jiffies;
+ 
+ 		/* Fetch packet length. */
+ 		GETC(c); len = (c << 8);
+ 		GETC(c); len |= (c & 0xff);
+ 		DPRINTF(("PPP: read: len=%u\n", len));
+ 
+ 		if ((len + 2) > nr) {
+ 			/*
+ 			 * Frame too big; can't copy it, but do
+ 			 * update us_rbuff_head.
+ 			 */
+ 			DPRINTF(("PPP: read of %u bytes too small for %d frame\n",
+ 							nr, len+2));
+ 			pp->us_rbuff_head += len;
+ 			if (pp->us_rbuff_head > pp->us_rbuff_end)
+ 				pp->us_rbuff_head +=
+ 					- (pp->us_rbuff_end - pp->us_rbuff);
+ 			clear_bit(0, &pp->us_rbuff_lock);
+ 			wake_up_interruptible(&pp->read_wait);
+ 			pp->stats.rgiants++;
+ 			return(-EOVERFLOW);		/* ZZZ; HACK! */
+ 		} else {
+ 			/*
+ 			 * Have the space: copy the packet, faking the
+ 			 * first two bytes.
+ 			 */
+ 			put_fs_byte(PPP_ADDRESS, buf++);
+ 			put_fs_byte(PPP_CONTROL, buf++);
+ 			i = len;
+ 			while (i-- > 0) {
+ 				GETC(c);
+ 				put_fs_byte(c, buf++);
+ 			}
+ 		}
+ 
+ 		clear_bit(0, &pp->us_rbuff_lock);
+ 		DPRINTF(("PPP: read: passing %d bytes up\n", len + 2));
+ 		pp->stats.rothers++;
+ 		return(len + 2);
+ 	}
+ 
+ wait:
+ 	/* We need to wait. */
+ 	current->timeout = 0;
+ 	DPRINTF(("PPP: read: sleeping\n"));
+ 	interruptible_sleep_on(&pp->read_wait);
+ 	if (current->signal & ~current->blocked) return(-EINTR);
+   } while(1);
+ 
+   /*NOTREACHED*/
+   return(0);
+ }
+ 
+ 
+ /*
+  * Write a frame with NR chars from BUF to TTY.
+  * We have to put the FCS field on ourselves.
+  */
+ int
+ ppp_write(struct tty_struct *tty, struct file *file,
+ 	  unsigned char *buf, unsigned int nr)
+ {
+   struct ppp *pp = pp_find(tty);
+   int i;
+ 
+   if (pp == NULL || pp->magic != PPP_MAGIC) {
+ 	DPRINTF(("PPP: write: cannot find PPP unit\n"));
+ 	return(-EIO);
+   }
+ 
+   CHECK_PPP(-ENXIO);
+   
+   /* Has someone has been ifconfigging? */
+   if (pp->mtu != pp->dev->if_mtu)
+ 	pp_changedmtu(pp, pp->dev->if_mtu, pp->mru);
+ 
+   if (nr > pp->mtu) {
+ 	DPRINTF(("PPP: write: truncating user packet from %u to MTU %d\n",
+ 						nr, pp->mtu));
+ 	nr = pp->mtu;
+   }
+ 
+ #ifdef PPP_DEBUG
+   if (ppp_debug >= 3)
+ 	ppp_print_buffer ("write frame", buf, nr, USER_DS);
+ #endif
+ 
+   /* lock this PPP unit so we will be the only writer; sleep if necessary. */
+   while((pp->sending == 1) || !pp_lock(pp)) {
+ 	current->timeout = 0;
+ 	DPRINTF(("PPP: write: sleeping\n"));
+ 	interruptible_sleep_on(&pp->write_wait);
+ 	if (current->signal & ~current->blocked) return(-EINTR);
+   }
+ 
+   /* OK, locked.  Stuff the given bytes into the buffer. */
+   DPRINTF(("PPP: write: acquired write lock\n"));
+   pp->xhead = pp->xbuff;
+ 
+ #ifdef OPTIMIZE_FLAG_TIME
+   if (jiffies - pp->last_xmit > OPTIMIZE_FLAG_TIME) *pp->xhead++ = PPP_FLAG;
+   pp->last_xmit = jiffies;
+ #else      
+   *pp->xhead++ = PPP_FLAG;
+ #endif
+ 
+   pp->fcs = PPP_FCS_INIT;
+   i = nr;
+   while (i-- > 0)
+ 	pp_stuff_char(pp, get_fs_byte(buf++));
+   ppp_add_fcs(pp);		/* concatenate FCS at end */
+   *pp->xhead++ = PPP_FLAG;
+ 
+   /* Reset the time of the last write operation. */
+   pp->ddinfo.nip_sjiffies = jiffies;
+ 
+   DPRINTF(("PPP: write: writing %d chars\n", pp->xhead - pp->xbuff));
+ #ifdef PPP_DEBUG
+   if (ppp_debug >= 6)
+ 	ppp_print_buffer("xmit buffer", pp->xbuff,
+ 			pp->xhead - pp->xbuff, KERNEL_DS);
+ #endif
+ 
+   /* Packet is ready-to-go. */
+   pp->stats.sothers++;
+   ppp_kick_tty(pp);
+ 
+   return((int) nr);
+ }
+  
+ 
+ int
+ ppp_select(struct tty_struct *tty, struct inode * inode,
+ 	   struct file * filp, int sel_type, select_table * wait)
+ {
+   struct ppp *pp = pp_find(tty);
+   
+   if (pp == NULL || pp->magic != PPP_MAGIC) {
+ 	DPRINTF(("PPP: select: can't find PPP block from tty!\n"));
+ 	return(-EBADF);
+   }
+   
+   /* If the PPP protocol is no longer active, return false. */
+   CHECK_PPP(0);
+   
+   /* Process the request based upon the type desired */
+   switch (sel_type) {
+ 	case SEL_IN:
+ 		if (set_bit(0, &pp->us_rbuff_lock) == 0) {
+ 			/* Test for the presence of data in the queue */
+ 			if (pp->us_rbuff_head != pp->us_rbuff_tail) {
+ 				clear_bit(0, &pp->us_rbuff_lock);
+ 				return(1);
+ 			}
+ 			clear_bit(0, &pp->us_rbuff_lock);
+ 		}
+ 		/*FALLTHROUGH*/
+ 
+ 	case SEL_EX:
+ 		/* Is there a pending error condition? */
+ 		if (tty->packet && tty->link->ctrl_status) return(1);
+     
+ 		/* closed? */
+ 		if (tty->flags & (1 << TTY_SLAVE_CLOSED)) return(1);
+     
+ 		/* If the TTY is disconnected, then this is an exception too. */
+ 		if (tty_hung_up_p(filp)) return(1);
+ 
+ 		select_wait(&pp->read_wait, wait);
+ 		break;
+     
+ 	case SEL_OUT:
+ 		if (pp_lock(pp)) {
+ 			if (pp->sending == 0) {
+ 				pp_unlock(pp);
+ 				return(1);
+ 			}
+ 			pp_unlock(pp);
+ 		}
+ 		select_wait(&pp->write_wait, wait);
+ 		break;
+   }
+   return(0);
+ }
*** original/net/drv/ppp/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/ppp/utils.c	Fri May 13 17:06:57 1994
***************
*** 0 ****
--- 1,164 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the PPP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Various functions.
+  *
+  * Version:	@(#)utils.c	0.2.5	05/12/94
+  *
+  * Authors:	Michael Callahan <callahan@maths.ox.ac.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This program is  free  software; you  can redistribute  it
+  *		and/or modify it under the terms of the GNU General Public
+  *		License  as  published by the  Free  Software  Foundation;
+  *		either version 2  of the License, or (at  your option) any
+  *		later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #include <linux/ip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include <stdarg.h>
+ #include "ppp.h"
+ #include "global.h"
+ 
+ 
+ int		ppp_debug = 0;			/* PPP debugging flag	*/
+ int		ppp_debug_netpackets = 0;
+ #if CHECK_CHARS
+ unsigned long paritytab[8] = {
+   0x96696996, 0x69969669, 0x69969669, 0x96696996,
+   0x69969669, 0x96696996, 0x96696996, 0x69969669
+ };
+ #endif
+ 
+ 
+ #ifdef PPP_DEBUG
+ void
+ ppp_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (ppp_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ 
+ 
+ static inline void
+ ppp_print_hex(register char *out, char *in, int count)
+ {
+   static char hex[] = "0123456789ABCDEF";
+   register unsigned char next_ch;
+ 
+   while (count-- > 0) {
+ 	next_ch = (unsigned char) get_fs_byte(in);
+ 
+ 	*out++ = hex[(next_ch >> 4) & 0x0F];
+ 	*out++ = hex[next_ch & 0x0F];
+ 	in++;
+   }
+ }
+ 
+ 
+ static inline void
+ ppp_print_char(register char *out, char *in, int count)
+ {
+   register unsigned char next_ch;
+ 
+   while (count-- > 0) {
+ 	next_ch = (unsigned char) get_fs_byte(in);
+ 
+ 	if (next_ch < 0x20 || next_ch > 0x7e) *out++ = '.';
+ 	  else {
+ 		*out++ = *in;
+ 		/* printk/syslogd has a bug !! */
+ 		if (next_ch == '%') *out++ = '%';
+ 	}
+ 	in++;
+   }
+   *out = '\0';
+ }
+ 
+ 
+ void
+ ppp_print_buffer(const char *name, char *buf, int count, int seg)
+ {
+   char line [44];
+   int  old_fs = get_fs();
+ 
+   set_fs(seg);
+ 
+   if (name != (char *) NULL)
+ 	DPRINTF(("PPP: %s, count = %d\n", name, count));
+ 
+   while (count > 8) {
+ 	memset(line, ' ', sizeof(line));
+ 	ppp_print_hex(line, buf, 8);
+ 	ppp_print_char(&line[8 * 3], buf, 8);
+ 	DPRINTF(("%s\n", line));
+ 	count -= 8;
+ 	buf += 8;
+   }
+ 
+   if (count > 0) {
+ 	memset(line, ' ', sizeof(line));
+ 	ppp_print_hex(line, buf, count);
+ 	ppp_print_char(&line[8 * 3], buf, count);
+ 	DPRINTF(("%s\n", line));
+   }
+ 
+   set_fs(old_fs);
+ }
+ #endif	/* PPP_DEBUG */
+ 
+ 
+ int
+ ppp_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		ppp_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		ppp_debug = level;
+ 		break;
+ 	default:
+ 		if (val >= 128) {
+ 			ppp_debug = 0;
+ 			return(0);
+ 		}
+ 		ppp_debug = val;
+ 		break;
+   }
+ 
+   return(0);
+ }
*** original/net/drv/slip/COPYRIGHT	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/COPYRIGHT	Mon May  9 13:13:09 1994
***************
*** 0 ****
--- 1,48 ----
+ 
+ Routines to compress and uncompress tcp packets (for transmission
+ over low speed serial lines).
+ 
+ Copyright (c) 1989 Regents of the University of California.
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by the University of California, Berkeley.  The name of the
+ University may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ 
+ 	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ 	- Initial distribution.
+ 
+ 
+ 	modified for KA9Q Internet Software Package by
+ 	Katie Stevens (dkstevens@ucdavis.edu)
+ 	University of California, Davis
+ 	Computing Services
+  		- 01-31-90	initial adaptation (from 1.19)
+  		PPP.05	02-15-90 [ks]
+  		PPP.08	05-02-90 [ks]	use PPP protocol field to signal
+ 					compression
+  		PPP.15	09-90	 [ks]	improve mbuf handling
+  		PPP.16	11-02	 [karn]	substantially rewritten to use
+ 					NOS facilities
+ 
+ 	- Feb 1991	Bill_Simpson@um.cc.umich.edu
+  		variable number of conversation slots
+  		allow zero or one slots
+  		separate routines
+  		status display
+ 
+ 	Chuck Hedrick <hedrick@klinzhai.rutgers.edu>
+  		Adapted for Linux NET-1.
+ 
+ 	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ 		Adapted for Linux NET-2, cleanup and
+ 		integration into the Linux SLIP driver.
+ 
*** original/net/drv/slip/FAQ	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/FAQ	Mon May  9 13:13:09 1994
***************
*** 0 ****
--- 1,136 ----
+ SLIP is Serial-Line IP.  It is an encapsulation of TCP/IP for asynch
+ lines.  Most commonly it's used as a way to connect a system to a
+ TCP/IP network using a modem.  With a complete implementation of SLIP,
+ your system can act as a full-scale Internet network host.  The only
+ difference in principle between a SLIP connection and Ethernet is that
+ a serial-line is normally a lot slower than an Ethernet.
+ 
+ You may run into PPP ("Point to Point Protocol").  This is a newer
+ protocol than SLIP.  It's somewhat more general-purpose: it can
+ support synchronous lines as well as async, and it can support
+ protocols other than TCP/IP.  It can be used for the same things as
+ SLIP.  But because it's more general, it is also a lot more complex
+ than SLIP.  (Many of us think it's a typical committee product.)  I
+ don't know of any PPP's for Linux, though someone may have a newer
+ version of KA9Q that supports PPP.
+ 
+ [
+ 	There is a group of people working on PPP for Linux - FvK
+ ]
+ 
+ There are at least two implementations of SLIP for Linux.  One has
+ SLIP code that works with the normal Linux kernel-based TCP/IP.  If
+ you use this, then all the normal TCP/IP utilities will work over
+ SLIP.  I believe as of the net-2 release (with 0.99pl10), this is the
+ preferred version.  The file slip.instructions in this directory give
+ instructions on how to set up SLIP with the 0.99pl10 kernel and the
+ net2 utilities.
+ 
+ [
+ 	This file will be merged into the NET-2 FAQ asap - FvK
+ ]
+ 
+ The other implementation of SLIP is based on KA9Q.  KA9Q is an
+ implementation of TCP/IP originally written for MS/DOS, to support
+ amateur radio use of TCP/IP.  It has now been ported to Linux and
+ other Unix-like systems, as well as some other micros.  It is
+ available for educational and personal non-commercial use.  I am
+ responsible for one port of KA9Q to Linux.  It is on tsx-11.mit.edu,
+ as binaries/usr.bin/ka9qbin.N.tar.Z for binary and
+ sources/usr.bin/ka9qsrc.N.tar.Z for source.  N is a version number.  I
+ believe there are ports of newer versions of KA9Q, which may have more
+ features.  I haven't tried any of them and so can't say anything about
+ them.
+ 
+ ka9qbin.N.tar.Z contains a READ.ME that explains how to install it.
+ Installation of KA9Q -- or any other version of SLIP or PPP -- will
+ probably require either some understanding of TCP/IP or help from your
+ local network support staff.  That's frustrating to some people,
+ because Linux users typically want to be self-supporting.
+ Unfortunately SLIP by its nature requires cooperation with somebody
+ else.  Its purpose is to tie you into a network.  Normally that
+ network is run by a campus or corporate group.  You're going to need
+ them to set things up so that you can connect via SLIP, and you're
+ going to need some information from them in order to be able to
+ configure KA9Q.
+ 
+ If you control the machine at the other end, in theory you can put
+ SLIP up there yourself.  There are SLIP implementations for most
+ versions of Unix, and I believe Multinet for VMS also supports it.
+ (However don't ask me where to get them, because I don't know.)
+ However even in this case, you should still coordinate with the group
+ that runs your network.  You'll have to configure the machine running
+ SLIP as a router.  Many network managers will tend to get violent if a
+ user suddenly starts running an unauthorized router.
+ 
+ Here are the strengths and weaknesses of this version of KA9Q:
+ 
+  strengths:
+    reliability -- I have fixed several problems, including bugs
+ 	in the generic header compression code from Berkeley.
+    performance -- it's been carefully tuned.  If the system you
+ 	are talking to supports header compression, response at
+ 	9600 or faster feels as good via SLIP as with Kermit or
+ 	other normal communications packages.  At 2400 it's 
+ 	slow, but usable, as long as you have header compression.
+ 	Without header compression, don't even think about 
+ 	speeds below 9600.
+    telnet implements a number of newer features, so ^S and ^Q
+ 	are interpreted locally, and ^C and ^O stop output
+ 	fairly quickly.  This of course assumes that you are
+ 	running a current telnet daemon on the other end.  (Many
+ 	vendors, including Sun, supply versions of the telnet daemon
+ 	that are years out of date.)  Telnet lets you type special
+ 	telnet commands using ^^ followed by a letter.  (It is
+ 	compatible with the telnet in Cisco's terminal servers.)
+ 	It does local line editing when talking to IBM systems.
+ 	In general I prefer its user interface and general
+ 	operation over Berkeley's telnet.
+ 
+  weaknesses:
+    everything is packaged in a single executable.  You can have
+ 	several telnet sessions, ftp sessions, etc., but they'll
+ 	all be part of one program.  You can get back to command
+ 	level in that program, switch between sessions, close
+ 	a session etc.  But what you can't do is open a bunch
+ 	of X windows and run separate copies of FTP in each
+ 	window.  (There are two exceptions:  (1) It does support
+ 	a separate telnet program.  You can open multiple windows
+ 	and run a different copy of telnet in each.  (2) It
+ 	supports X.  So if you start up xterm or any other X
+ 	client on a remote system, you will get a separate X window.)
+ 	This is not intrinsic to the KA9Q architecture.  It is possible 
+ 	to do a server implementation that would support multiple 
+ 	applications. I haven't done that because I thought we were going to
+ 	have a "real" kernel-based implementation shortly.
+    there's only a limited number of services: telnet, ftp, X,
+ 	finger, ping.  I doubt that mail or other things (even
+ 	when present) actually work.
+    there's almost no incoming services.  ftp works OK.  finger
+ 	is sort of there, though not quite the way you might
+ 	expect.  Incoming telnet can be configured to either
+ 	get you into a talk-like link or actually start up a
+ 	login shell.
+ 
+ In general, this code is well suited to the needs of a single-user
+ system, but not a server or multi-user system.
+ 
+ I am spending almost no time doing anything with KA9Q, because I
+ assume it will be replaced by the kernel TCP/IP shortly.  (However
+ it's taking much longer for the kernel TCP/IP to stabilize than I had
+ hoped.)  Someone is apparently planning continuing support of a newer
+ version of KA9Q for its amateur radio services.
+ 	
+ When installing SLIP (or any other TCP/IP software, for that matter),
+ you may find it useful to have a general introduction to TCP/IP.  I
+ have written two papers, which when taken together give you much of
+ the information you would need to configure a TCP/IP-based host or
+ network.  They are available via anonymous FTP at cs.rutgers.edu, as
+ runet/tcp-ip-intro.doc and .ps, and tcp-ip-admin.doc and .ps.  .doc is
+ straight text.  .ps is Postscript.  These papers have been fairly
+ widely distributed.  (I recently noticed one of them on the SunSolve
+ CDROM, without a titlepage that would show where it came from -- the
+ only signs of my authorship are in a footnote about 1/3 of the way
+ through the document.  And Sun has added a Sun copyright notice...)
+ 
+ 
*** original/net/drv/slip/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/Makefile	Mon May  9 13:13:09 1994
***************
*** 0 ****
--- 1,36 ----
+ #
+ # Makefile for the Linux SLIP device driver.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ OBJS	= main.o utils.o ioctl.o packet.o encap.o compress.o
+ 
+ .c.s:
+ 		$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 		$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 		$(AS) -o $*.o $<
+ 
+ slip.o:		$(OBJS)
+ 		ld -r -o slip.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/drv/slip/compress.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/compress.c	Mon May  9 13:13:09 1994
***************
*** 0 ****
--- 1,805 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Van Jacobsen Header Compression functions.
+  *
+  * Version:	@(#)compress.c	1.7.1	04/25/94
+  *
+  * Authors:	Van Jacobson, <van@helios.ee.lbl.gov>, Dec 31, 1989
+  *		See the `COPYRIGHT' file in this directory for the
+  *		legal status and version info of this code.
+  *
+  *		Katie Stevens, <dkstevens@ucdavis.edu>, 01-31-90
+  *		Phil Karn, <karn@ka9q.ampr.org>, 11-02-90
+  *		Bill Simpson, <Bill_Simpson@um.cc.umich.edu>, 02-91
+  *		Chuck Hedrick, <hedrick@klinzhai.rutgers.edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Jim Seagrave, <jes@grendel.demon.co.uk>
+  *		Steve Bour, <jsbour@ualr.edu>
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in_systm.h>
+ #include <linux/in.h>		/* for IPPROTO_TCP, ntohs() et al */
+ #include <linux/if.h>
+ #include <linux/ip.h>
+ #include <linux/tty.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "slip.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ /* Put a short in host order into a char array in network order. */
+ static unsigned char *
+ put16(unsigned char *cp, unsigned short int x)
+ {
+   *cp++ = (x >> 8);
+   *cp++ = x;
+ 
+   return(cp);
+ }
+ 
+ 
+ /* Pull a 16-bit integer in host order from buffer in network byte order. */
+ static short int
+ pull16(unsigned char **cpp)
+ {
+   short int rval;
+ 
+   rval = *(*cpp)++;
+   rval <<= 8;
+   rval |= *(*cpp)++;
+   return(rval);
+ }
+ 
+ 
+ /* Encode a number. */
+ static unsigned char *
+ encode(unsigned char *cp, unsigned int n)
+ {
+   if (n >= 256 || n == 0) {
+ 	*cp++ = 0;
+ 	cp = put16(cp, n);
+   } else {
+ 	*cp++ = n;
+   }
+   return(cp);
+ }
+ 
+ 
+ /* Decode a number. */
+ static long int
+ decode(unsigned char **cpp)
+ {
+   long int x;
+ 
+   x = (long int) *(*cpp)++;
+   if (x == 0) {
+ 	return(pull16(cpp) & 0xFFFF);	/* pull16 returns -1 on error */
+   } else {
+ 	return(x & 0xFF);		/* -1 if PULLCHAR returned error */
+   }
+ }
+ 
+ 
+ /* Dump a VJ header. */
+ #ifdef SLIP_DEBUG
+ void
+ slhc_print(unsigned char *cp, int len)
+ {
+   unsigned char *ptr;
+   short int csum;
+ 
+   ptr = (cp + 2);
+   csum = pull16(&ptr);
+   printk("CSLIP: type=0x%02X changes=0x%02X sum=0x%X len=%d\n",
+ 		(*cp & 0377), (*(cp + 1) & 0377), (int) csum, len);
+ }
+ #endif	/* SLIP_DEBUG */
+ 
+ 
+ /*
+  * This routine does all the checksum computations that don't
+  * require anything special (like copying or special headers.)
+  */
+ static unsigned short
+ slhc_ip_compute_csum(unsigned char *buff, int len)
+ {
+   unsigned long sum = 0;
+ 
+   /* Do the first multiple of 4 bytes and convert to 16 bits. */
+   if (len > 3) {
+ 	__asm__("clc\n"
+ 	        "1:\t"
+ 	    	"lodsl\n\t"
+ 	    	"adcl %%eax, %%ebx\n\t"
+ 	    	"loop 1b\n\t"
+ 	    	"adcl $0, %%ebx\n\t"
+ 	    	"movl %%ebx, %%eax\n\t"
+ 	    	"shrl $16, %%eax\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum) , "=S" (buff)
+ 	        : "0" (sum), "c" (len >> 2) ,"1" (buff)
+ 	        : "ax", "cx", "si", "bx" );
+   }
+   if (len & 2) {
+ 	__asm__("lodsw\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum), "=S" (buff)
+ 	        : "0" (sum), "1" (buff)
+ 	        : "bx", "ax", "si");
+   }
+   if (len & 1) {
+ 	__asm__("lodsb\n\t"
+ 	    	"movb $0, %%ah\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum), "=S" (buff)
+ 	        : "0" (sum), "1" (buff)
+ 	        : "bx", "ax", "si");
+   }
+   sum =~sum;
+   return(sum & 0xFFFF);
+ }
+ 
+ 
+ /* Check the header of an incoming IP datagram. */
+ static int
+ slhc_ip_csum(struct ip *iph)
+ {
+   if (slhc_ip_compute_csum((unsigned char *)iph,
+ 		      (iph->ip_hl * sizeof(unsigned long))) == 0) return(0);
+   return(1);
+ }
+ 
+ 
+ void
+ slhc_i_status(struct slcompress *comp)
+ {
+   if (comp != NULL) {
+ 	printk("\t%ld Cmp, %ld Uncmp, %ld Bad, %ld Tossed\n",
+ 		comp->sl->stats.sls_i_compressed,
+ 		comp->sl->stats.sls_i_uncompressed,
+ 		comp->sl->stats.sls_i_error,
+ 		comp->sl->stats.sls_i_tossed);
+   }
+ }
+ 
+ 
+ void
+ slhc_o_status(struct slcompress *comp)
+ {
+   if (comp != NULL) {
+ 	printk("\t%ld Cmp, %ld Uncmp, %ld AsIs, %ld NotTCP\n",
+ 		comp->sl->stats.sls_o_compressed,
+ 		comp->sl->stats.sls_o_uncompressed,
+ 		comp->sl->stats.sls_o_tcp,
+ 		comp->sl->stats.sls_o_nontcp);
+ 	printk("\t%10ld Searches, %10ld Misses\n",
+ 		comp->sl->stats.sls_o_searches,
+ 		comp->sl->stats.sls_o_misses);
+   }
+ }
+ 
+ 
+ int
+ slhc_toss(struct slcompress *comp)
+ {
+   if (comp == NULL) return(0);
+ 
+   comp->flags |= SLF_TOSS;
+   return(0);
+ }
+ 
+ 
+ /* Free a compression data structure. */
+ void
+ slhc_free(struct slcompress *comp)
+ {
+   if (comp == NULL) return;
+ 
+   if (comp->rstate != NULL) kfree(comp->rstate);
+ 
+   if (comp->tstate != NULL) kfree(comp->tstate);
+ 
+   kfree_s(comp, sizeof(struct slcompress));
+ }
+ 
+ 
+ /*
+  * Initialize compression data structure
+  * 'slots' must be in range 0 to 255 (zero meaning no compression)
+  */
+ struct slcompress *
+ slhc_init(struct slip *sl, int rslots, int tslots)
+ {
+   struct slcompress *comp;
+   struct cstate *ts;
+   short i;
+ 
+   comp = (struct slcompress *) kmalloc(sizeof(struct slcompress), GFP_KERNEL);
+   if (comp == NULL) return(NULL);
+ 
+   memset(comp, 0, sizeof(struct slcompress));
+   if (rslots > 0 && rslots < 256) {
+ 	comp->rstate = (struct cstate *)
+ 			kmalloc(rslots * sizeof(struct cstate), GFP_KERNEL);
+ 	if (comp->rstate == NULL) return(NULL);
+ 	memset(comp->rstate, 0, rslots * sizeof(struct cstate));
+ 	comp->rslot_limit = rslots - 1;
+   }
+ 
+   if (tslots > 0 && tslots < 256) {
+ 	comp->tstate = (struct cstate *)
+ 			kmalloc(tslots * sizeof(struct cstate), GFP_KERNEL);
+ 	if (comp->tstate == NULL) return(NULL);
+ 	memset(comp->tstate, 0, rslots * sizeof(struct cstate));
+ 	comp->tslot_limit = tslots - 1;
+   }
+   comp->xmit_oldest = 0;
+   comp->xmit_current = 255;
+   comp->recv_current = 255;
+   comp->sl = sl;
+ 
+   /*
+    * don't accept any packets with implicit index until we get
+    * one with an explicit index.  Otherwise the uncompress code
+    * will try to use connection 255, which is almost certainly
+    * out of range
+    */
+   comp->flags |= SLF_TOSS;
+ 
+   if (tslots > 0) {
+ 	ts = comp->tstate;
+ 	for(i = comp->tslot_limit; i > 0; --i) {
+ 		ts[i].connid = i;
+ 		ts[i].next = &(ts[i - 1]);
+ 	}
+ 	ts[0].next = &(ts[comp->tslot_limit]);
+ 	ts[0].connid = 0;
+   }
+   return(comp);
+ }
+ 
+ 
+ /* 
+  * icp and isize are the original packet.
+  * ocp is a place to put a copy if necessary.
+  * cpp is initially a pointer to icp.  If the copy is used,
+  *    change it to ocp.
+  */
+ int
+ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
+ 	      unsigned char *ocp, unsigned char **cpp, int compress_cid)
+ 
+ {
+   unsigned char new_seq[16];
+   struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
+   struct cstate *lcs = ocs;
+   struct cstate *cs = lcs->next;
+   unsigned char *cp = new_seq;
+   struct tcphdr *th, *oth;
+   struct ip *ip;
+   unsigned long deltaS, deltaA;
+   short changes = 0;
+   int hlen;
+ 
+   ip = (struct ip *) icp;
+ 
+   /* Bail if this packet isn't TCP, or is an IP fragment */
+   if (ip->ip_p != IPPROTO_TCP ||
+       (ntohs(ip->ip_off) & IP_OFFSET) || (ip->ip_off & 32)) {
+ 	DPRINTF(("comp: noncomp 1 %d %d %d\n", ip->ip_p, 
+ 					ntohs(ip->ip_off), ip->ip_off));
+ 	/* Send as regular IP */
+ 	if (ip->ip_p != IPPROTO_TCP) comp->sl->stats.sls_o_nontcp++;
+ 	  else comp->sl->stats.sls_o_tcp++;
+ 	return(isize);
+   }
+ 
+   /* Extract TCP header */
+   th = (struct tcphdr *)
+ 	(((unsigned char *)ip) + (ip->ip_hl * sizeof(unsigned long)));
+   hlen = (ip->ip_hl * sizeof(unsigned long)) +
+ 	 (th->doff * sizeof(unsigned long));
+ 
+   /*
+    * Bail if the TCP packet isn't `compressible' (i.e.,
+    * ACK isn't set or some other control bit is set).
+    */
+   if (th->syn || th->fin || th->rst || !(th->ack)) {
+ 	DPRINTF(("comp: noncomp 2 %x %x %d %d %d %d\n", ip, th, 
+ 					th->syn, th->fin, th->rst, th->ack));
+ 	/* TCP connection stuff; send as regular IP */
+ 	comp->sl->stats.sls_o_tcp++;
+ 	return(isize);
+   }
+ 
+   /*
+    * Packet is compressible -- we're going to send either a COMPRESSED_TCP
+    * or UNCOMPRESSED_TCP packet.  Either way, we need to locate (or create)
+    * the connection state.
+    *
+    * States are kept in a circularly linked list with xmit_oldest pointing
+    * to the end of the list.  The list is kept in lru order by moving a
+    * state to the head of the list whenever it is referenced.  Since the
+    * list is short and, empirically, the connection we want is almost al-
+    * ways near the front, we locate states via linear search.  If we don't
+    * find a state for the datagram, the oldest state is (re-)used.
+    */
+   for ( ; ; ) {
+ 	if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr &&
+ 	    ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr &&
+ 	    th->source == cs->cs_tcp.source &&
+ 	    th->dest == cs->cs_tcp.dest) goto found;
+ 
+ 	/* If current equal oldest, at end of list. */
+ 	if (cs == ocs) break;
+ 	lcs = cs;
+ 	cs = cs->next;
+ 	comp->sl->stats.sls_o_searches++;
+   };
+ 
+   /*
+    * Didn't find it -- re-use oldest cstate.  Send an
+    * uncompressed packet that tells the other side what
+    * connection number we're using for this conversation.
+    *
+    * Note that since the state list is circular, the oldest
+    * state points to the newest and we only need to set
+    * xmit_oldest to update the lru linkage.
+    */
+   comp->sl->stats.sls_o_misses++;
+   comp->xmit_oldest = lcs->connid;
+   DPRINTF(("comp: not found\n"));
+   goto uncompressed;
+ 
+ found:
+   /* Found it -- move to the front on the connection list. */
+   if (lcs == ocs) {
+ 	/* found at most recently used */
+   } else if (cs == ocs) {
+ 	/* found at least recently used */
+ 	comp->xmit_oldest = lcs->connid;
+   } else {
+ 	/* more than 2 elements */
+ 	lcs->next = cs->next;
+ 	cs->next = ocs->next;
+ 	ocs->next = cs;
+   }
+ 
+   /*
+    * Make sure that only what we expect to change changed.
+    * Check the following:
+    * IP protocol version, header length & type of service.
+    * The "Don't fragment" bit.
+    * The time-to-live field.
+    * The TCP header length.
+    * IP options, if any.
+    * TCP options, if any.
+    * If any of these things are different between the previous &
+    * current datagram, we send the current datagram `uncompressed'.
+    */
+   oth = &cs->cs_tcp;
+ 
+   if (
+       ip->ip_v != cs->cs_ip.ip_v ||
+       ip->ip_hl != cs->cs_ip.ip_hl ||
+       ip->ip_tos != cs->cs_ip.ip_tos ||
+       (ip->ip_off & 64) != (cs->cs_ip.ip_off & 64) ||
+       ip->ip_ttl != cs->cs_ip.ip_ttl ||
+       th->doff != cs->cs_tcp.doff ||
+       (ip->ip_hl > 5 &&
+        memcmp(ip + 1, cs->cs_ipopt, ((ip->ip_hl) - 5) * sizeof(unsigned long))
+ 									!= 0) ||
+       (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) *
+ 						sizeof(unsigned long) != 0))
+     ) {
+ 	DPRINTF(("comp: incompat\n"));
+ 	goto uncompressed;
+   }
+ 	
+   /*
+    * Figure out which of the changing fields changed.  The
+    * receiver expects changes in the order: urgent, window,
+    * ack, seq (the order minimizes the number of temporaries
+    * needed in this section of code).
+    */
+   if (th->urg) {
+ 	deltaS = ntohs(th->urg_ptr);
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_U;
+   } else if (th->urg_ptr != oth->urg_ptr) {
+ 	/*
+ 	 * Argh! URG not set but urp changed -- a sensible
+ 	 * implementation should never do this but RFC793
+ 	 * doesn't prohibit the change so we have to deal
+ 	 * with it.
+ 	 */
+ 	DPRINTF(("comp: urg incompat\n"));
+ 	goto uncompressed;
+   }
+   if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) {
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_W;
+   }
+   if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) {
+ 	if (deltaA > 0x0000FFFF) goto uncompressed;
+ 	cp = encode(cp, deltaA);
+ 	changes |= NEW_A;
+   }
+   if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) {
+ 	if (deltaS > 0x0000FFFF) goto uncompressed;
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_S;
+   }
+ 	
+   switch(changes) {
+ 	case 0:
+ 		/*
+ 		 * Nothing changed. If this packet contains data and the
+ 		 * last one didn't, this is probably a data packet following
+ 		 * an ack (normal on an interactive connection) and we send
+ 		 * it compressed.  Otherwise it's probably a retransmit,
+ 		 * retransmitted ack or window probe.  Send it uncompressed
+ 		 * in case the other side missed the compressed version.
+ 		 */
+ 		if (ip->ip_len != cs->cs_ip.ip_len &&
+ 		    ntohs(cs->cs_ip.ip_len) == hlen) break;
+ 		DPRINTF(("comp: retrans\n"));
+ 		goto uncompressed;
+ 		break;
+ 
+ 	case SPECIAL_I:
+ 	case SPECIAL_D:
+ 		/*
+ 		 * Actual changes match one of our special case
+ 		 * encodings -- send packet uncompressed.
+  		 */
+ 		DPRINTF(("comp: special\n"));
+ 		goto uncompressed;
+ 
+ 	case NEW_S|NEW_A:
+ 		if (deltaS == deltaA &&
+ 		    deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ 			/* Special case for echoed terminal traffic. */
+ 			changes = SPECIAL_I;
+ 			cp = new_seq;
+ 		}
+ 		break;
+ 
+ 	case NEW_S:
+ 		if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ 			/* Special case for data xfer. */
+ 			changes = SPECIAL_D;
+ 			cp = new_seq;
+ 		}
+ 		break;
+   }
+ 
+   deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
+   if (deltaS != 1) {
+ 	cp = encode(cp, deltaS);
+ 	changes |= NEW_I;
+   }
+   if (th->psh) changes |= TCP_PUSH_BIT;
+ 
+   /*
+    * Grab the cksum before we overwrite it below.  Then
+    * update our state with this packet's header.
+    */
+   deltaA = ntohs(th->check);
+   memcpy(&cs->cs_ip, ip, sizeof(struct ip));		/* FIXME: 20 */
+   memcpy(&cs->cs_tcp, th, sizeof(struct tcphdr));	/* FIXME: 20 */
+ 
+   /*
+    * We want to use the original packet as our compressed packet.
+    *
+    * (cp - new_seq) is the number of bytes we need for compressed
+    * sequence numbers.  In addition we need one byte for the change
+    * mask, one for the connection id and two for the tcp checksum.
+    * So, (cp - new_seq) + 4 bytes of header are needed.
+    */
+   deltaS = cp - new_seq;
+   if (compress_cid == 0 || comp->xmit_current != cs->connid) {
+ 	cp = ocp;
+ 	*cpp = ocp;
+ 	*cp++ = changes | NEW_C;
+ 	*cp++ = cs->connid;
+ 	comp->xmit_current = cs->connid;
+   } else {
+ 	cp = ocp;
+ 	*cpp = ocp;
+ 	*cp++ = changes;
+   }
+   cp = put16(cp, (short)deltaA);	/* Write TCP checksum */
+ 
+   /* deltaS is now the size of the change section of the compressed header. */
+   DPRINTF(("comp: %x %x %x %d %d\n", icp, cp, new_seq, hlen, deltaS));
+   memcpy(cp, new_seq, deltaS);	/* Write list of deltas */
+   memcpy(cp + deltaS, icp + hlen, isize - hlen);
+   comp->sl->stats.sls_o_compressed++;
+   ocp[0] |= SL_TYPE_COMP_TCP;
+   return(isize - hlen + deltaS + (cp - ocp));
+ 
+ uncompressed:
+   /*
+    * Update connection state cs & send uncompressed packet (i.e.,
+    * a regular ip/tcp packet but with the 'conversation id' we hope
+    * to use on future compressed packets in the protocol field).
+    */
+   memcpy(&cs->cs_ip, ip, sizeof(struct ip));		/* FIXME: 20 */
+   memcpy(&cs->cs_tcp, th, sizeof(struct tcphdr));	/* FIXME: 20 */
+   if (ip->ip_hl > 5)
+ 	memcpy(cs->cs_ipopt, ip+1, ((ip->ip_hl) - 5) * sizeof(unsigned long));
+   if (th->doff > 5)
+ 	memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * sizeof(unsigned long));
+   comp->xmit_current = cs->connid;
+   comp->sl->stats.sls_o_uncompressed++;
+   memcpy(ocp, icp, isize);
+   *cpp = ocp;
+   ocp[9] = cs->connid;
+   ocp[0] |= SL_TYPE_UNCOMP_TCP;
+   return(isize);
+ }
+ 
+ 
+ int
+ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
+ {
+   struct tcphdr *thp;
+   struct ip *ip;
+   struct cstate *cs;
+   unsigned char *cp = icp;
+   int changes;
+   int len, hdrlen;
+   long x;
+ 
+   /* We've got a compressed packet; read the change byte. */
+   comp->sl->stats.sls_i_compressed++;
+   if (isize < 3) {
+ 	comp->sl->stats.sls_i_error++;
+ 	DPRINTF(("uncomp: runt\n"));
+ 	return(0);
+   }
+   changes = *cp++;
+   if (changes & NEW_C) {
+ 	/*
+ 	 * Make sure the state index is in range, then grab the state.
+ 	 * If we have a good state index, clear the 'discard' flag.
+ 	 */
+ 	x = *cp++;	/* Read conn index */
+ 	if (x < 0 || x > comp->rslot_limit) goto bad;
+ 
+ 	comp->flags &=~ SLF_TOSS;
+ 	comp->recv_current = x;
+   } else {
+ 	/*
+ 	 * This packet has an implicit state index.  If
+ 	 * we've had a line error since the last time we
+ 	 * got an explicit state index, we have to toss
+ 	 * the packet.
+ 	 */
+ 	if (comp->flags & SLF_TOSS) {
+ 		comp->sl->stats.sls_i_tossed++;
+ 		DPRINTF(("uncomp: toss\n"));
+ 		return(0);
+ 	}
+   }
+   cs = &comp->rstate[comp->recv_current];
+   thp = &cs->cs_tcp;
+   ip = &cs->cs_ip;
+ 
+   if ((x = pull16(&cp)) == -1) {	/* Read the TCP checksum */
+ 	DPRINTF(("uncomp: bad tcp chk\n"));
+ 	goto bad;
+   }
+   thp->check = htons(x);
+   thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
+ 
+   /* 
+    * we can use the same number for the length of the saved header and
+    * the current one, because the packet wouldn't have been sent
+    * as compressed unless the options were the same as the previous one
+    */
+   hdrlen = (ip->ip_hl * sizeof(unsigned long)) +
+ 	   (thp->doff * sizeof(unsigned long));
+   switch(changes & SPECIALS_MASK) {
+ 	case SPECIAL_I:		/* Echoed terminal traffic */
+ 		{
+ 			short i;
+ 
+ 			i = ntohs(ip->ip_len) - hdrlen;
+ 			thp->ack_seq = htonl(ntohl(thp->ack_seq) + i);
+ 			thp->seq = htonl(ntohl(thp->seq) + i);
+ 		}
+ 		break;
+ 
+ 	case SPECIAL_D:		/* Unidirectional data */
+ 		thp->seq = htonl(ntohl(thp->seq) +
+ 				 ntohs(ip->ip_len) - hdrlen);
+ 		break;
+ 
+ 	default:
+ 		if (changes & NEW_U) {
+ 			thp->urg = 1;
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad U\n"));
+ 				goto bad;
+ 			}
+ 			thp->urg_ptr = htons(x);
+ 		} else {
+ 			thp->urg = 0;
+ 		}
+ 		if (changes & NEW_W) {
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad W\n"));
+ 				goto bad;
+ 			}	
+ 			thp->window = htons(ntohs(thp->window) + x);
+ 		}
+ 		if (changes & NEW_A) {
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad A\n"));
+ 				goto bad;
+ 			}
+ 			thp->ack_seq = htonl(ntohl(thp->ack_seq) + x);
+ 		}
+ 		if (changes & NEW_S) {
+ 			if ((x = decode(&cp)) == -1) {
+ 				DPRINTF(("uncomp: bad S\n"));
+ 				goto bad;
+ 			}
+ 			thp->seq = htonl(ntohl(thp->seq) + x);
+ 		}
+ 		break;
+   }
+ 
+   if (changes & NEW_I) {
+ 	if ((x = decode(&cp)) == -1) {
+ 		DPRINTF(("uncomp: bad I\n"));
+ 		goto bad;
+ 	}
+ 	ip->ip_id = htons(ntohs(ip->ip_id) + x);
+   } else {
+ 	ip->ip_id = htons(ntohs(ip->ip_id) + 1);
+   }
+ 
+   /*
+    * At this point, cp points to the first byte of data in the
+    * packet.  Put the reconstructed TCP and IP headers back on the
+    * packet.  Recalculate IP checksum (but not TCP checksum).
+    */
+   len = isize - (cp - icp);
+   if (len < 0) goto bad;
+   len += hdrlen;
+   ip->ip_len = htons(len);
+   ip->ip_sum = 0;
+   DPRINTF(("uncomp: %d %d %d %d\n", cp - icp, hdrlen, isize, len));
+ 
+   /*
+    * Note from ChuckH to FredK: "this routine'd better behave well
+    * when confronted with overlapping copies!" -FvK
+    */
+   memmove(icp + hdrlen, cp, len - hdrlen);
+ 
+   /* Copy in the IP header (and options). */
+   cp = icp;
+   memcpy(cp, ip, sizeof(struct ip));		/* FIXME: 20 */
+   cp += sizeof(struct ip);			/* FIXME: 20 */
+   if (ip->ip_hl > 5) {
+ 	memcpy(cp, cs->cs_ipopt, ((ip->ip_hl) - 5) * sizeof(unsigned long));
+ 	cp += ((ip->ip_hl) - 5) * sizeof(unsigned long);
+   }
+ 
+ #if 1	/* FIXME: Check This Soon */
+   ip = (struct ip *) icp;
+   ip->ip_sum = slhc_ip_compute_csum((unsigned char *)ip,
+ 		      (ip->ip_hl * sizeof(unsigned long)));
+ #else
+   ((struct ip *)icp)->ip_sum = slhc_ip_csum((struct ip *)icp);
+ #endif
+ 
+   /* Copy in the TCP header (and options). */
+   memcpy(cp, thp, sizeof(struct tcphdr));	/* FIXME: 20 */
+   cp += sizeof(struct tcphdr);
+   if (thp->doff > 5) {
+ 	memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * sizeof(unsigned long));
+ 	cp += ((thp->doff) - 5) * sizeof(unsigned long);
+   }
+ 
+   DPRINTF(("\runcomp: change %x len %d\n", changes, len));
+   return(len);
+ 
+ bad:
+   comp->sl->stats.sls_i_error++;
+   return(slhc_toss(comp));
+ }
+ 
+ 
+ int
+ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
+ {
+   struct ip *ip;
+   struct tcphdr *thp;
+   struct cstate *cs;
+   unsigned char index;
+   short ip_len;
+ 
+   if (isize < sizeof(struct ip)) {
+ 	/* The packet is shorter than a legal IP header. */
+ 	comp->sl->stats.sls_i_runt++;
+ 	return(slhc_toss(comp));
+   }
+ 
+   /* Clear the IP header "version" bit field. */
+   icp[0] &= 0x4F;
+ 
+   /* Sneak a peek at the IP header's IHL field to find its length. */
+   ip_len = (icp[0] & 0x0F) << 2;
+   if (ip_len < sizeof(struct ip)) {
+ 	/* The IP header length field is too small. */
+ 	comp->sl->stats.sls_i_runt++;
+ 	return(slhc_toss(comp));
+   }
+   index = icp[9];
+   icp[9] = IPPROTO_TCP;
+   ip = (struct ip *) icp;
+ 
+   if (slhc_ip_csum(ip)) {
+ 	/* Bad IP header checksum; discard. */
+ 	comp->sl->stats.sls_i_badcheck++;
+ 	return(slhc_toss(comp));
+   }
+ 
+   thp = (struct tcphdr *)
+ 	(((unsigned char *)ip) + (ip->ip_hl * sizeof(unsigned long)));
+   if (index > comp->rslot_limit) {
+ 	comp->sl->stats.sls_i_error++;
+ 	return(slhc_toss(comp));
+   }
+ 
+   /* Update local state. */
+   cs = &comp->rstate[comp->recv_current = index];
+   comp->flags &=~ SLF_TOSS;
+ 
+   /* Save IP header (plus options). */
+   memcpy(&cs->cs_ip, ip, sizeof(struct ip));		/* FIXME: 20 */
+   if (ip->ip_hl > 5)
+ 	memcpy(cs->cs_ipopt, ip+1, ((ip->ip_hl) - 5) * sizeof(unsigned long));
+ 
+   /* Save TCP header (plus options). */
+   memcpy(&cs->cs_tcp, thp, sizeof(struct tcphdr));	/* FIXME: 20 */
+   if (thp->doff > 5)
+ 	memcpy(cs->cs_tcpopt, thp+1, ((thp->doff) - 5) * sizeof(unsigned long));
+ 
+   cs->cs_hsize = (ip->ip_hl * 2) + (thp->doff * 2);
+ 
+   /*
+    * Put headers back on packet.
+    * Neither header checksum is recalculated.
+    */
+   comp->sl->stats.sls_i_uncompressed++;
+ 
+   return(isize);
+ }
*** original/net/drv/slip/compress.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/compress.h	Mon May  9 13:13:09 1994
***************
*** 0 ****
--- 1,101 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Van Jacobsen Header Compression definitions.
+  *
+  * Version:	@(#)compress.h	1.7.0	04/02/94
+  *
+  * Authors:	Van Jacobson, <van@helios.ee.lbl.gov>, Dec 31, 1989
+  *		See the `COPYRIGHT' file in this directory for the
+  *		legal status and version info of this code.
+  *
+  *		Katie Stevens, <dkstevens@ucdavis.edu>, 01-31-90
+  *		Phil Karn, <karn@ka9q.ampr.org>, 11-02-90
+  *		Bill Simpson, <Bill_Simpson@um.cc.umich.edu>, 02-91
+  *		Chuck Hedrick, <hedrick@klinzhai.rutgers.edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Jim Seagrave, <jes@grendel.demon.co.uk>
+  */
+ #include <linux/ip.h>
+ #include <linux/ip_tcp.h>
+ 
+ 
+ /* SLIP compression masks for len/vers byte. */
+ #define SL_TYPE_ERROR		0x00
+ #define SL_TYPE_IP		0x40
+ #define SL_TYPE_UNCOMP_TCP	0x70
+ #define SL_TYPE_COMP_TCP	0x80
+ 
+ /* Bits in first octet of compressed packet. */
+ #define NEW_C		0x40			/* flag bits for what	*/
+ #define NEW_I		0x20			/* changed in a packet	*/
+ #define NEW_S		0x08
+ #define NEW_A		0x04
+ #define NEW_W		0x02
+ #define NEW_U		0x01
+ 
+ /* Reserved, special-case values of above. */
+ #define SPECIAL_I	(NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+ #define SPECIAL_D	(NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+ #define SPECIALS_MASK	(NEW_S|NEW_A|NEW_W|NEW_U)
+ 
+ #define TCP_PUSH_BIT	0x10
+ 
+ 
+ #define SLF_TOSS	0x01			/* keep tossing until id rcv */
+ 
+ 
+ /* All the state data for one serial line (we need one of these per line). */
+ struct slcompress {
+   struct cstate	*tstate;		/* transmit connection states	*/
+   struct cstate *rstate;		/* receive connection states	*/
+ 
+   unsigned char tslot_limit;		/* highest xmit slot id (0-l)	*/
+   unsigned char rslot_limit;		/* highest recv slot id (0-l)	*/
+ 
+   unsigned char	xmit_oldest;		/* oldest xmit in ring		*/
+   unsigned char xmit_current;		/* most recent xmit id		*/
+   unsigned char recv_current;		/* most recent rcvd id		*/
+ 
+   unsigned char	flags;
+ 
+   struct slip	*sl;			/* back-pointer to SLIP cb	*/
+ };
+ 
+ 
+ /*
+  * "state" data for each active tcp conversation on the wire.  This is
+  * basically a copy of the entire IP/TCP header from the last packet
+  * we saw from the conversation together with a small identifier
+  * the transmit & receive ends of the line use to locate saved header.
+  */
+ struct cstate {
+   unsigned char	connid;		/* connection id number (xmit)		*/
+   struct cstate	*next;		/* next in ring (xmit)			*/
+   struct ip	cs_ip;		/* IP hdr from most recent packet	*/
+   struct tcphdr	cs_tcp;		/* TCP hdr from most recent packet	*/
+   unsigned char	cs_ipopt[64];
+   unsigned char	cs_tcpopt[64];
+   int		cs_hsize;
+ };
+ 
+ 
+ extern struct slcompress	*slhc_init(struct slip *sl, int rslots,
+ 					   int tslots);
+ extern void			slhc_free(struct slcompress *comp);
+ extern int			slhc_compress(struct slcompress *comp,
+ 					      unsigned char *icp, int isize,
+ 					      unsigned char *ocp,
+ 					      unsigned char **cpp,
+ 					      int compress_cid);
+ extern int			slhc_uncompress(struct slcompress *comp,
+ 						unsigned char *icp, int isize);
+ extern int			slhc_remember(struct slcompress *comp,
+ 					      unsigned char *icp, int isize);
+ extern int			slhc_toss(struct slcompress *comp);
+ extern void			slhc_print(unsigned char *ptr, int len);
+ extern void			slhc_i_status(struct slcompress *comp);
+ extern void			slhc_o_status(struct slcompress *comp);
*** original/net/drv/slip/encap.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/encap.c	Mon May  9 13:13:09 1994
***************
*** 0 ****
--- 1,211 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Data encapsulation functions.
+  *
+  * Version:	@(#)encap.c	1.7.0	04/02/94
+  *
+  * Authors:	Laurence Culhane, <loz@holmes.demon.co.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Matt Dillon, <dillon@moonshot.west.oic.com>
+  *
+  *		Copyright 1992 Laurence Culhane
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_slip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include "slip.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ /*
+  * Encapsulate a frame of data using the standard
+  * 8-bit transparent SLIP protocol.
+  */
+ int
+ slip_encap(unsigned char *s, unsigned char *d, int len)
+ {
+   int count = 0;
+ 
+   /*
+    * Send an initial END character to flush out any
+    * data that may have accumulated in the receiver
+    * due to line noise.
+    */
+   d[count++] = END;
+ 
+   /*
+    * For each byte in the packet, send the appropriate
+    * character sequence, according to the SLIP protocol.
+    */
+   while(len-- > 0) {
+ 	switch(*s) {
+ 		case END:
+ 			d[count++] = ESC;
+ 			d[count++] = ESC_END;
+ 			break;
+ 
+ 		case ESC:
+ 			d[count++] = ESC;
+ 			d[count++] = ESC_ESC;
+ 			break;
+ 
+ 		default:
+ 			d[count++] = *s;
+ 	}
+ 	s++;
+   }
+ 
+   d[count++] = END;
+   return(count);
+ }
+ 
+ 
+ /*
+  * Un-encapsulate a frame of data using the
+  * standard 8-bit transparent SLIP protocol.
+  */
+ void
+ slip_unencap(struct slip *sl, unsigned char *s, int count)
+ {
+   int i;
+ 
+   for (i = 0; i < count; ++i, ++s) switch(*s) {
+ 	case ESC:
+ 		sl->flags |= SLF_ESCAPE;
+ 		break;
+ 
+ 	case ESC_ESC:
+ 		if (sl->flags & SLF_ESCAPE) sl_enqueue(sl, ESC);
+ 		  else sl_enqueue(sl, *s);
+ 		sl->flags &= ~SLF_ESCAPE;
+ 		break;
+ 
+ 	case ESC_END:
+ 		if (sl->flags & SLF_ESCAPE) sl_enqueue(sl, END);
+ 		  else sl_enqueue(sl, *s);
+ 		sl->flags &= ~SLF_ESCAPE;
+ 		break;
+ 
+ 	case END:
+ 		if (sl->rcount > 2) sl_bump(sl);
+ 		sl_dequeue(sl, sl->rcount);
+ 		sl->rcount = 0;
+ 		sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
+ 		break;
+ 
+ 	default:
+ 		sl_enqueue(sl, *s);
+ 		sl->flags &= ~SLF_ESCAPE;
+   }
+ }
+ 
+ 
+ /*
+  * Encapsulate a frame of data using the new 6-bit
+  * `printable ASCII' protocol, which can be used
+  * over terminal server and some X.25 links.
+  */
+ int
+ slip_encap6(unsigned char *s, unsigned char *d, int len)
+ {
+   unsigned short v = 0;
+   short bits = 0;
+   int i, count = 0;
+ 
+   /*
+    * Send an initial END character to flush out any
+    * data that may have accumulated in the receiver
+    * due to line noise.
+    */
+   d[count++] = 0x70;
+ 
+   /* Encode the packet into printable ascii characters. */
+   for (i = 0; i < len; ++i) {
+ 	v = (v << 8) | s[i];
+ 	bits += 8;
+ 	while (bits >= 6) {
+ 		unsigned char c;
+ 
+ 		bits -= 6;
+ 		c = 0x30 + ((v >> bits) & 0x3F);
+ 		d[count++] = c;
+ 	}
+   }
+ 
+   if (bits) {
+ 	unsigned char c;
+ 
+ 	c = 0x30 + ((v << (6 - bits)) & 0x3F);
+ 	d[count++] = c;
+   }
+ 
+   d[count++] = 0x70;
+   return(count);
+ }
+ 
+ 
+ /*
+  * Un-encapsulate a frame of data using the new
+  * 6-bit `printable ASCII' protocol, which can
+  * be used over terminal server and some X.25
+  * links.
+  */
+ void
+ slip_unencap6(struct slip *sl, unsigned char *s, int count)
+ {
+   unsigned char c;
+   int i, min;
+ 
+   min = (sl->flags & SLF_COMP) ? 2 : 8;
+   for (i = 0; i < count; ++i, ++s) {
+ 	if (*s == 0x70) {
+ 		if (sl->rcount > min) {
+ #ifdef NOTDEF
+ 			printk("rbuff %02x %02x %02x %02xn",
+ 				sl->rbuff[0], sl->rbuff[1],
+ 				sl->rbuff[2], sl->rbuff[3]);
+ #endif
+ 			sl_bump(sl);
+ 		}
+ 		sl_dequeue(sl, sl->rcount);
+ 		sl->rcount = 0;
+ 		sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
+ 					/* SLF_ESCAPE not used */
+ 		sl->xbits = 0;
+ 	} else if (*s >= 0x30 && *s < 0x70) {
+ 		sl->xdata = (sl->xdata << 6) | ((*s - 0x30) & 0x3F);
+ 		sl->xbits += 6;
+ 		if (sl->xbits >= 8) {
+ 			sl->xbits -= 8;
+ 			c = (unsigned char)(sl->xdata >> sl->xbits);
+ 			sl_enqueue(sl, c);
+ 		}
+ 	}
+   }
+ }
*** original/net/drv/slip/encap.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/encap.h	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,25 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Data encapsulation definitions.
+  *
+  * Version:	@(#)encap.h	1.7.0	04/02/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ 
+ 
+ extern int	slip_encap(unsigned char *s, unsigned char *d, int len);
+ extern void	slip_unencap(struct slip *sl, unsigned char *s, int count);
+ extern int	slip_encap6(unsigned char *s, unsigned char *d, int len);
+ extern void	slip_unencap6(struct slip *sl, unsigned char *s, int count);
*** original/net/drv/slip/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/global.h	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,123 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Define the SLIP device driver internal functions.
+  *
+  * Version:	@(#)global.h	1.7.0	04/02/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _SLIP_GLOBAL_H
+ #define _SLIP_GLOBAL_H
+ 
+ 
+ /* SLIP protocol characters. */
+ #define END             0300		/* indicates end of frame	*/
+ #define ESC             0333		/* indicates byte stuffing	*/
+ #define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
+ #define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
+ 
+ 
+ /* SLIP interface statistics. */
+ struct slip_stats {
+   unsigned long rx_octets_raw;		/* inbound raw data		*/
+   unsigned long	rx_octets;		/* and after decompression	*/
+   unsigned long	tx_octets;		/* outbound data (post-comp)	*/
+   unsigned long	tx_octets_raw;  	/* outbound data (pre-comp)	*/
+  
+   unsigned long	errors;			/* character error count	*/
+  
+   unsigned long sls_o_nontcp;		/* outbound non-TCP packets	*/
+   unsigned long sls_o_tcp;		/* outbound TCP packets		*/
+   unsigned long sls_o_uncompressed;	/* outbound uncompressed pkts	*/
+   unsigned long sls_o_compressed;	/* outbound compressed pkts	*/
+   unsigned long sls_o_searches;		/* searches for conn state	*/
+   unsigned long sls_o_misses;		/* conn state not found		*/
+  
+   unsigned long sls_i_not_compressed;
+   unsigned long sls_i_uncompressed;	/* inbound uncompressed pkts	*/
+   unsigned long sls_i_compressed;	/* inbound compressed pkts	*/
+   unsigned long sls_i_error;		/* inbound error pkts		*/
+   unsigned long sls_i_tossed;		/* inbound packets tossed	*/
+ 
+   unsigned long sls_i_runt;		/* packet too small		*/
+   unsigned long sls_i_badcheck;		/* packet checksum error	*/
+ };
+ 
+ 
+ #define SLF_INUSE	0x0001		/* "channel allocated"		*/
+ #define SLF_ESCAPE	0x0002		/* "SLIP_ESC seen"		*/
+ #define SLF_ERROR	0x0004		/* "frame/line error"		*/
+ #define SLF_COMP	0x0010		/* "compression active"		*/
+ #define SLF_EXPN	0x0020		/* "6-bit encap active"		*/
+ 
+ 
+ struct slip {
+   unsigned short	flags;		/* bitmap flags			*/
+ 
+   /* Various fields. */
+   int			line;		/* SLIP channel number		*/
+   struct tty_struct	*tty;		/* ptr to TTY structure		*/
+   struct ifnet		*dev;		/* easy for intr handling	*/
+   int			rbufsize;	/* size of RECV buffer		*/
+   int			xbufsize;	/* size of XMIT buffer		*/
+ 
+   /* VJ Header Compression ("CSLIP") control block. */
+   struct slcompress	*slcomp;
+ 
+   /* 6-bit EXPN encoding statii. */
+   int			xdata;		/* EXPN - printable encoding	*/
+   int			xbits;		/* EXPN - printable encoding	*/
+ 
+   /* Statistics. */
+   struct slip_stats	stats;		/* SLIP protocol statistics	*/
+ 
+   /* These are pointers to the malloc()ed frame buffers. */
+   unsigned char		*rbuff;		/* receiver buffer		*/
+   unsigned char		*xbuff;		/* transmitter buffer		*/
+   unsigned char		*cbuff;		/* compression buffer		*/
+ 
+   /* These are the various pointers into the buffers. */
+   unsigned char		*rhead;		/* RECV buffer pointer (head)	*/
+   unsigned char		*rend;		/* RECV buffer pointer (end)	*/
+   int			rcount;		/* SLIP receive counter		*/
+ };
+ 
+ 
+ extern int		slip_debug;	/* SLIP debug level flag	*/
+ extern struct slip	*sl_ctrl[];	/* SLIP control blocks		*/
+ 
+ 
+ extern void		sl_enqueue(struct slip *sl, unsigned char c);
+ extern void		sl_dequeue(struct slip *sl, int i);
+ extern void		sl_bump(struct slip *sl);
+ extern void		sl_free_buff(struct slip *sl);
+ extern int		sl_alloc_buff(struct slip *sl);
+ extern struct slip	*sl_find(struct tty_struct *tty);
+ extern int		slip_ioctl(struct inode *inode, struct file *file,
+ 				   unsigned int cmd, unsigned long arg);
+ extern void		slip_handler(struct tty_struct *tty);
+ extern int		slip_xmit(struct ifnet *dev, struct sk_buff *skb);
+ extern int		slip_recv(struct ifnet *dev, struct sk_buff *skb,
+ 				  int len);
+ extern void		slip_send(struct ifnet *dev, struct sk_buff *skb,
+ 				  struct sockaddr *target, int priority);
+ 
+ extern int		slip_getencap(struct slip *sl);
+ extern int		slip_setencap(struct slip *sl, int encap);
+ extern int		slip_dbg_ioctl(unsigned long arg, int level);
+ extern void		slip_print(unsigned char *ptr);
+ extern void		slip_dprintf(char *fmt, ...);
+ 
+ 
+ #endif	/* _SLIP_GLOBAL_H */
*** original/net/drv/slip/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/ioctl.c	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,189 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		SLIP I/O control request handler.
+  *
+  * Version:	@(#)ioctl.c	1.7.0	04/05/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/ip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "slip.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ /* Fetch the caller's info block. */
+ static int
+ slip_getioreq(unsigned long arg, int rw, struct ifreq *ptr)
+ {
+   int err;
+ 
+   if (rw == 1) {
+ 	err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct ifreq));
+ 	if (err < 0) return(err);
+ 	memcpy_tofs((char *) arg, ptr, sizeof(struct ifreq));
+ 	return(0);
+   }
+ 
+   /* We are reading a request from the caller. */
+   err = verify_area(VERIFY_READ, (char *) arg, sizeof(struct ifreq));
+   if (err < 0) return(err);
+   memcpy_fromfs(ptr, (char *) arg, sizeof(struct ifreq));
+ 
+   return(0);
+ }
+ 
+ 
+ /* Check if the new MTU setting is OK. */
+ static int
+ slip_check_mtu(struct ifnet *dev, int new_mtu)
+ {
+   struct slip *sl;
+ 
+   if ((new_mtu < 0) || (new_mtu >= 8000)) return(-EINVAL);
+   dev->if_mtu = new_mtu;
+   sl = sl_ctrl[dev->if_unit];
+ 
+   if (sl->rbuff || sl->xbuff) sl_free_buff(sl);
+ 
+   return(sl_alloc_buff(sl));
+ }
+ 
+ 
+ /* Get or set the current SLIP statistics. */
+ static int
+ slip_do_stats(struct inode *inode, unsigned long arg, unsigned cmd)
+ {
+   struct slip *sl;
+   int i;
+ 
+   i = MINOR(inode->i_rdev);
+   if (i < 0 || i >= SL_MAX_DEV) return(-ENODEV);
+   sl = sl_ctrl[i];
+ 
+   switch(cmd) {
+ 	case SIOCGIFSTATS:
+ 		i = verify_area(VERIFY_WRITE, (char *) arg,
+ 				sizeof(struct slip_stats));
+ 		if (i < 0) return(i);
+ 		memcpy_tofs((char *) arg, &sl->stats,
+ 			    sizeof(struct slip_stats));
+ 		return(0);
+ 
+ 	case SIOCSIFSTATS:
+ 		i = verify_area(VERIFY_READ, (char *) arg,
+ 				sizeof(struct slip_stats));
+ 		if (i < 0) return(i);
+ 		memcpy_fromfs(&sl->stats, (char *) arg,
+ 			    sizeof(struct slip_stats));
+ 		return(0);
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
+ 
+ 
+ /* Perform I/O control on an active SLIP channel. */
+ int
+ slip_ioctl(struct inode *inode, struct file *file,
+ 	   unsigned int cmd, unsigned long arg)
+ {
+   struct ifnet *dev;
+   struct ifreq req;
+   struct slip *sl;
+   int i;
+ 
+   DPRINTF(("SLIP: ioctl(0x%X, 0x%X, 0x%X)\n", inode, cmd, arg));
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		i = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (i < 0) return(i);
+ 		return(slip_dbg_ioctl(arg, 1));
+ 
+ 	case SIOCGIFNAME:
+ 		if ((sl = sl_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		i = verify_area(VERIFY_WRITE, (int *) arg,
+ 				(strlen(sl->dev->if_name) + 1));
+ 		if (i < 0) return(i);
+ 		memcpy_tofs((int *) arg, sl->dev->if_name,
+ 			    strlen(sl->dev->if_name) + 1);
+ 		return(0);
+ 
+ 	case SIOCSIFMTU:
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= SL_MAX_DEV) return(-ENODEV);
+ 		if (sl_ctrl[dev->if_unit] == NULL) return(-ENODEV);
+ 		return(slip_check_mtu(dev, (int) arg));
+ 
+ 	case SIOCGIFENCAP:		/* "get ENCAP mode"	*/
+ 		dev = (struct ifnet *) inode;
+ 		if (dev->if_unit >= SL_MAX_DEV) return(-ENODEV);
+ 		if ((sl = sl_ctrl[dev->if_unit]) == NULL) return(-ENODEV);
+ 
+ 		/* Get user IFREQ block. */
+ 		i = slip_getioreq(arg, 0, &req);
+ 		if (i < 0) return(i);
+ 
+ 		/* Copy the ENCAP value. */
+ 		req.ifr_encap = slip_getencap(sl);
+ 		
+ 		/* Send result back to user. */
+ 		i = slip_getioreq(arg, 1, &req);
+ 		return(i);
+ 
+ 	case SIOCSIFENCAP:		/* "set ENCAP mode"	*/
+ 		if (! suser()) return(-EPERM);
+ 		if ((sl = sl_find((struct tty_struct *) inode)) == NULL)
+ 								return(-EINVAL);
+ 		i = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (i < 0) return(i);
+ 		i = get_fs_long((int *) arg);
+ 		return(slip_setencap(sl, i));
+ 
+ 	case SIOCSIFSTATS:		/* "set stats" */
+ 		if (! suser()) return(-EPERM);
+ 		/*FALLTHROUGH*/
+ 
+ 	case SIOCGIFSTATS:		/* "get stats" */
+ 		return(slip_do_stats(inode, arg, cmd));
+ 
+ 	default:
+ 		break;
+   }
+   return(-EINVAL);
+ }
*** original/net/drv/slip/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/main.c	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,436 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Main program of the SLIP driver.
+  *
+  * Version:	@(#)main.c	1.7.0	04/02/94
+  *
+  * Authors:	Laurence Culhane, <loz@holmes.demon.co.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1992 Laurence Culhane
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_slip.h>
+ #include <linux/ip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "slip.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ #define	VERSION		"1.7.0"
+ 
+ 
+ static struct tty_ldisc	sl_ldisc;
+ struct slip		*sl_ctrl[SL_MAX_DEV];
+ 
+ 
+ /* Initialize a SLIP control block for use. */
+ static void
+ sl_initialize(struct slip *sl)
+ {
+   sl->flags = 0;
+ 
+   /* Clear all pointers. */
+   sl->slcomp = NULL;
+   sl->rbuff = NULL;
+   sl->rbufsize = 0;
+   sl->rhead = NULL;
+   sl->rend = NULL;
+   sl->rcount = 0;
+   sl->xbuff = NULL;
+   sl->xbufsize = 0;
+   sl->cbuff = NULL;
+ }
+ 
+ 
+ /* Find a SLIP channel from its `tty' link. */
+ struct slip *
+ sl_find(struct tty_struct *tty)
+ {
+   int i;
+ 
+   if (tty == NULL) return(NULL);
+   for (i = 0; i < SL_MAX_DEV; i++) {
+ 	if (sl_ctrl[i] == NULL) continue;
+ 	if (sl_ctrl[i]->tty == tty) return(sl_ctrl[i]);
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Find a free SLIP channel, and link in this `tty' line. */
+ static struct slip *
+ sl_alloc(void)
+ {
+   struct slip *sl;
+   int i;
+ 
+   for (i = 0; i < SL_MAX_DEV; i++) {
+ 	sl = sl_ctrl[i];
+ 	if (sl != NULL) {
+ 		cli();
+ 		if ((sl->flags & SLF_INUSE) == 0) {
+ 			/*
+ 			 * Must set at least IFF_UP or sl_close()
+ 			 * will not be called when the interface
+ 			 * is closed and INUSE will not be cleared!
+ 			 */
+ 			sl->flags |= SLF_INUSE;
+ 			sl->dev->if_flags |= (IFF_UP | IFF_RUNNING);
+ 			sl->tty = NULL;
+ 			sti();
+ 			return(sl);
+ 		}
+ 		sti();
+ 	}
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Free a SLIP channel. */
+ static void
+ sl_free(struct slip *sl)
+ {
+   cli();
+   if (sl->flags & SLF_INUSE) {
+ 	sl->flags &= ~SLF_INUSE;
+ 	sl->tty = NULL;
+ 	sl->dev->if_flags &= ~(IFF_UP | IFF_RUNNING);
+   }
+   sti();
+ }
+ 
+ 
+ /* Free the current SLIP channel buffers, if any. */
+ void
+ sl_free_buff(struct slip *sl)
+ {
+   if (sl == NULL) return;
+   if (sl->rbuff != NULL) kfree_s(sl->rbuff, sl->rbufsize);
+   sl->rbuff = NULL; sl->rbufsize = 0;
+   if (sl->xbuff != NULL) kfree_s(sl->xbuff, sl->xbufsize);
+   sl->xbuff = NULL; sl->xbufsize = 0;
+ }
+ 
+ 
+ /* Open the low-level part of the SLIP channel. Easy! */
+ static int
+ sl_open(struct ifnet *dev)
+ {
+   struct slip *sl;
+ 
+   sl = sl_ctrl[dev->if_unit];
+   if (sl->tty == NULL) {
+ 	DPRINTF(("SLIP: channel %d not connected!\n", sl->line));
+ 	return(-ENXIO);
+   }
+   sl->dev = dev;
+   sl->rcount = 0;
+ 
+   DPRINTF(("SLIP: channel %d opened.\n", sl->line));
+   return(0);
+ }
+ 
+ 
+ /* Close the low-level part of the SLIP channel. Easy! */
+ static int
+ sl_close(struct ifnet *dev)
+ {
+   struct slip *sl;
+ 
+   sl = sl_ctrl[dev->if_unit];
+   if (sl->tty == NULL) {
+ 	DPRINTF(("SLIP: channel %d not connected!\n", sl->line));
+ 	return(-EBUSY);
+   }
+   sl_free(sl);
+   sl_free_buff(sl);
+   sl_initialize(sl);
+ 
+   DPRINTF(("SLIP: channel %d closed.\n", sl->line));
+   return(0);
+ }
+ 
+ 
+ /* Allocate the SLIP frame buffers. */
+ int
+ sl_alloc_buff(struct slip *sl)
+ {
+   if (sl->xbuff != NULL || sl->rbuff != NULL) return(-EBUSY);
+   sl->xbufsize = (sl->dev->if_mtu * 2) + 4;
+   sl->xbuff = (unsigned char *) kmalloc(sl->xbufsize, GFP_ATOMIC);
+   if (sl->xbuff == NULL) {
+ 	DPRINTF(("SLIP: no memory for SLIP XMIT buffer!\n"));
+ 	return(-ENOMEM);
+   }
+   sl->rbufsize = (sl->dev->if_mtu * 2) + 4;
+   sl->rbuff = (unsigned char *) kmalloc(sl->rbufsize, GFP_ATOMIC);
+   if (sl->rbuff == NULL) {
+ 	DPRINTF(("SLIP: no memory for SLIP RECV buffer!\n"));
+ 	return(-ENOMEM);
+   }
+   sl->rend = (unsigned char *) (sl->rbuff + sl->rbufsize - 4);
+   sl->rhead = sl->rbuff;
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Open the high-level part of the SLIP channel.  
+  * This function is called by the TTY module when the
+  * SLIP line discipline is called for.  Because we are
+  * sure the tty line exists, we only have to link it to
+  * a free SLIP channel...
+  */
+ static int
+ slip_open(struct tty_struct *tty)
+ {
+   struct slip *sl;
+   int ret;
+ 
+   /* First make sure we're not already connected. */
+   if ((sl = sl_find(tty)) != NULL) {
+ 	DPRINTF(("SLIP: TTY %d already connected to %s !\n",
+ 					tty->line, sl->dev->if_name));
+ 	return(-EEXIST);
+   }
+ 
+   /* OK.  Find a free SLIP channel to use. */
+   if ((sl = sl_alloc()) == NULL) {
+ 	DPRINTF(("SLIP: TTY %d not connected: all channels in use!\n",
+ 								tty->line));
+ 	return(-ENFILE);
+   }
+ 
+   /* Tell TTY to flush all buffers of this line. */
+   sl->tty = tty;
+   tty_read_flush(tty);
+   tty_write_flush(tty);
+ 
+   /* Allocate the SLIP frame buffers. */
+   if ((ret = sl_alloc_buff(sl)) < 0) return(ret);
+ 
+   /* Pre-allocate the VJ Header Compression buffers. */
+   sl->cbuff = (unsigned char *)
+ 	kmalloc((sl->dev->if_mtu * 2) + 4, GFP_ATOMIC);
+   if (sl->cbuff == NULL) {
+ 	DPRINTF(("SLIP: no memory for SLIP COMPRESS buffer!\n"));
+ 	return(-ENOMEM);
+   }
+   sl->slcomp = slhc_init(sl, 16, 16);
+   if (sl->slcomp == NULL) {
+ 	DPRINTF(("SLIP: no memory for SLCOMP!\n"));
+ 	return(-ENOMEM);
+   }
+ 
+   /* Start off with uncompressed SLIP. */
+   (void) slip_setencap(sl, SLIP_ENCAP_SLIP);
+ 
+   /* Perform the low-level SLIP initialization. */
+   (void) sl_open(sl->dev);
+   sl->dev->if_tbusy = 0;
+   DPRINTF(("SLIP: TTY %d connected to %s.\n", tty->line, sl->dev->if_name));
+ 
+   /* Done.  We have linked the TTY line to a channel. */
+   return(sl->line);
+ }
+ 
+ 
+ /*
+  * Close down a SLIP channel.
+  * This means flushing out any pending queues, and then restoring the
+  * TTY line discipline to what it was before it got hooked to SLIP
+  * (which usually is TTY again).
+  */
+ static void
+ slip_close(struct tty_struct *tty)
+ {
+   struct slip *sl;
+ 
+   /* First make sure we're connected. */
+   if ((sl = sl_find(tty)) == NULL) {
+ 	DPRINTF(("SLIP: TTY %d not connected !\n", tty->line));
+ 	return;
+   }
+ 
+   /* Close down the NET interface. */
+   (void) dev_close(sl->dev);
+ 
+   /* Free all SLIP buffers. */
+   sl_free_buff(sl);
+   if (sl->cbuff != NULL) {
+ 	kfree(sl->cbuff);
+ 	sl->cbuff = NULL;
+   }
+   if (sl->slcomp != NULL) {
+ 	slhc_free(sl->slcomp);
+ 	sl->slcomp = NULL;
+   }
+ 
+   DPRINTF(("SLIP: TTY %d disconnected from %s.\n",
+ 				tty->line, sl->dev->if_name));
+ }
+ 
+ 
+ static int
+ slip_fopen(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= SL_MAX_DEV) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ slip_fclose(struct inode *inode, struct file *file)
+ {
+   int minor;
+ 
+   minor = MINOR(inode->i_rdev);
+   if (minor < 0 || minor >= SL_MAX_DEV) return;
+ }
+ 
+ 
+ static struct file_operations slip_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   slip_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   slip_fopen,	/* OPEN		*/
+   slip_fclose	/* CLOSE	*/
+ };
+ 
+ 
+ int
+ slip_init(struct ddi *ddi)
+ {
+   struct slip *sl;
+   struct ifnet *dev;
+   int i;
+ 
+   printk("%s: version %s: ", ddi->name, VERSION);
+ 
+   /* Check for sanity here... */
+   if (ddi->nunits >= SL_MAX_DEV) {
+ 	printk("too many channels requested!\n");
+ 	return(-1);
+   }
+ 
+   /* Register VFS device. */
+   if (ddi->major != 0) {
+ 	if (register_chrdev(ddi->major, ddi->name, &slip_fops) < 0) {
+ 		printk("cannot register device %d!\n", ddi->major);
+ 		return(-EINVAL);
+ 	}
+ 	printk("device %d/%d: ", ddi->major, ddi->minor);
+   }
+   printk("%d channels: ", ddi->nunits);
+ 
+   /* Fill in our LDISC request block. */
+   sl_ldisc.flags = 0;
+   sl_ldisc.open = slip_open;
+   sl_ldisc.close = slip_close;
+   sl_ldisc.read = NULL;
+   sl_ldisc.write = NULL;
+   sl_ldisc.select = NULL;
+   sl_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ 			   unsigned int, unsigned long)) slip_ioctl;
+   sl_ldisc.handler = slip_handler;
+   if (tty_register_ldisc(N_SLIP, &sl_ldisc) != 0) {
+ 	printk("cannot register line discipline %d !\n", N_SLIP);
+ 	return(-ENODEV);
+   }
+   printk("OK\n");
+   
+   /* Set up all our channels. */
+   for(i = 0; i < ddi->nunits; i++) {
+ 	sl = (struct slip *) kmalloc(sizeof(struct slip), GFP_KERNEL);
+ 	if (sl == NULL) {
+ 		printk("SLIP: no memory for control block #%d !\n", i);
+ 		return(-ENOMEM);
+ 	}
+ 	memset(sl, 0, sizeof(struct slip));
+ 
+ 	if ((dev = dev_create(NULL)) == NULL) {
+ 		printk("SLIP: no memory for interface descriptor #%d !\n", i);
+ 		kfree_s(sl, sizeof(struct slip));
+ 		return(-ENOMEM);
+ 	}
+ 	sl_ctrl[i] = sl;
+ 	sl->line = i;
+ 	sl->dev = dev;
+ 	sl_initialize(sl);
+ 	(void) slip_setencap(sl, SLIP_ENCAP_SLIP);
+ 
+ 	sprintf(dev->if_name, SLIP_IFNAME, i);
+ 	dev->if_flags = 0;
+ 	dev->if_mtu = SLIP_MTU;
+ 	dev->if_unit = sl->line;
+ 	dev->if_tbusy = 0;
+ 	dev->if_machlen	= SLIP_HLEN;
+ 	dev->if_macalen	= SLIP_ALEN;
+ 	dev->if_macaddr->ifa_addr.sa_family = ARPHRD_SLIP;
+ 
+ 	dev->mac1 = NULL;
+ 	dev->mac2 = NULL;
+ 	dev->mac_aget = NULL;
+ 	dev->mac_aset = NULL;
+ 	dev->mac_bget = NULL;
+ 	dev->mac_bset = NULL;
+ 	dev->mac_print = NULL;
+ 	dev->mac_sprint = NULL;
+ 	dev->set_multi = NULL;
+ 
+ 	dev->open = sl_open;
+ 	dev->close = sl_close;
+ 	dev->ioctl = (int (*)(struct inode *, struct file *,
+ 			   unsigned int, unsigned long)) slip_ioctl;
+ 	dev->xmit = slip_xmit;
+ 	dev->send = slip_send;
+ 	dev->recv = slip_recv;
+   }
+ 
+   /* Clear out the remaining SLIP channels. */
+   while (i < SL_MAX_DEV)
+ 	sl_ctrl[i++] = NULL;
+ 
+   /* All done... */
+   return(0);
+ }
*** original/net/drv/slip/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/packet.c	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,400 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		SLIP packet handling functions.
+  *
+  * Version:	@(#)packet.c	1.7.0	04/02/94
+  *
+  * Authors:	Laurence Culhane, <loz@holmes.demon.co.uk>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Chuck Hedrick, <hedrick@klinzhai.rutgers.edu>
+  *		Jim Seagrave, <jes@grendel.demon.co.uk>
+  *		Matt Dillon, <dillon@moonshot.west.oic.com>
+  *
+  *		Copyright 1992 Laurence Culhane
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_slip.h>
+ #include <linux/if_ether.h>		/* for ETH_P_IP */
+ #include <linux/ip.h>
+ #include <linux/tty.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include "slip.h"
+ #include "global.h"
+ #include "compress.h"
+ #include "encap.h"
+ 
+ 
+ /* Set the "sending" flag. */
+ static void
+ sl_lock(struct slip *sl)
+ {
+   unsigned long flags;
+ 
+   save_flags(flags);
+   cli();
+   sl->dev->if_tbusy = 1;
+   restore_flags(flags);
+ }
+ 
+ 
+ /* Clear the "sending" flag. */
+ static void
+ sl_unlock(struct slip *sl)
+ {
+   unsigned long flags;
+ 
+   save_flags(flags);
+   cli();
+   sl->dev->if_tbusy = 0;
+   restore_flags(flags);
+ }
+ 
+ 
+ /*
+  * Stuff one byte into a SLIP receiver buffer.
+  * This should be a ring buffer, with a check to see if we
+  * are being overrun.  If so, we should increase the correct
+  * if_stats counter (rx_overruns).
+  */
+ void
+ sl_enqueue(struct slip *sl, unsigned char c)
+ {
+   unsigned long flags;
+ 
+   save_flags(flags);
+   cli();
+   if (sl->rhead < sl->rend) {
+ 	*sl->rhead = c;
+ 	sl->rhead++;
+ 	sl->rcount++;
+   }
+   restore_flags(flags);
+ }
+ 
+ 
+ /* Release 'i' bytes from a SLIP receiver buffer. */
+ void
+ sl_dequeue(struct slip *sl, int i)
+ {
+   unsigned long flags;
+ 
+   save_flags(flags);
+   cli();
+   if ((sl->rhead - i) >= sl->rbuff) {
+ 	sl->rhead -= i;
+ 	sl->rcount -= i;
+   } else {
+   	sl->rhead = sl->rbuff;
+   	sl->rcount = 0;
+   }
+   restore_flags(flags);
+ }
+ 
+ 
+ /* Send one completely decapsulated IP datagram to the IP layer. */
+ void
+ sl_bump(struct slip *sl)
+ {
+   struct sk_buff *skb;
+   unsigned long flags;
+   unsigned char c;
+   int done, count;
+ 
+   DPRINTF(("\"%s\" recv:\n", sl->dev->if_name));
+   if (sl->flags & SLF_ERROR) {
+ 	slhc_toss(sl->slcomp);
+ 	return;
+   }
+   count = sl->rcount;
+   
+ #ifdef SLIP_DEBUG
+   /* Dump the VJ header, if any. */
+   if (slip_debug != 0) slhc_print(sl->rbuff, count);
+ #endif
+ 
+   /* Is this a "Compressed Packet" header? */
+   if ((c = sl->rbuff[0]) & SL_TYPE_COMP_TCP) {
+ 	if ((sl->flags & SLF_COMP) == 0) {
+ 		printk("SLIP: %s recv VJ while not enabled!\n",
+ 						sl->dev->if_name);
+ 		sl->dev->if_stats->rx_errors++;
+ 		return;	/* alas... */
+ 	}
+ 
+ 	/*
+ 	 * Make sure we've reserved enough
+ 	 * space for uncompress to use.
+ 	 *
+ 	 * FIXME: this goes wrong if TCP and IP options
+ 	 *        are enabled- 80 bytes is too small then!
+ 	 */
+ 	save_flags(flags);
+ 	cli();
+ 	if ((sl->rhead + 80) < sl->rend) {
+ 		sl->rhead += 80;
+ 		sl->rcount += 80;
+ 		done = 1;
+ 	} else {
+ 		sl->dev->if_stats->rx_overrun++;
+ 		done = 0;
+ 	}
+ 	restore_flags(flags);
+ 	if (! done) return;	/* not enough space available */
+ 
+ 	/* De-compress the packet to its original size. */
+ 	if ((count = slhc_uncompress(sl->slcomp, sl->rbuff, count)) <= 0) {
+ 		sl->dev->if_stats->rx_errors++;
+ 		return;
+ 	} 
+   } else if (c >= SL_TYPE_UNCOMP_TCP) {
+ 	/*
+ 	 * Is this a "Compression Wanted" packet header?
+ 	 * We just got a "compression request" from the other end.
+ 	 */
+ 	DPRINTF(("SLIP: %s requested VJ Header Compression!\n",
+ 						sl->dev->if_name));
+ 	/*
+ 	 * Check if this packet is a valid one.  If so, we enable
+ 	 * compression, as the other end seems to want it.  Note,
+ 	 * that I personally dislike doing this automatically, as
+ 	 * I sometimes do not want this to happen.  Not all of us
+ 	 * have modems that are smart enough to turn off compres-
+ 	 * sion if it doesn't seem worth the trouble- FvK.
+ 	 */
+ 	if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
+ 		sl->dev->if_stats->rx_errors++;
+ 		return;
+ 	}
+ 	slip_setencap(sl, 1);		/* enable compression */
+   } else {
+ 	sl->stats.sls_i_not_compressed++;
+   }
+ 
+   /* We finally decoded the frame. */
+ #ifdef SLIP_DEBUG
+   if (slip_debug != 0) slip_print(sl->rbuff);
+ #endif
+ 
+   /* Allocate and fill in an skb for this packet. */
+   if ((skb = skb_create(count)) == NULL) {
+ 	printk("SLLP: no memory for skb (len=%d)\n", count);
+ 	sl->dev->if_stats->rx_dropped++;
+ 	return;
+   }
+ 
+   /* Copy the packet buffer into the skb. */
+   memcpy(skb->raw, sl->rbuff, count);
+ 
+   /* Bump the datagram to the upper layers... */
+   DPRINTF(("SLIP: packet is %d at 0x%X\n", count, skb));
+   do {
+ 	done = dev_rint(sl->dev, skb, count);
+ 	if (done == 0 || done == 1) break;
+   } while(1);
+ 
+   /* Update the statistics. */
+   sl->dev->if_stats->rx_packets++;
+   if (done == 1) sl->dev->if_stats->rx_dropped++;
+ }
+ 
+ 
+ /* TTY finished sending a datagram, so clean up. */
+ static void
+ sl_next(struct slip *sl)
+ {
+   DPRINTF(("SLIP: sl_next(0x%X) called!\n", sl));
+   sl_unlock(sl);
+   dev_tint(sl->dev);
+ }
+ 
+ 
+ /* Encapsulate one IP datagram and stuff into a TTY queue. */
+ static void
+ sl_encapsulate(struct slip *sl, unsigned char *icp, int len)
+ {
+   unsigned char *bp, *p;
+   int count;
+ 
+   DPRINTF(("SLIP: sl_encapsulate(0x%X, %d) called\n", icp, len));
+   DPRINTF(("\"%s\" sent:\n", sl->dev->if_name));
+ #ifdef SLIP_DEBUG
+   if (slip_debug != 0) slip_print(icp);
+ #endif
+ 
+   p = icp;
+   sl->stats.tx_octets += len;
+   if (sl->flags & SLF_COMP) {
+ 	len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
+   }
+   sl->stats.tx_octets_raw += len;
+ 
+   /*
+    * This is ugly.
+    * Perhaps we should make a table of encapsulation methods
+    * and simply index to that table here? -FvK
+    */
+   if (sl->flags & SLF_EXPN) {
+       count = slip_encap6(p, (unsigned char *)sl->xbuff, len);
+   } else {
+       count = slip_encap(p, (unsigned char *)sl->xbuff, len);
+   }
+ 
+   bp = (unsigned char *) sl->xbuff;
+ 
+   /* Tell TTY to send it on its way. */
+   DPRINTF(("SLIP: kicking TTY for %d bytes at 0x%X\n", count, bp));
+   if (tty_write_data(sl->tty, (char *) bp, count,
+ 	     (void (*)(void *))sl_next, (void *) sl) == 0) {
+ 	DPRINTF(("SLIP: TTY already done with %d bytes!\n", count));
+ 	sl_next(sl);
+   }
+ 
+   /* Update the interface stats. */
+   sl->dev->if_stats->tx_packets++;
+ }
+ 
+ 
+ /*
+  * Handle the 'receiver data ready' interrupt.
+  * This function is called by the 'tty_io' module in the kernel when
+  * a block of SLIP data has been received, which can now be decapsulated
+  * and sent on to some IP layer for further processing.
+  */
+ void
+ slip_handler(struct tty_struct *tty)
+ {
+   unsigned char buff[128];	/* an mbuf is 128 bytes max. -FvK */
+   struct slip *sl;
+   int count, err_count;
+ 
+   DPRINTF(("SLIP: slip_handler(%d) called\n", tty->line));
+   if ((sl = sl_find(tty)) == NULL) {
+ 	DPRINTF(("SLIP: tty%d not connected!\n", tty->line));
+ 	return;	/* not connected */
+   }
+ 
+   /* Suck the bytes out of the TTY queues. */
+   do {
+ 	err_count = count = tty_read_raw_data(tty, buff, 128);
+ 	if (count == 0) break;
+ 	if (count < 0) {
+ 		count = -count;
+ 		sl->stats.errors++;
+ 	}
+ 	sl->stats.rx_octets_raw += count;
+ 	if ((count == 1) && (err_count < 0)) sl->flags |= SLF_ERROR;
+ 
+ 	/*
+ 	 * This is ugly.
+ 	 * Perhaps we should make a table of encapsulation methods
+ 	 * and simply index to that table here? -FvK
+ 	 */
+ 	if (sl->flags & SLF_EXPN) {
+ 	    slip_unencap6(sl, buff, count);
+ 	} else {
+ 	    slip_unencap(sl, buff, count);
+ 	}
+   } while(1);
+ }
+ 
+ 
+ /* Encapsulate an IP datagram and kick it into a TTY queue. */
+ int
+ slip_xmit(struct ifnet *dev, struct sk_buff *skb)
+ {
+   struct tty_struct *tty;
+   struct slip *sl;
+   int size;
+ 
+   /* Find the correct SLIP channel to use. */
+   sl = sl_ctrl[dev->if_unit];
+   tty = sl->tty;
+   DPRINTF(("SLIP: slip_xmit(\"%s\") skb=0x%X busy=%d\n",
+ 				dev->if_name, skb, sl->dev->if_tbusy));
+   if (skb == NULL) return(0);
+ 
+   /* If we are busy already- too bad. */
+   if (sl->dev->if_tbusy) {
+ 	DPRINTF(("SLIP: slip_xmit: BUSY\n"));
+ 	sl->dev->if_stats->tx_dropped++;
+ 	return(1);
+   }
+ 
+   /* We were not, so we are now... :-) */
+   sl_lock(sl);
+ 
+   /* Check the size of the frame. */
+   if (skb->len < sizeof(struct ip)) {
+ 	printk("SLIP: %s: xmit: RUNT IP datagram %d bytes (ignored)\n",
+ 					dev->if_name, skb->len);
+ 	sl->dev->if_stats->tx_dropped++;
+ 	sl_unlock(sl);
+ 	return(1);
+   }
+ 
+   /* Don't believe what the skb says- get the frame size from the header. */
+   size = ntohs(((struct ip *) skb->raw)->ip_len);
+ 
+   /* Encap and send... */
+   sl_encapsulate(sl, (unsigned char *) skb->raw, size);
+ 
+   return(0);
+ }
+ 
+ 
+ /* Process an incoming packet. */
+ int
+ slip_recv(struct ifnet *dev, struct sk_buff *skb, int len)
+ {
+   DPRINTF(("SLIP: %s recv: len=%d\n", dev->if_name, len));
+ 
+   /* SLIP does not have a MAC header. */
+   skb->raw = (unsigned char *) (skb + 1);
+   skb->len -= dev->if_machlen;
+ 
+   return(ETH_P_IP);
+ }
+ 
+ 
+ /* Send out a packet. */
+ void
+ slip_send(struct ifnet *dev, struct sk_buff *skb,
+ 	  struct sockaddr *target, int priority)
+ {
+   DPRINTF(("SLIP: %s sent: len=%d\n", dev->if_name, skb->len));
+ 
+   skb->raw = (unsigned char *) (skb + 1);
+ #if 0
+   skb->len += dev->if_machlen;
+ #endif
+ 
+   /* Call the DEV interface for the actual work. */
+   dev_queue(dev, skb, target, priority);
+ }
*** original/net/drv/slip/slip.h	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/slip.h	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,49 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Define the SLIP device driver interface and constants.
+  *
+  * Version:	@(#)slip.h	1.7.0	04/02/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _SLIP_H
+ #define _SLIP_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	SLIP_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  SLIP_DEBUG
+ #   define SLIP_DEBUG
+ #endif
+ 
+ #ifdef	SLIP_DEBUG
+ #   define DPRINTF(x)	slip_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ /* SLIP configuration. */
+ #define SL_MAX_DEV	64		/* should be enough for now :-)	*/
+ 
+ 
+ extern int		slip_init(struct ddi *ddi);
+ 
+ 
+ #endif	/* _SLIP_H */
*** original/net/drv/slip/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/drv/slip/utils.c	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,224 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the SLIP protocol for the kernel-
+  *		based devices like TTY.  It interfaces between a raw TTY,
+  *		and the kernel's NET protocol layers (via DDI).
+  *
+  *		Various functions.
+  *
+  * Version:	@(#)utils.c	1.7.0	04/02/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/if.h>
+ #include <linux/if_slip.h>
+ #include <linux/ip.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include <stdarg.h>
+ #include "slip.h"
+ #include "global.h"
+ #include "compress.h"
+ 
+ 
+ int		slip_debug = 0;			/* SLIP debugging flag	*/
+ 
+ 
+ #ifdef SLIP_DEBUG
+ void
+ slip_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (slip_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ 
+ 
+ /* Display a DARPA Internet address in readable format. */
+ char *
+ slip_in_ntoa(unsigned long in)
+ {
+   static char buff[18];
+   unsigned char *ptr;
+ 
+   ptr = (unsigned char *) &in;
+   sprintf(buff, "%d.%d.%d.%d",
+ 	(ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255), (ptr[3] & 255));
+   return(buff);
+ }
+ 
+ 
+ void
+ slip_print(unsigned char *ptr)
+ {
+   unsigned char buff[32];
+   struct ip *iph;
+   int addr, i, len;
+ 
+   /* Dump the IP header. */
+   iph = (struct ip *) ptr;
+   i = ntohs(iph->ip_off);
+   printk("SLIP: ver=%d ihl=%d tos=0x%02X len=%d id=0x%04X ttl=%d\n",
+ 		iph->ip_v, iph->ip_hl, iph->ip_tos, ntohs(iph->ip_len),
+ 		ntohs(iph->ip_id), iph->ip_ttl);
+   printk("      prot=0x%04X check=0x%04X offset=0x%04X flags:",
+ 		iph->ip_p, iph->ip_sum, (i & IP_OFFSET));
+   if (i & IP_CE) printk(" CE");
+   if (i & IP_MF) printk(" MF");
+   if (i & IP_DF) printk(" DF");
+   printk("\n    source=%s ", slip_in_ntoa(iph->ip_src.s_addr));
+   printk("dest=%s\n", slip_in_ntoa(iph->ip_dst.s_addr));
+   printk("    ----\n");
+ 
+   /* Dump the data. */
+   ptr = (unsigned char *) ((unsigned long) (iph + 1)) +
+ 			   (iph->ip_hl * sizeof(unsigned long));
+   addr = 0;
+   len = iph->ip_len;
+   if (len > IP_MAXPACKET) return;
+ 
+   len -= (iph->ip_hl * sizeof(unsigned long));
+   while (len > 0) {
+ 	printk("    %04X: ", addr);
+ 	for(i = 0; i < 16; i++) {
+ 		if (len > 0) {
+ 			printk("%02X ", (*ptr & 0xFF));
+ 			buff[i] = *ptr++;
+ 			if (buff[i] < 32 || buff[i] > 126) buff[i] = '.';
+ 		} else {
+ 			printk("   ");
+ 			buff[i] = ' ';
+ 		}
+ 		addr++;
+ 		len--;
+ 	};
+ 	buff[i] = '\0';
+ 	printk("  \"%s\"\n", buff);
+   }
+   printk("    ----\n\n");
+ }
+ #endif	/* SLIP_DEBUG */
+ 
+ 
+ /* Set the LINK ENCAP type for a channel. */
+ int
+ slip_setencap(struct slip *sl, int encap)
+ {
+   switch(encap) {
+ 	case SLIP_ENCAP_SLIP:	/* normal SLIP */
+ 		DPRINTF(("%s: SLIP ENCAP: normal\n", sl->dev->if_name));
+ 		sl->flags &= ~(SLF_COMP | SLF_EXPN);
+ 		return(0);
+ 
+ 	case SLIP_ENCAP_VJ:	/* VJ-compressed SLIP */
+ 		DPRINTF(("%s: SLIP ENCAP: VJ Compression\n", sl->dev->if_name));
+ 		sl->flags &= ~SLF_EXPN;
+ 		sl->flags |= SLF_COMP;
+ 		return(0);
+ 
+ 	case SLIP_ENCAP_SLIP6:	/* 6-bit printable */
+ 		DPRINTF(("%s: SLIP ENCAP: 6-bit encap\n", sl->dev->if_name));
+ 		sl->flags &= ~SLF_COMP;
+ 		sl->flags |= SLF_EXPN;
+ 		return(0);
+ 
+ 	case SLIP_ENCAP_VJSLIP6:/* 6-bit printable + VJ-compressed */
+ 		DPRINTF(("%s: SLIP ENCAP: 6-bit printable + VJ Compression\n",
+ 						sl->dev->if_name));
+ 		sl->flags |= (SLF_COMP | SLF_EXPN);
+   		return(0);
+ 
+ 	default:
+ 		break;
+   }
+ 
+   return(-EINVAL);
+ }
+ 
+ 
+ /* Get the LINK ENCAP type of a channel. */
+ int
+ slip_getencap(struct slip *sl)
+ {
+   int flags, ret;
+ 
+   flags = (sl->flags & (SLF_COMP | SLF_EXPN));
+   ret = -EINVAL;
+   switch(flags) {
+ 	case 0:				/* standard SLIP */
+ 		ret = SLIP_ENCAP_SLIP;
+ 		break;
+ 
+ 	case SLF_COMP:			/* VJ SLIP */
+ 		ret = SLIP_ENCAP_VJ;
+ 		break;
+ 
+ 	case SLF_EXPN:			/* 6-bit SLIP */
+ 		ret = SLIP_ENCAP_SLIP6;
+ 		break;
+ 
+ 	case (SLF_EXPN | SLF_COMP):	/* VJ 6-bit SLIP */
+ 		ret = SLIP_ENCAP_VJSLIP6;
+ 		break;
+ 
+ 	default:
+ 		break;
+   }
+   return(ret);
+ }
+ 
+ 
+ int
+ slip_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		slip_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		slip_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			slip_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/inet/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/inet/Makefile	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,39 ----
+ #
+ # Makefile for the Linux INET ("TCP/IP") protocol layer.
+ #
+ # 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 definition is now in the main makefile...
+ 
+ .c.o:
+ 	$(CC) $(CFLAGS) -c -o $*.o $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ .c.s:
+ 	$(CC) $(CFLAGS) -S -o $*.s $<
+ 
+ 
+ OBJS	= sock.o ioctl.o utils.o timer.o proc.o proto.o \
+ 	  route.o ip.o raw.o icmp.o tcp.o udp.o
+ 
+ 
+ inet.o:		$(OBJS)
+ 		$(LD) -r -o inet.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.a *.s tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.a *.s tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/inet/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/inet/global.h	Mon May  9 13:13:10 1994
***************
*** 0 ****
--- 1,143 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Global Definitions for the TCP/IP (INET) module. This is
+  *		mostly a bunch of "general" macros, plus the PROTOCOL link
+  *		code and data.
+  *
+  * Version:	@(#)global.h	1.1.13	01/16/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/fcntl.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/termios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/in.h>
+ #include <linux/in_systm.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/ip.h>
+ #include <linux/ip_icmp.h>
+ #include <linux/ip_udp.h>
+ #include <linux/ip_tcp.h>
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include "inet.h"
+ 
+ 
+ /* Some macros to get "flat" IP addresses from the structures. */
+ #define	IN_IFADDR(ifa)		(((struct sockaddr_in *) \
+ 				    &(ifa->ifa_addr))->sin_addr.s_addr)
+ #define	IN_IFDSTADDR(ifa)	(((struct sockaddr_in *) \
+ 				    &(ifa->ifa_dstaddr))->sin_addr.s_addr)
+ #define	IN_IFBRDADDR(ifa)	(((struct sockaddr_in *) \
+ 				    &(ifa->ifa_broadaddr))->sin_addr.s_addr)
+ #define	IN_IFMASK(ifa)		(((struct sockaddr_in *) \
+ 				    &(ifa->ifa_mask))->sin_addr.s_addr)
+ 
+ #define SA_ADDR(sa)		(((struct sockaddr_in *) &(sa))->sin_addr.s_addr)
+ #define SA_FAMILY(sa)		(((struct sockaddr_in *) &(sa))->sin_family)
+ 
+ /* Local address types. */
+ #define IS_MYADDR	1		/* address is (one of) our own	*/
+ #define IS_LOOPBACK	2		/* address is for LOOPBACK	*/
+ #define IS_BROADCAST	3		/* address is a valid broadcast	*/
+ 
+ /* Debug levels. One per module. */
+ #define DBG_INET	1		/* sock.c			*/
+ #define DBG_RT		2		/* route.c			*/
+ #define DBG_PROTO	3		/* protocol.c			*/
+ #define DBG_TMR		4		/* timer.c			*/
+ #define DBG_PKT		5		/* packet.c			*/
+ #define DBG_RAW		6		/* raw.c			*/
+ #define DBG_IP		7		/* ip.c				*/
+ #define DBG_ICMP	8		/* icmp.c			*/
+ #define DBG_TCP		9		/* tcp.c			*/
+ #define DBG_UDP		10		/* udp.c			*/
+ 
+ #define FREE_READ	0		/* kfree_skb() opcode		*/
+ #define FREE_WRITE	1		/* kfree_skb() opcode		*/
+ 
+ 
+ /* This is used to register INET protocols. */
+ struct ipprot {
+   char		*name;
+   unsigned char	protocol;
+   unsigned char	copy;
+   void		*data;
+   int		(*handler)(struct sk_buff *skb, struct ifnet *dev,
+ 			   struct options *opt, unsigned long daddr,
+ 			   unsigned short len, unsigned long saddr,
+ 			   int redo, struct ipprot *proto);
+   void		(*err_handler)(int type, int code, int err, int fatal,
+ 			       struct ip *iph, struct ipprot *proto);
+ 
+ 
+ 
+   struct ipprot	*next;
+ };
+ 
+ 
+ extern int		inet_debug;
+ extern struct sock	*timer_base;
+ 
+ 
+ /* INET global utility functions. */
+ extern char		*in_print(unsigned char *);
+ extern char		*in_sprint(struct sockaddr *);
+ extern char		*in_ntoa(unsigned long);
+ extern int		in_check(struct sockaddr *);
+ extern unsigned long	in_getmask(unsigned long dst, struct ifnet *dev,
+ 				   struct ifaddr *ifa);
+ extern int		in_compare(struct sockaddr *, struct sockaddr *);
+ extern void		in_mksock(struct sockaddr *, unsigned char *);
+ extern unsigned char	*in_unsock(struct sockaddr *);
+ extern int		in_chk_addr(unsigned long addr);
+ extern struct ifnet	*in_dev_check(unsigned long daddr);
+ extern struct ifnet	*in_dev_primary(void);
+ extern unsigned long	in_local_addr(unsigned long daddr);
+ 
+ /* INET protocol handling functions. */
+ extern struct ipprot	*inet_get_proto(unsigned char prot);
+ extern int		inet_add_proto(struct ipprot *prot);
+ extern int		inet_del_proto(struct ipprot *prot);
+ extern int		inet_proto_recv(struct ifnet *dev, struct sk_buff *skb,
+ 					struct ip *iph, struct options *opt);
+ extern int		inet_proto_errcv(int type, int code, int err,
+ 					 int fatal, struct ip *iph);
+ 
+ /* Memory handling functions. */
+ extern void		inet_kfree_skb(struct sk_buff *skb, int rw);
+ extern void		inet_kfree_skb_sk(struct sock *sk,
+ 					  struct sk_buff *skb, int rw);
+ 
+ extern void		inet_dprintf(int level, char *fmt, ...);
+ extern int		inet_dbg_ioctl(unsigned long arg, int level);
+ extern int		inet_ioctl(struct socket *sock, unsigned int cmd,
+ 				   unsigned long arg);
+ 
+ /* Timer handling functions. */
+ extern void		inet_timer(unsigned long data);
+ extern void		inet_delete_timer(struct sock *sk);
+ extern void		inet_reset_timer(struct sock *sk, int, unsigned long);
+ 
+ /* End of GLOBAL.H */
*** original/net/inet/icmp.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/icmp.c	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,688 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Internet Control Message Protocol (ICMP) as per RFC-792.
+  *
+  * Version:	@(#)icmp.c	1.1.22	04/23/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Mark Evans, <evansmp@uhura.aston.ac.uk>
+  *		Steve Entwistle, <steve@rama.demon.co.uk>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "route.h"
+ #include "sock.h"
+ 
+ 
+ #define SKBICMP(x)	((struct icmp *) ((x)->raw))
+ #define min(a,b)	((a)<(b)?(a):(b))
+ 
+ 
+ static unsigned short icmp_lid = 0;	/* ICMP "id" pointer		*/
+ static unsigned short icmp_lseq = 0;	/* ICMP "seq" pointer		*/
+ 
+ 
+ /*
+  * Try to map an ICMP error code, as received from the network,
+  * into a usable set of a UNIX error code and a flag whether the
+  * error is fatal or not.  This needs a lot of checking against
+  * other implementations -FvK
+  */
+ static int
+ icmp_geterr(int type, int code, int *fatal)
+ {
+   int ret, fat;
+ 
+   ret = 0;
+   fat = 0;
+   switch(type) {
+ 	case ICMP_UNREACH:
+ 		switch(code) {
+ 			case ICMP_UNREACH_NET:
+ 				ret = ENETUNREACH;
+ 				fat = 1;
+ 				break;
+ 
+ 			case ICMP_UNREACH_HOST:
+ 				ret = EHOSTUNREACH;
+ 				fat = 1;
+ 				break;
+ 
+ 			case ICMP_UNREACH_PROTOCOL:
+ 				ret = ENOPROTOOPT;
+ 				fat = 1;
+ 				break;
+ 
+ 			case ICMP_UNREACH_PORT:
+ 				ret = ECONNREFUSED;
+ 				fat = 1;
+ 				break;
+ 
+ 			default:
+ 				ret = ECONNABORTED;
+ 				fat = 1;
+ 		}
+ 		break;
+ 
+ 	case ICMP_SOURCEQUENCH:
+ 		ret = ENOBUFS;	/* not really an error, handled locally */
+ 		fat = 0;
+ 		break;
+ 
+ 	case ICMP_TIMXCEED:
+ 		switch(code) {
+ 			case ICMP_TIMXCEED_INTRANS:
+ 				ret = EHOSTDOWN;
+ 				fat = 1;
+ 
+ 			default:
+ 				break;
+ 		}
+ 		break;
+ 
+ 	default:
+ 		break;
+   }
+ 
+   *fatal = fat;
+   return(ret);
+ }
+ 
+ 
+ /* Display the contents of an ICMP header. */
+ static void
+ print_icmp(struct icmp *icmph)
+ {
+   if ((inet_debug & (1 << DBG_ICMP)) == 0) return;
+ 
+   printk("ICMP: TYPE %d  CODE %d  CHECKSUM 0x%04X\n",
+ 			icmph->icmp_type, icmph->icmp_code, icmph->icmp_cksum);
+   printk("      ID 0x%04X  SEQ 0x%04X  GATEWAY %s\n",
+ 			icmph->icmp_id, icmph->icmp_seq,
+ 			in_ntoa(icmph->icmp_gwaddr.s_addr));
+ }
+ 
+ 
+ /* Return the next ICMP "id", in network byte order. */
+ static unsigned short
+ icmp_get_id(void)
+ {
+   unsigned short id;
+ 
+   cli();
+   id = ++icmp_lid;
+   if (icmp_lid == 65530) icmp_lid = 1;
+   sti();
+ 
+   return(htons(id));
+ }
+ 
+ 
+ /* Return the next ICMP "seq", in network byte order. */
+ static unsigned short
+ icmp_get_seq(void)
+ {
+   unsigned short seq;
+ 
+   cli();
+   seq = ++icmp_lseq;
+   if (icmp_lseq == 65530) icmp_lseq = 1;
+   sti();
+ 
+   return(htons(seq));
+ }
+ 
+ 
+ /* Send an ICMP message. */
+ void
+ icmp_send(unsigned long daddr, int type, int code, struct ip *iph)
+ {
+   struct icmp *icmph;
+   struct sk_buff *skb;
+   struct rtable *rt;
+   struct ifnet *dev;
+   unsigned long saddr;
+   int offset;
+ 
+   DPRINTF((DBG_ICMP, "ICMP: send: daddr=%s type=%d code=%d skb=0x%X\n",
+ 	   				in_ntoa(daddr), type, code, iph));
+ 
+   /* Get some memory for the message. */
+   skb = skb_create(64 + (iph->ip_hl * sizeof(unsigned long)) +
+ 		   ((iph->ip_hl * sizeof(unsigned long)) + 8) +
+ 		   sizeof(struct icmp));
+   if (skb == NULL) return;
+ 
+   /* Fetch the correct device to use; we need to know the correct source. */
+   if ((rt = inet_route(daddr, NULL)) == NULL) {
+ 	DPRINTF((DBG_ICMP, "ICMP: Huh?  No route but still ICMP?\n"));
+ 	return;
+   }
+ 
+   /*
+    * If this packet is going to the loopback device, the source
+    * address of this packet will be the same as the destination
+    * address, as done with the other protocols.
+    */
+   saddr = daddr;
+   if (! (rt->rt_ifp->if_flags & IFF_LOOPBACK)) {
+ 	struct ifaddr *ifa;
+ 
+ 	/* Fetch the interface address. */
+ 	if ((ifa = dev_get_addr(rt->rt_ifp, AF_INET)) == NULL) {
+ 		DPRINTF((DBG_ICMP, "ICMP: No IP address but still ICMP?\n"));
+ 		return;
+ 	}
+ 	saddr = IN_IFADDR(ifa);
+   }
+ 
+   /*
+    * Build Layer 2-3 headers for message back to source.
+    * Note that we already searched the interface list for a good
+    * one to use, because we needed to determine the source address
+    * to use.  If all is well, the "dev" resulting from the IP header
+    * build should the be same as rt->rt_ifp. -FvK
+    */
+   dev = NULL;
+   offset = ip_build_header(skb, saddr, daddr, &dev,
+ 			   IPPROTO_ICMP, NULL, (iph == NULL) ? 0 : iph->ip_tos,
+ 			   ip_parms.defttl, 0);
+   if (offset < 0) {
+ 	DPRINTF((DBG_ICMP, "ICMP: send: cannot create IP header !\n"));
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_WRITE);
+ 	return;
+   }
+   skb->len = offset;
+   icmph = (struct icmp *) ((unsigned char *) (skb + 1) + offset);
+ 
+   /* Create the ICMP header. */
+   icmph->icmp_type = type;
+   icmph->icmp_code = code;
+   switch(type) {
+ 	case ICMP_ECHO:			/* fill in ID and SEQUENCE	*/
+ 	case ICMP_TSTAMP:
+ 	case ICMP_IREQ:
+ 	case ICMP_MASKREQ:
+ 		icmph->icmp_id = icmp_get_id();
+ 		icmph->icmp_seq = icmp_get_seq();
+ 		break;
+ 
+ 	case ICMP_REDIRECT:		/* fill in our address as GW	*/
+ 		icmph->icmp_gwaddr.s_addr = saddr;
+ 		break;
+ 
+ 	case ICMP_PARAMPROB:		/* not quite right...		*/
+ 	default:			/* fill in a 4-byte zero	*/
+ 		icmph->icmp_void = 0;
+ 		break;
+   }
+   skb->len += sizeof(struct icmp);
+ 
+   /* Copy the offending IP header, plus 64 bits of datagram data. */
+   if (iph != NULL) {
+ 	memcpy(icmph->icmp_data, iph, (iph->ip_hl * sizeof(unsigned long)) + 8);
+ 	skb->len += ((iph->ip_hl * sizeof(unsigned long)) + 8);
+   }
+ 
+   /* Create the ICMP checksum. */
+   icmph->icmp_cksum = 0;
+   icmph->icmp_cksum = ip_compute_csum((unsigned char *) icmph,
+ 				      skb->len - offset);
+ 
+   DPRINTF((DBG_ICMP, ">>\n"));
+   print_icmp(icmph);
+ 
+   /* Send it and free it. */
+   ip_queue_xmit(NULL, dev, skb, 1);
+ }
+ 
+ 
+ /* Handle ICMP_UNREACH and ICMP_QUENCH. */
+ static void
+ icmp_unreach(struct icmp *icmph, struct sk_buff *skb)
+ {
+   struct ip *iph;
+   int err, fat;
+ 
+   iph = (struct ip *) &icmph->icmp_ip;
+   switch(icmph->icmp_code & 7) {
+ 	case ICMP_UNREACH_NET:
+ 		DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		break;
+ 
+ 	case ICMP_UNREACH_HOST:
+ 		DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		break;
+ 
+ 	case ICMP_UNREACH_PROTOCOL:
+ 		printk("ICMP: %s:%d: protocol unreachable.\n",
+ 			in_ntoa(iph->ip_dst.s_addr), ntohs(iph->ip_p));
+ 		break;
+ 
+ 	case ICMP_UNREACH_PORT:
+ 		DPRINTF((DBG_ICMP, "ICMP: %s: port unreachable.\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		break;
+ 
+ 	case ICMP_UNREACH_NEEDFRAG:
+ 		printk("ICMP: %s: fragmentation needed and DF set.\n",
+ 					in_ntoa(iph->ip_dst.s_addr));
+ 		break;
+ 
+ 	case ICMP_UNREACH_SRCFAIL:
+ 		printk("ICMP: %s: Source Route Failed.\n",
+ 					in_ntoa(iph->ip_dst.s_addr));
+ 		break;
+ 
+ 	default:
+ 		DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
+ 		    	(icmph->icmp_code & 7), in_ntoa(iph->ip_dst.s_addr)));
+ 		break;
+   }
+ 
+   /*
+    * Tell the correct protocol layer what happened, whether
+    * it is interested in it or not.  We just do our job here.
+    */
+   err = icmp_geterr(icmph->icmp_type, icmph->icmp_code, &fat);
+   (void) inet_proto_errcv(icmph->icmp_type, icmph->icmp_code, err, fat, iph);
+ }
+ 
+ 
+ /* Handle ICMP_REDIRECT. */
+ static void
+ icmp_redirect(struct icmp *icmph, struct sk_buff *skb, struct ifnet *dev)
+ {
+   struct ip *iph;
+   struct ip *iphr;
+ 
+   iphr = (struct ip *) ((unsigned char *) (skb + 1) + dev->if_machlen);
+   iph = (struct ip *) &icmph->icmp_ip;
+   switch(icmph->icmp_code & 0x07) {
+ 	case ICMP_REDIRECT_NET:
+ 		DPRINTF((DBG_ICMP, "ICMP: redirect(NET) from %s ",
+ 					in_ntoa(iphr->ip_src.s_addr)));
+ 		DPRINTF((DBG_ICMP, "for %s !\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		inet_rt_add((RTF_DYNAMIC|RTF_MODIFIED|RTF_GATEWAY),
+ 			    iph->ip_dst.s_addr, icmph->icmp_gwaddr.s_addr,
+ 			    ~0L, iph->ip_ttl, dev);
+ 		break;
+ 
+ 	case ICMP_REDIRECT_HOST:
+ 		DPRINTF((DBG_ICMP, "ICMP: redirect(HOST) from %s ",
+ 					in_ntoa(iphr->ip_src.s_addr)));
+ 		DPRINTF((DBG_ICMP, "for %s !\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		inet_rt_add((RTF_DYNAMIC|RTF_MODIFIED|RTF_HOST|RTF_GATEWAY),
+ 			    iph->ip_dst.s_addr, icmph->icmp_gwaddr.s_addr,
+ 			    ~0L, iph->ip_ttl, dev);
+ 		break;
+ 
+ 	case ICMP_REDIRECT_TOSNET:
+ 		DPRINTF((DBG_ICMP, "ICMP: redirect(TOS_NET) from %s ",
+ 					in_ntoa(iphr->ip_src.s_addr)));
+ 		DPRINTF((DBG_ICMP, "for %s (ignored)\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		inet_rt_add((RTF_DYNAMIC|RTF_MODIFIED|RTF_GATEWAY),
+ 			    iph->ip_dst.s_addr, icmph->icmp_gwaddr.s_addr,
+ 			    ~0L, iph->ip_ttl, dev);
+ 		break;
+ 
+ 	case ICMP_REDIRECT_TOSHOST:
+ 		DPRINTF((DBG_ICMP, "ICMP: redirect(TOS_HOST) from %s ",
+ 					in_ntoa(iphr->ip_src.s_addr)));
+ 		DPRINTF((DBG_ICMP, "for %s (ignored)\n",
+ 					in_ntoa(iph->ip_dst.s_addr)));
+ 		inet_rt_add((RTF_DYNAMIC|RTF_MODIFIED|RTF_HOST|RTF_GATEWAY),
+ 			    iph->ip_dst.s_addr, icmph->icmp_gwaddr.s_addr,
+ 			    ~0L, iph->ip_ttl, dev);
+ 		break;
+ 
+ 	default:
+ 		DPRINTF((DBG_ICMP, "ICMP: unreach: CODE=%d from %s !\n",
+ 		    (icmph->icmp_code & 0x7), in_ntoa(iphr->ip_src.s_addr)));
+ 		break;
+   }
+ }
+ 
+ 
+ /* Handle ICMP_ECHO ("ping") requests. */
+ static void
+ icmp_echo(struct icmp *icmph, struct sk_buff *skb, struct ifnet *dev,
+ 	  unsigned long saddr, unsigned long daddr, int len,
+ 	  struct options *opt, struct ip *iph)
+ {
+   struct icmp *icmphr;
+   struct sk_buff *skb2;
+   int size, offset;
+ 
+   size = dev->if_machlen + len + 64 /* to be safe */;
+   skb2 = skb_create(size);
+   if (skb2 == NULL) return;
+ 
+   /* Build Layer 2-3 headers for message back to source. */
+   offset = ip_build_header(skb2, daddr, saddr, &dev, IPPROTO_ICMP,
+ 			   opt, iph->ip_tos, ip_parms.defttl, 0);
+   if (offset < 0) {
+ 	DPRINTF((DBG_ICMP,
+ 		"ICMP: echo: could not build header for response !\n"));
+ 	skb2->sk = NULL;
+ 	inet_kfree_skb(skb2, FREE_WRITE);
+ 	return;
+   }
+ 
+   /* Re-adjust length according to actual IP header size. */
+   skb2->len = offset + len;
+ 
+   /* Build ICMP_ECHO Response message. */
+   icmphr = (struct icmp *) ((char *) (skb2 + 1) + offset);
+   memcpy((char *) icmphr, (char *) icmph, len);
+   icmphr->icmp_type = ICMP_ECHOREPLY;
+   icmphr->icmp_code = 0;
+   icmphr->icmp_cksum = 0;
+   icmphr->icmp_cksum = ip_compute_csum((unsigned char *) icmphr, len);
+ 
+   /* Ship it out - free it when done. */
+   ip_queue_xmit(NULL, dev, skb2, 1);
+ }
+ 
+ 
+ /* Handle the ICMP INFORMATION REQUEST. */
+ static void
+ icmp_info(struct icmp *icmph, struct sk_buff *skb, struct ifnet *dev,
+ 	  unsigned long saddr, unsigned long daddr, int len,
+ 	  struct options *opt, struct ip *iph)
+ {
+   /* NOT YET */
+ }
+ 
+ 
+ /* Handle ICMP_ADRESS_MASK requests. */
+ static void
+ icmp_address(struct icmp *icmph, struct sk_buff *skb, struct ifnet *dev,
+ 	    unsigned long saddr, unsigned long daddr, int len,
+ 	    struct options *opt, struct ip *iph)
+ {
+   struct icmp *icmphr;
+   struct sk_buff *skb2;
+   struct ifaddr *ifa;
+   int size, offset;
+ 
+   size = dev->if_machlen + len + 64 /* to be safe */;
+   skb2 = skb_create(size);
+   if (skb2 == NULL) return;
+ 
+   /* Build Layer 2-3 headers for message back to source. */
+   offset = ip_build_header(skb2, daddr, saddr, &dev, IPPROTO_ICMP,
+ 			   opt, iph->ip_tos, ip_parms.defttl, 0);
+   if (offset < 0) {
+ 	DPRINTF((DBG_ICMP,
+ 		"ICMP: address: could not build header for response !\n"));
+ 	skb2->sk = NULL;
+ 	inet_kfree_skb(skb2, FREE_WRITE);
+ 	return;
+   }
+ 
+   /* Re-adjust length according to actual IP header size. */
+   skb2->len = offset + len;
+ 
+   /* Fetch IP address for this interface. */
+   if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) {
+ 	DPRINTF((DBG_ICMP, "ICMP: Huh?  No IP address but still ICMP?\n"));
+ 	skb2->sk = NULL;
+ 	inet_kfree_skb(skb2, FREE_WRITE);
+ 	return;
+   }
+ 
+   /* Build ICMP ADDRESS MASK Response message. */
+   icmphr = (struct icmp *) ((char *) (skb2 + 1) + offset);
+   icmphr->icmp_type = ICMP_MASKREPLY;
+   icmphr->icmp_code = 0;
+   icmphr->icmp_id = icmph->icmp_id;
+   icmphr->icmp_seq = icmph->icmp_seq;
+   memcpy(icmphr->icmp_data, &(IN_IFMASK(ifa)), sizeof(IN_IFMASK(ifa)));
+   icmphr->icmp_cksum = 0;
+   icmphr->icmp_cksum = ip_compute_csum((unsigned char *) icmphr, len);
+ 
+   /* Ship it out - free it when done. */
+   ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+ }
+ 
+ 
+ /* Handle ICMP Time Exceeded. */
+ static void
+ icmp_timex(struct icmp *icmph, unsigned long saddr)
+ {
+   char buff[32];
+   struct ip *iph;
+ 
+   iph = (struct ip *) &icmph->icmp_ip;
+   switch(icmph->icmp_code) {
+ 	case ICMP_TIMXCEED_INTRANS:
+ 		strcpy(buff, "TTL count");
+ 		break;
+ 
+ 	case ICMP_TIMXCEED_REASS:
+ 		strcpy(buff, "Fragment Reassembly Time");
+ 		break;
+ 
+ 	default:
+ 		sprintf(buff, "Unknown code 0x%X\n", icmph->icmp_code);
+   }
+ 
+   DPRINTF((DBG_ICMP, "ICMP: Time Exceeded (%s) from %s", buff, in_ntoa(saddr)));
+   DPRINTF((DBG_ICMP, " for %s\n", in_ntoa(iph->ip_dst.s_addr)));
+ 
+   /* Tell IP to nuke the queue for this fragment. */
+   ip_icmpexpire(iph);
+ }
+ 
+ 
+ /* Handle ICMP Parameter Problem. */
+ static void
+ icmp_parprob(struct icmp *icmph, unsigned long saddr)
+ {
+   struct ip *iph;
+ 
+   iph = (struct ip *) &icmph->icmp_ip;
+   DPRINTF((DBG_ICMP, "ICMP: Parameter Problem from %s", in_ntoa(saddr)));
+   DPRINTF((DBG_ICMP, " for %s\n", in_ntoa(iph->ip_dst.s_addr)));
+ }
+ 
+ 
+ /* Handle ICMP Timestamp requests. */
+ static void
+ icmp_timestamp(struct icmp *icmph, struct sk_buff *skb, struct ifnet *dev,
+ 	       unsigned long saddr, unsigned long daddr, int len,
+ 	       struct options *opt, struct ip *iph)
+ {
+   struct icmp *icmphr;
+   struct sk_buff *skb2;
+   int size, offset;
+   unsigned long *timeptr, midtime;
+   extern struct timeval xtime;			/* kernel/time.c */
+ 
+   /*
+    * `len' should be large enough to hold the reply packet; must
+    * be at least sizeof(actual-ip-header + sizeof icmp-header + 8).
+    */
+   size = dev->if_machlen + len + 64 /* to be safe */;
+   skb2 = skb_create(size);
+   if (skb2 == NULL) return;
+ 
+   /* Build Layer 2-3 headers for message back to source. */
+   offset = ip_build_header(skb2, daddr, saddr, &dev, IPPROTO_ICMP,
+ 			   opt, iph->ip_tos, ip_parms.defttl, 0);
+   if (offset < 0) {
+        DPRINTF((DBG_ICMP,
+ 		"ICMP: timestamp: could not build header for response !\n"));
+        skb2->sk = NULL;
+        inet_kfree_skb(skb2, FREE_WRITE);
+        return;
+   }
+ 
+   /* Re-adjust length according to actual IP header size. */
+   skb2->len = offset + len;
+ 
+   /* Build ICMP TIMESTAMP Response message. */
+   icmphr = (struct icmp *) ((char *) (skb2 + 1) + offset);
+   *icmphr = *icmph;
+   icmphr->icmp_type = ICMP_TSTAMPREPLY;
+   icmphr->icmp_code = icmphr->icmp_cksum = 0;
+ 
+   /* Fill in the current time im ms since midnight UT. */
+   midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000;
+   timeptr = (unsigned long *) icmphr->icmp_data;
+ 
+   /* The original timestamp (timeptr[0]) is still in the copy: */
+   timeptr[1] = timeptr[2] = htonl(midtime);
+ 
+   icmphr->icmp_cksum = ip_compute_csum((unsigned char *) icmphr, len);
+ 
+   /* Ship it out - free it when done. */
+   ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+ }
+ 
+ 
+ /* Deal with incoming ICMP packets. */
+ static int
+ icmp_recv(struct sk_buff *skb1, struct ifnet *dev, struct options *opt,
+ 	  unsigned long daddr, unsigned short len, unsigned long saddr,
+ 	  int redo, struct ipprot *protocol)
+ {
+   struct icmp *icmph;
+   struct ip *iph;
+ 
+ #if (ICMP_PINGBC == 0)
+   /* Drop broadcast packets. */
+   if (in_chk_addr(daddr) == IS_BROADCAST) {
+ 	DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
+ 							in_ntoa(saddr)));
+ 	skb1->sk = NULL;
+ 	inet_kfree_skb(skb1, FREE_READ);
+ 	return(0);
+   }
+ #endif
+   iph = SKBIP(skb1);
+   skb1->raw += (iph->ip_hl * sizeof(unsigned long));
+   len -= (iph->ip_hl * sizeof(unsigned long));
+   icmph = SKBICMP(skb1);
+ 
+   /* Validate the packet first. */
+   print_icmp(icmph);
+   if (ip_compute_csum((unsigned char *) icmph, len)) {
+ 	/* Failed checksum! */
+ 	printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
+ 	skb1->sk = NULL;
+ 	inet_kfree_skb(skb1, FREE_READ);
+ 	return(0);
+   }
+ 
+   /* Parse the ICMP message */
+   switch(icmph->icmp_type) {
+ 	case ICMP_ECHOREPLY:			/* ignored */
+ 		break;
+ 
+ 	case ICMP_UNREACH:
+ 	case ICMP_SOURCEQUENCH:
+ 		icmp_unreach(icmph, skb1);
+ 		break;
+ 
+ 	case ICMP_REDIRECT:
+ 		icmp_redirect(icmph, skb1, dev);
+ 		break;
+ 
+ 	case ICMP_ECHO: 
+ 		icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt, iph);
+ 		break;
+ 
+ 	case ICMP_TIMXCEED:
+ 		icmp_timex(icmph, saddr);
+ 		break;
+ 
+ 	case ICMP_PARAMPROB:
+ 		icmp_parprob(icmph, saddr);
+ 		break;
+ 
+ 	case ICMP_TSTAMP:
+ 		icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt, iph);
+ 		break;
+ 
+ 	case ICMP_TSTAMPREPLY:			/* ignored */
+ 		break;
+ 
+ 	case ICMP_IREQ:
+ 		icmp_info(icmph, skb1, dev, saddr, daddr, len, opt, iph);
+ 		break;
+ 
+ 	case ICMP_IREQREPLY:			/* ignored */
+ 		break;
+ 
+ 	case ICMP_MASKREQ:
+ 		icmp_address(icmph, skb1, dev, saddr, daddr, len, opt, iph);
+ 		break;
+ 
+ 	case ICMP_MASKREPLY:			/* ignored */
+ 		break;
+ 
+ 	default:
+ 		DPRINTF((DBG_ICMP,
+ 			"ICMP: Unsupported ICMP from %s, type = 0x%X\n",
+ 					in_ntoa(saddr), icmph->icmp_type));
+ 		break;
+   }
+ 
+   /* We processed the packet, so nuke it. */
+   skb1->sk = NULL;
+   inet_kfree_skb(skb1, FREE_READ);
+   return(0);
+ }
+ 
+ 
+ /* Perform any ICMP-related I/O control requests. */
+ int
+ icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+ {
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		return(inet_dbg_ioctl(arg, DBG_ICMP));
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+   return(0);
+ }
+ 
+ 
+ static struct ipprot icmp_protocol = {
+   "ICMP",
+   IPPROTO_ICMP,		/* protocol ID		*/
+   0,			/* copy			*/
+   NULL,			/* data			*/
+   icmp_recv,		/* ICMP handler		*/
+   NULL,			/* ICMP error control	*/
+   NULL			/* next			*/
+ };
+ 
+ 
+ void
+ icmp_proto_init(void)
+ {
+   /* Only register this IP protocol. */
+   (void) inet_add_proto(&icmp_protocol);
+ }
*** original/net/inet/inet.h	Thu Jan  1 01:00:00 1970
--- linux/net/inet/inet.h	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,79 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		General Definitions for the TCP/IP (INET) module.
+  *
+  * Version:	@(#)inet.h	1.1.5	01/16/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		This work was derived from Ross Biro's inspirational work
+  *		for the LINUX operating system.  His version numbers were:
+  *
+  *		$Id: Space.c,v     0.8.4.5  1992/12/12 19:25:04 bir7 Exp $
+  *		$Id: arp.c,v       0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
+  *		$Id: arp.h,v       0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
+  *		$Id: dev.c,v       0.8.4.13 1993/01/23 18:00:11 bir7 Exp $
+  *		$Id: dev.h,v       0.8.4.7  1993/01/23 18:00:11 bir7 Exp $
+  *		$Id: eth.c,v       0.8.4.4  1993/01/22 23:21:38 bir7 Exp $
+  *		$Id: eth.h,v       0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
+  *		$Id: icmp.c,v      0.8.4.9  1993/01/23 18:00:11 bir7 Exp $
+  *		$Id: icmp.h,v      0.8.4.2  1992/11/15 14:55:30 bir7 Exp $
+  * 		$Id: ip.c,v        0.8.4.8  1992/12/12 19:25:04 bir7 Exp $
+  * 		$Id: ip.h,v        0.8.4.2  1993/01/23 18:00:11 bir7 Exp $
+  * 		$Id: loopback.c,v  0.8.4.8  1993/01/23 18:00:11 bir7 Exp $
+  * 		$Id: packet.c,v    0.8.4.7  1993/01/26 22:04:00 bir7 Exp $
+  *		$Id: protocols.c,v 0.8.4.3  1992/11/15 14:55:30 bir7 Exp $
+  *		$Id: raw.c,v       0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+  *		$Id: sock.c,v      0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
+  *		$Id: sock.h,v      0.8.4.7  1993/01/26 22:04:00 bir7 Exp $
+  *		$Id: tcp.c,v       0.8.4.16 1993/01/26 22:04:00 bir7 Exp $
+  *		$Id: tcp.h,v       0.8.4.7  1993/01/22 22:58:08 bir7 Exp $
+  *		$Id: timer.c,v     0.8.4.8  1993/01/23 18:00:11 bir7 Exp $
+  *		$Id: timer.h,v     0.8.4.2  1993/01/23 18:00:11 bir7 Exp $
+  *		$Id: udp.c,v       0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+  *		$Id: udp.h,v       0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
+  *		$Id: we.c,v        0.8.4.10 1993/01/23 18:00:11 bir7 Exp $
+  *		$Id: wereg.h,v     0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ /* INET feature selection. */
+ #define IP_HAVE_GW	1		/* enable IP gatewaying		*/
+ #define IP_GW_SAME	1		/* see comment in ip_forward()	*/
+ #define IP_HAVE_OPTS	0		/* enable IP option processing	*/
+ #define SK_BSD_BUG	0		/* emulate BSD 'ftp' bug	*/
+ #define ICMP_PINGBC	0		/* reply to broadcast ECHO rq's	*/
+ #define TCP_DELAY_ACK	0		/* default TCP "ack" behaviour	*/
+ 
+ 
+ /* Has the master NETDBG flag been set? */
+ #undef	INET_DEBUG
+ #ifdef	NETDBG
+ #   undef  INET_DEBUG
+ #   define INET_DEBUG
+ #endif
+ 
+ #ifdef	INET_DEBUG
+ #  define	DPRINTF(x)	inet_dprintf x 
+ #else
+ #   define	DPRINTF(x)	/*zilch*/
+ #endif
+ 
+ 
+ extern int		inet_proto_init(struct ddi *ddi);
+ 
+ 
+ /* End of INET.H */
*** original/net/inet/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/ioctl.c	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,99 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		This file handles all IP-specific I/O control calls.
+  *
+  * Version:	@(#)ioctl.c	1.1.5	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "raw.h"
+ #include "route.h"
+ #include "sock.h"
+ 
+ 
+ int
+ inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+ {
+   struct ifreq ifr;
+   struct ifnet *dev;
+   struct ifaddr *ifa;
+   struct sock *sk;
+   int err;
+ 
+   DPRINTF((DBG_INET, "INET: inet_ioctl(0x%X, 0x%X, 0x%X)\n", sock, cmd, arg));
+ 
+   sk = NULL;
+   if (sock != NULL) {
+ 	if ((sk = (struct sock *) sock->data) == NULL) {
+ 		printk("INET: ioctl: warning: sock->data = NULL !\n");
+ 		return(0);
+ 	}
+   }
+ 
+   switch(cmd) {
+ 	case FIOSETOWN:
+ 	case SIOCSPGRP:
+ 		if (sk != NULL) sk->proc = get_fs_long((long *) arg);
+ 		break;
+ 
+ 	case FIOGETOWN:
+ 	case SIOCGPGRP:
+ 		if (sk != NULL) {
+ 			err = verify_area(VERIFY_WRITE, (long *) arg,
+ 					  sizeof(long));
+ 			if (err < 0) return(err);
+ 			put_fs_long(sk->proc, (long *) arg);
+ 		}
+ 		break;
+ 
+ 	case DDIOCSDBG:
+ 		return(inet_dbg_ioctl(arg, DBG_INET));
+ 
+ 	case SIOCADDRT:
+ 	case SIOCDELRT:
+ 		return(inet_rt_ioctl(cmd, arg));
+ 
+ 	case SIOCSIFADDR:
+ 		err = verify_area(VERIFY_READ, (char *) arg,
+ 				  sizeof(struct ifreq));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&ifr, (char *) arg, sizeof(struct ifreq));
+ 
+ 		/* See which interface the caller is talking about. */
+ 		if ((dev = dev_get_name(ifr.ifr_name)) == NULL) {
+ 			DPRINTF((2, "INET: SIOC: unk iface \"%s\" requested.\n",
+ 								ifr.ifr_name));
+ 			return(-EINVAL);
+ 		}
+ 		ifa = dev_add_addr(dev, AF_INET);
+ 		if (ifa == NULL) {
+ 			printk("INET: Huh?  Interface has no IP address?\n");
+ 			return(-EINVAL);
+ 		}
+ 		IN_IFMASK(ifa) = in_getmask(IN_IFADDR(ifa), NULL, NULL);
+ 		IN_IFBRDADDR(ifa) = IN_IFADDR(ifa) | ~IN_IFMASK(ifa);
+ 		break;
+ 
+ 	case SIOCSIFDSTADDR:
+ 	case SIOCSIFBRDADDR:
+ 	case SIOCSIFNETMASK:
+ 		break;	/* always OK */
+ 
+ 	default:
+ 		if (!sk || !sk->prot->ioctl) return(-EINVAL);
+ 		return(sk->prot->ioctl(sk, cmd, arg));
+   }
+ 
+   return(0);
+ }
*** original/net/inet/ip.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/ip.c	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,1636 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		The Internet Protocol (IP) module as per RFC-791.
+  *
+  * Version:	@(#)ip.c	1.1.43	04/23/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "route.h"
+ #include "sock.h"
+ #include <linux/if_ether.h>
+ 
+ 
+ #define IP_FRAG_TIME	(30 * HZ)		/* fragment lifetime	*/
+ 
+ 
+ /* Describe an IP fragment. */
+ struct ipfrag {
+   int		offset;		/* offset of fragment in IP datagram	*/
+   int		end;		/* last byte of data in datagram	*/
+   int		len;		/* length of this fragment		*/
+   struct sk_buff *skb;		/* complete received fragment		*/
+   unsigned char	*ptr;		/* pointer into real fragment data	*/
+   struct ipfrag	*next;		/* linked list pointers			*/
+   struct ipfrag	*prev;
+ };
+ 
+ /* Describe an entry in the "incomplete datagrams" queue. */
+ struct ipq {
+   unsigned char	*mac;		/* pointer to MAC header		*/
+   struct ip	*iph;		/* pointer to IP header			*/
+   short		len;		/* total length of original datagram	*/
+   short		last_seen;	/* last fragment has arrived		*/
+   short		ihlen;		/* length of the IP header		*/
+   short 	maclen;		/* length of the MAC header		*/
+   struct timer_list timer;	/* when will this queue expire?		*/
+   struct ipfrag	*fragments;	/* linked list of received fragments	*/
+   struct ipq	*next;		/* linked list pointers			*/
+   struct ipq	*prev;
+ };
+ 
+ 
+ static struct ipq *ipqueue = NULL;		/* IP fragment queue	*/
+ static int ip_ident = 0;			/* IP dgram identifier	*/
+ struct ip_parms ip_parms = {			/* IP operating parms	*/
+   IPVERSION,
+   MAXTTL,
+   DEFTTL,
+   IPFRAGTTL,
+   IPTTLDEC,
+   IP_MAXPACKET,
+   IP_MSS
+ };
+ 
+ 
+ #ifdef INET_DEBUG
+ static void
+ ip_print(struct ip *iph)
+ {
+ #if 0
+   unsigned char buff[32];
+ #endif
+   unsigned char *ptr;
+   int addr, len, i;
+ 
+   /* Dump the IP header. */
+   i = ntohs(iph->ip_off);
+   printk("IP: ver=%d ihl=%d tos=0x%02X len=%d id=0x%04X ttl=%d\n",
+ 		iph->ip_v, iph->ip_hl, iph->ip_tos, ntohs(iph->ip_len),
+ 		ntohs(iph->ip_id), iph->ip_ttl);
+   printk("    prot=0x%04X check=0x%04X offset=0x%04X flags:",
+ 		iph->ip_p, iph->ip_sum, (i & IP_OFFSET));
+   if (i & IP_CE) printk(" CE");
+   if (i & IP_MF) printk(" MF");
+   if (i & IP_DF) printk(" DF");
+   printk("\n    source=%s ", in_ntoa(iph->ip_src.s_addr));
+   printk("dest=%s\n", in_ntoa(iph->ip_dst.s_addr));
+   printk("    ----\n");
+ 
+   /* Dump the data. */
+   ptr = (unsigned char *)(iph + 1);
+   addr = 0;
+   len = ntohs(iph->ip_len) - (iph->ip_hl * sizeof(unsigned long));
+ #if 1
+   printk("LEN=%d\n", len);
+ #else
+   while (len > 0) {
+ 	printk("    %04X: ", addr);
+ 	for(i = 0; i < 16; i++) {
+ 		if (len > 0) {
+ 			printk("%02X ", (*ptr & 0xFF));
+ 			buff[i] = *ptr++;
+ 			if (buff[i] < 32 || buff[i] > 126) buff[i] = '.';
+ 		} else {
+ 			printk("   ");
+ 			buff[i] = ' ';
+ 		}
+ 		addr++;
+ 		len--;
+ 	};
+ 	buff[i] = '\0';
+ 	printk("  \"%s\"\n", buff);
+   }
+   printk("    ----\n\n");
+ #endif
+ }
+ #endif	/* INET_DEBUG */
+ 
+ 
+ /*
+  * The "retransmit backoff" function - subject of much
+  * research.  Use binary exponential up to retry #4, and
+  * quadratic after that.  This yields the sequence
+  *
+  *	1, 2, 4, 8, 16, 25, 36, 49, 64, 81, 100 ...
+  */
+ int
+ ip_backoff(int n)
+ {
+   if (n <= 4) return(1 << n);	/* binary exponential back off */
+     else return(n * n);		/* quadratic back off */
+ }
+ 
+ 
+ /*
+  * This routine sorts the send list, and resets the
+  * sk->send_head and sk->send_tail pointers.
+  */
+ void
+ ip_sort_send(struct sock *sk)
+ {
+   struct sk_buff *list = NULL;
+   struct sk_buff *skb, *skb2, *skb3;
+ 
+   for (skb = sk->send_head; skb != NULL; skb = skb2) {
+ 	skb2 = skb->link3;
+ 	if (list == NULL || before(skb->seq, list->seq)) {
+ 		skb->link3 = list;
+ 		sk->send_tail = skb;
+ 		list = skb;
+ 	} else {
+ 		for (skb3 = list; ; skb3 = skb3->link3) {
+ 			if (skb3->link3 == NULL ||
+ 			    before(skb->seq, skb3->link3->seq)) {
+ 				skb->link3 = skb3->link3;
+ 				skb3->link3 = skb;
+ 				if (skb->link3 == NULL) sk->send_tail = skb;
+ 				break;
+ 			}
+ 		}
+ 	}
+   }
+   sk->send_head = list;
+ }
+   
+ 
+ /*
+  * This routine does all the checksum computations that don't
+  * require anything special (like copying or special headers).
+  */
+ unsigned short
+ ip_compute_csum(unsigned char * buff, int len)
+ {
+   unsigned long sum = 0;
+ 
+   /* Do the first multiple of 4 bytes and convert to 16 bits. */
+   if (len > 3) {
+ 	__asm__("clc\n"
+ 	        "1:\t"
+ 	    	"lodsl\n\t"
+ 	    	"adcl %%eax, %%ebx\n\t"
+ 	    	"loop 1b\n\t"
+ 	    	"adcl $0, %%ebx\n\t"
+ 	    	"movl %%ebx, %%eax\n\t"
+ 	    	"shrl $16, %%eax\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum) , "=S" (buff)
+ 	        : "0" (sum), "c" (len >> 2) ,"1" (buff)
+ 	        : "ax", "cx", "si", "bx" );
+   }
+   if (len & 2) {
+ 	__asm__("lodsw\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum), "=S" (buff)
+ 	        : "0" (sum), "1" (buff)
+ 	        : "bx", "ax", "si");
+   }
+   if (len & 1) {
+ 	__asm__("lodsb\n\t"
+ 	    	"movb $0, %%ah\n\t"
+ 	    	"addw %%ax, %%bx\n\t"
+ 	    	"adcw $0, %%bx"
+ 	        : "=b" (sum), "=S" (buff)
+ 	        : "0" (sum), "1" (buff)
+ 	        : "bx", "ax", "si");
+   }
+   sum =~sum;
+   return(sum & 0xFFFF);
+ }
+ 
+ 
+ #if IP_HAVE_OPTS
+ /*
+  * These two routines will do routing.
+  * However, because the IP OPTIONS do not work yet (only because
+  * we need better memory handling functions, like mbufs, instead
+  * of the current sk_buff's...), we don't do anything just yet.
+  */
+ static void
+ ip_strict_route(struct ip *iph, struct options *opt)
+ {
+   /* NOT YET */
+ }
+ 
+ 
+ static void
+ ip_loose_route(struct ip *iph, struct options *opt)
+ {
+   /* NOT YET */
+ }
+ #endif	/* IP_HAVE_OPTS */
+ 
+ 
+ /* This routine will check to see if we have lost a gateway. */
+ void
+ ip_route_check(unsigned long daddr)
+ {
+   /* NOT YET */
+ }
+ 
+ 
+ /*
+  * Take an skb, and fill in the MAC header.
+  * On interfaces that support the ARP protocol (indicated by the
+  * absence of the IFF_NOARP flag, *AND* the absence of the flags
+  * called IFF_LOOPBACK and IFF_POINTOPOINT), we may have to call
+  * ARP to see if the IP address we have can be translated into
+  * some hardware address.  If so, we can indeed fill in the MAC
+  * header.  Otherwise, we have to finish doing as much as we can,
+  * and then call ARP to queue the packet for later transmission.
+  * We indicate this by setting the 'arp' bit to zero ("we still
+  * need ARP").
+  *
+  * Note that function, and its associated mess, will disappear
+  * in the near future, as the new code will have proper protocol
+  * layering. -FvK
+  */
+ static int
+ ip_mac_header(struct sk_buff *skb, unsigned long daddr, struct ifnet *dev)
+ {
+   int mac;
+ 
+   DPRINTF((DBG_IP, "IP: mac_header: skb=0x%X dev=0x%X daddr=%s\n",
+ 					skb, dev, in_ntoa(daddr)));
+ 
+   skb->arp = 1;		/* no ARP needed (yet) for this packet */
+   skb->dev = dev;
+   SA_FAMILY(skb->target) = AF_INET;
+   SA_ADDR(skb->target) = daddr;
+ 
+   if (dev->mac1 != NULL) {
+ 	struct sockaddr sa, sin;
+ 	struct sockaddr *sap;
+ 	int arp;
+ 
+ 	sap = &sa;
+ 	if ((dev->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_NOARP)) == 0) {
+ 		switch(in_chk_addr(daddr)) {
+ 			case IS_BROADCAST:	/* broadcasts */
+ 				sap = dev->mac_bget(dev);
+ 				break;
+ 
+ 			case IS_MYADDR:		/* local device address */
+ 				sap = dev->mac_aget(dev);
+ 				break;
+ 
+ 			default:
+ 				SA_FAMILY(sin) = AF_INET;
+ 				SA_ADDR(sin) = daddr;
+ 
+ 				/*
+ 				 * Tell ARP which type of hardware it
+ 				 * should be looking for. If set to 0
+ 				 * (AF_UNSPEC), ARP will pick the first
+ 				 * matching entry for this address.
+ 				 */
+ 				SA_FAMILY(sa) =
+ 					SA_FAMILY(dev->if_macaddr->ifa_addr);
+ 
+ 				arp = arp_find(&sin, &sa, dev);
+ 				if (arp != 0) skb->arp = 0;
+ 		}
+ 	}
+ 	mac = dev->mac1(dev, skb, ETH_P_IP, &(dev->if_macaddr->ifa_addr), sap);
+   } else mac = 0;
+ 
+   return(mac);
+ }
+ 
+ 
+ /*
+  * This function builds an array of IP options after the basic IP
+  * header, and adapts the IP header length accordingly.  We fetch
+  * our data from the in-core IP option structure.
+  */
+ static void
+ ip_build_options(struct ip *iph, struct options *opt)
+ {
+ #if IP_HAVE_OPTS
+   unsigned char *ptr;
+   int count;
+ 
+   /* If there is nothing, do nothing either... */
+   if (opt == NULL) return;
+ 
+   /* Mark the end of the options. */
+   count = 0;
+   ptr = (unsigned char *) (iph + 1);
+   *ptr = IPOPT_EOL;
+   count++;
+ 
+   /* Adapt the IP IHL field. */
+   iph->ip_hl += ((count + sizeof(unsigned long) - 1) / sizeof(unsigned long));
+ #endif	/* IP_HAVE_OPTS */
+ }
+ 
+ 
+ /*
+  * This function decodes the IP options that are encoded in the IP
+  * basic header, and copies the decoded values into an in-core
+  * option structure.  It returns 0 if all went well.
+  */
+ static int
+ ip_do_options(struct ip *iph, struct options *opt)
+ {
+ #if IP_HAVE_OPTS
+   unsigned char *buff;
+   int done = 0;
+   int i, len = sizeof(struct ip);
+ #endif
+ 
+   /* Zero out the options. */
+   opt->record_route.route_size = 0;
+   opt->loose_route.route_size  = 0;
+   opt->strict_route.route_size = 0;
+   opt->tstamp.ipt_ptr          = 0;
+   opt->security                = 0;
+   opt->compartment             = 0;
+   opt->handling                = 0;
+   opt->stream                  = 0;
+   opt->tcc                     = 0;
+ 
+ #if IP_HAVE_OPTS
+   /* Advance the pointer to start at the options. */
+   buff = (unsigned char *)(iph + 1);
+ 
+   /* Now start the processing. */
+   while (!done && len < iph->ip_hl * sizeof(unsigned long)) switch(*buff) {
+ 	case IPOPT_EOL:
+ 		done = 1;
+ 		break;
+ 
+ 	case IPOPT_NOP:
+ 		buff++;
+ 		len++;
+ 		break;
+ 
+ 	case IPOPT_SECURITY:
+ 		buff++;
+ 		if (*buff != 11) return(1);
+ 		buff++;
+ 		opt->security = ntohs(*(unsigned short *)buff);
+ 		buff += 2;
+ 		opt->compartment = ntohs(*(unsigned short *)buff);
+ 		buff += 2;
+ 		opt->handling = ntohs(*(unsigned short *)buff);
+ 		buff += 2;
+ 	  	opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1));
+ 	  	buff += 3;
+ 	  	len += 11;
+ 	  	break;
+ 
+ 	case IPOPT_LSRR:
+ 		buff++;
+ 		if ((*buff - 3)% 4 != 0) return(1);
+ 		len += *buff;
+ 		opt->loose_route.route_size = (*buff -3)/4;
+ 		buff++;
+ 		if (*buff % 4 != 0) return(1);
+ 		opt->loose_route.pointer = *buff/4 - 1;
+ 		buff++;
+ 		buff++;
+ 		for (i = 0; i < opt->loose_route.route_size; i++) {
+ 			if (i >= MAX_ROUTE) return(1);
+ 			opt->loose_route.route[i] = *(unsigned long *)buff;
+ 			buff += 4;
+ 		}
+ 		break;
+ 
+ 	case IPOPT_SSRR:
+ 		buff++;
+ 		if ((*buff - 3)% 4 != 0) return(1);
+ 		len += *buff;
+ 		opt->strict_route.route_size = (*buff -3)/4;
+ 		buff++;
+ 		if (*buff % 4 != 0) return(1);
+ 		opt->strict_route.pointer = *buff/4 - 1;
+ 		buff++;
+ 		buff++;
+ 		for (i = 0; i < opt->strict_route.route_size; i++) {
+ 			if (i >= MAX_ROUTE) return(1);
+ 			opt->strict_route.route[i] = *(unsigned long *)buff;
+ 			buff += 4;
+ 		}
+ 		break;
+ 
+ 	case IPOPT_RR:
+ 		buff++;
+ 		if ((*buff - 3)% 4 != 0) return(1);
+ 		len += *buff;
+ 		opt->record_route.route_size = (*buff -3)/4;
+ 		buff++;
+ 		if (*buff % 4 != 0) return(1);
+ 		opt->record_route.pointer = *buff/4 - 1;
+ 		buff++;
+ 		buff++;
+ 		for (i = 0; i < opt->record_route.route_size; i++) {
+ 			if (i >= MAX_ROUTE) return(1);
+ 			opt->record_route.route[i] = *(unsigned long *)buff;
+ 			buff += 4;
+ 		}
+ 		break;
+ 
+ 	case IPOPT_SATID:
+ 		len += 4;
+ 		buff +=2;
+ 		opt->stream = *(unsigned short *)buff;
+ 		buff += 2;
+ 		break;
+ 
+ 	case IPOPT_TS:
+ #if 0	/* FIXME: soon !!! */
+ 		buff++;
+ 		len += *buff;
+ 		if (*buff % 4 != 0) return(1);
+ 		opt->tstamp.ipt_len = *buff / 4 - 1;
+ 		buff++;
+ 		if ((*buff - 1) % 4 != 0) return(1);
+ 		opt->tstamp.ipt_ptr = (*buff-1)/4;
+ 		buff++;
+ 		opt->tstamp.x.full_char = *buff;
+ 		buff++;
+ 		for (i = 0; i < opt->tstamp.len; i++) {
+ 			opt->tstamp.data[i] = *(unsigned long *)buff;
+ 			buff += 4;
+ 		}
+ #endif
+ 		break;
+ 
+ 	default:
+ 		return(1);
+   }
+ 
+   if (opt->record_route.route_size == 0) {
+ 	if (opt->strict_route.route_size != 0) {
+ 		memcpy(&(opt->record_route), &(opt->strict_route),
+ 					     sizeof(opt->record_route));
+ 	} else if (opt->loose_route.route_size != 0) {
+ 		memcpy(&(opt->record_route), &(opt->loose_route),
+ 					     sizeof(opt->record_route));
+ 	}
+   }
+ 
+   if (opt->strict_route.route_size != 0 &&
+       opt->strict_route.route_size != opt->strict_route.pointer) {
+ 	ip_strict_route(iph, opt);
+ 	return(0);
+   }
+ 
+   if (opt->loose_route.route_size != 0 &&
+       opt->loose_route.route_size != opt->loose_route.pointer) {
+ 	ip_loose_route(iph, opt);
+ 	return(0);
+   }
+ #endif	/* IP_HAVE_OPTS */
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * This routine builds the appropriate hardware/IP headers
+  * for the routine.  It assumes that if *dev != NULL then
+  * the protocol knows what it's doing, otherwise it uses
+  * the routing tables to select a device struct.
+  */
+ int
+ ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
+ 		struct ifnet **dev, int type, struct options *opt,
+ 		int tos, int ttl, int noroute)
+ {
+   struct ip *iph;
+   struct rtable *rt;
+   unsigned char *buff;
+   unsigned long raddr;
+   int mac;
+ 
+   DPRINTF((DBG_IP, "IP: build_header: skb=0x%X *dev=0x%X saddr=%s ",
+ 				skb, *dev, in_ntoa(saddr)));
+   DPRINTF((DBG_IP, "daddr=%s type=%d opt=0x%X tos=0x%02X ttl=%d nr=%d\n",
+ 			in_ntoa(daddr), type, opt, tos, ttl, noroute));
+   buff = (unsigned char *)(skb + 1);
+ 
+   /* See if we need to look up the device. */
+   raddr = daddr;
+   if (*dev == NULL) {
+ 	/*
+ 	 * The 'noroute' flag comes from the sk->dontroute option, and can
+ 	 * be used by programs to explicitly tell IP to which interface to
+ 	 * send the packet to.  We only have to call the dev_check routine
+ 	 * in that case, as we completely bypass the IP router.
+ 	 */
+ 	if (noroute) {
+ 		*dev = in_dev_check(daddr);
+ 		if (*dev == NULL) return(-ENETUNREACH);
+ 	} else {
+ 		rt = inet_route(daddr, opt);
+ 		if (rt == NULL) return(-ENETUNREACH);
+ 		*dev = rt->rt_ifp;
+ 
+ 		/* If there is a GW record, use the gateway address. */
+ 		if (rt->rt_flags & RTF_GATEWAY) raddr = SA_ADDR(rt->rt_gateway);
+ 	}
+   } else {
+ 	/* We still need the address of the first hop. */
+ 	rt = inet_route(daddr, opt);
+ 	if (rt == NULL) return(-ENETUNREACH);
+ 	if (*dev != rt->rt_ifp) {
+ 		printk("IP: Fishy stuff in ip_build_header !!!\n");
+ 		printk("    IP suggested interface %s, router says %s ....\n",
+ 			(*dev == NULL) ? "NULL" : (*dev)->if_name,
+ 			(rt->rt_ifp == NULL) ? "NULL" : rt->rt_ifp->if_name);
+ 			printk("    SRC=%s ", in_ntoa(saddr));
+ 		printk("  DST=%s  TYPE=%d (0x%04X)\n\n",
+ 				in_ntoa(daddr), type, type);
+ 	}
+ 
+ 	/* If there is a GW record, use the gateway address. */
+ 	if (rt->rt_flags & RTF_GATEWAY) raddr = SA_ADDR(rt->rt_gateway);
+   }
+ 
+   /*
+    * Does this ever happen?
+    * Yes, it does, while using the BOOTP client :)
+    */
+   if (saddr == INADDR_ANY) {
+ 	DPRINTF((DBG_IP, "IP: Source address INADDR_ANY - not good !\n"));
+   }
+   DPRINTF((DBG_IP, "IP: build_header: saddr=%s\n", in_ntoa(saddr)));
+   skb->dev = *dev;
+   skb->saddr = saddr;
+ 
+   /* Now build the MAC header. */
+   mac = ip_mac_header(skb, raddr, *dev);
+   buff += mac;
+ 
+   /*
+    * Now build the IP header.
+    * If we are using IPPROTO_RAW, then we don't need an IP header,
+    * since one is being supplied to us by the application program.
+    */
+   if (type == IPPROTO_RAW) return(mac);
+ 
+   iph = (struct ip *) buff;
+   iph->ip_v = ip_parms.version;
+   iph->ip_hl = (sizeof(struct ip) / sizeof(unsigned long));
+   iph->ip_tos = tos;
+   iph->ip_off = 0;	/* also clears FLAGS ! */
+   iph->ip_ttl = ttl;
+   iph->ip_src.s_addr = saddr;
+   iph->ip_dst.s_addr = daddr;
+   iph->ip_p = type;
+   iph->ip_id = htons(ip_ident++);
+   if (ip_ident == 32760) ip_ident = 1;
+ 
+   /* Setup the IP options. */
+   ip_build_options(iph, opt);
+ 
+   /* Return IP hdr (+ opts) + MAC header length. */
+   return((iph->ip_hl * sizeof(unsigned long)) + mac);
+ }
+ 
+ 
+ /* Check the header of an incoming IP datagram. */
+ static int
+ ip_csum(struct ip *iph)
+ {
+   if (ip_compute_csum((unsigned char *)iph,
+ 		      (iph->ip_hl * sizeof(unsigned long))) == 0) return(0);
+   return(1);
+ }
+ 
+ 
+ /* Generate a checksym for an outgoing IP datagram. */
+ static void
+ ip_send_check(struct ip *iph)
+ {
+   iph->ip_sum = 0;
+   iph->ip_sum = ip_compute_csum((unsigned char *)iph,
+ 			       (iph->ip_hl * sizeof(unsigned long)));
+ }
+ 
+ 
+ /* Create a new fragment entry. */
+ static struct ipfrag *
+ ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
+ {
+   struct ipfrag *fp;
+ 
+   fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
+   if (fp == NULL) {
+ 	printk("IP: frag_create: no memory left !\n");
+ 	return(NULL);
+   }
+   memset(fp, 0, sizeof(struct ipfrag));
+ 
+   /* Fill in the structure. */
+   fp->offset = offset;
+   fp->end = end;
+   fp->len = (end - offset);
+   fp->skb = skb;
+   fp->ptr = ptr;
+ 
+   return(fp);
+ }
+ 
+ 
+ /*
+  * Find the correct entry in the "incomplete datagrams" queue for
+  * this IP datagram, and return the queue entry address if found.
+  */
+ static struct ipq *
+ ip_find(struct ip *iph)
+ {
+   struct ipq *qp;
+   struct ipq *qplast;
+ 
+   cli();
+   qplast = NULL;
+   for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next) {
+ 	if (iph->ip_id         == qp->iph->ip_id &&
+ 	    iph->ip_src.s_addr == qp->iph->ip_src.s_addr &&
+ 	    iph->ip_dst.s_addr == qp->iph->ip_dst.s_addr &&
+ 	    iph->ip_p          == qp->iph->ip_p) {
+ 		/*
+ 		 * Make sure the entry does not expire while
+ 		 * we are using it.  Thanks, Alan!
+ 		 */
+ 		del_timer(&qp->timer);
+ 		sti();
+ 		return(qp);
+ 	}
+   }
+   sti();
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * Remove an entry from the "incomplete datagrams" queue, either
+  * because we completed, reassembled and processed it, or because
+  * it timed out.
+  */
+ static void
+ ip_free(struct ipq *qp)
+ {
+   struct ipfrag *fp;
+   struct ipfrag *xp;
+ 
+   /* Stop the timer for this entry. */
+   del_timer(&qp->timer);
+ 
+   /* Remove this entry from the "incomplete datagrams" queue. */
+   cli();
+   if (qp->prev == NULL) {
+ 	ipqueue = qp->next;
+ 	if (ipqueue != NULL) ipqueue->prev = NULL;
+   } else {
+ 	qp->prev->next = qp->next;
+ 	if (qp->next != NULL) qp->next->prev = qp->prev;
+   }
+   sti();
+ 
+   /* Release all fragment data. */
+   fp = qp->fragments;
+   while (fp != NULL) {
+ 	xp = fp->next;
+ 	fp->skb->sk = NULL;
+ 	inet_kfree_skb(fp->skb, FREE_READ);
+ 	kfree_s(fp, sizeof(struct ipfrag));
+ 	fp = xp;
+   }
+ 
+   /* Release the MAC header. */
+   kfree_s(qp->mac, qp->maclen);
+ 
+   /* Release the IP header. */
+   kfree_s(qp->iph, qp->ihlen + 8);
+ 
+   /* Finally, release the queue descriptor itself. */
+   kfree_s(qp, sizeof(struct ipq));
+ }
+ 
+ 
+ /* Oops- a fragment queue timed out.  Kill it and send an ICMP reply. */
+ static void
+ ip_expire(unsigned long arg)
+ {
+   struct ipq *qp;
+ 
+   qp = (struct ipq *)arg;
+   DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp));
+ 
+   /* Send an ICMP "Fragment Reassembly Timeout" message. */
+   icmp_send(qp->iph->ip_src.s_addr, ICMP_TIMXCEED,
+ 	    ICMP_TIMXCEED_REASS, qp->iph);
+ 
+   /* Nuke the fragment queue. */
+   ip_free(qp);
+ }
+ 
+ 
+ /*
+  * ICMP received a timeout for a fragment, so we'd better nuke
+  * its queue.
+  */
+ void
+ ip_icmpexpire(struct ip *iph)
+ {
+   struct ipq *qp;
+ 
+   DPRINTF((DBG_IP, "IP: icmpexpire: ICMP tells us to nuke "));
+   qp = ip_find(iph);
+   if (qp != NULL) {
+ 	DPRINTF((DBG_IP, "queue 0x%X !\n", qp));
+ 	ip_free(qp);
+   } else {
+ 	DPRINTF((DBG_IP, "nothing ???\n"));
+   }
+ }
+ 
+ 
+ /*
+  * Add an entry to the 'ipq' queue for a newly received IP datagram.
+  * We will (hopefully :-) receive all other fragments of this datagram
+  * in time, so we just create a queue for this datagram, in which we
+  * will insert the received fragments at their respective positions.
+  */
+ static struct ipq *
+ ip_create(struct sk_buff *skb, struct ip *iph)
+ {
+   struct ipq *qp;
+   int maclen;
+   int ihlen;
+ 
+   qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
+   if (qp == NULL) {
+ 	printk("IP: create: no memory left !\n");
+ 	return(NULL);
+   }
+   memset(qp, 0, sizeof(struct ipq));
+ 
+   /* Allocate memory for the MAC header. */
+   maclen = ((unsigned long) iph) - ((unsigned long) (skb + 1));
+   qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
+   if (qp->mac == NULL) {
+ 	printk("IP: create: no memory left !\n");
+ 	kfree_s(qp, sizeof(struct ipq));
+ 	return(NULL);
+   }
+ 
+   /* Allocate memory for the IP header (plus 8 octects for ICMP). */
+   ihlen = (iph->ip_hl * sizeof(unsigned long));
+   qp->iph = (struct ip *) kmalloc(ihlen + 8, GFP_ATOMIC);
+   if (qp->iph == NULL) {
+ 	printk("IP: create: no memory left !\n");
+ 	kfree_s(qp->mac, maclen);
+ 	kfree_s(qp, sizeof(struct ipq));
+ 	return(NULL);
+   }
+ 
+   /* Fill in the structure. */
+   memcpy(qp->mac, (skb + 1), maclen);
+   memcpy(qp->iph, iph, ihlen + 8);
+   qp->len = 0;
+   qp->ihlen = ihlen;
+   qp->maclen = maclen;
+   qp->fragments = NULL;
+   qp->last_seen = 0;
+ 
+   /* Start a timer for this entry. */
+   qp->timer.expires = IP_FRAG_TIME;		/* about 30 seconds	*/
+   qp->timer.data = (unsigned long) qp;		/* pointer to queue	*/
+   qp->timer.function = ip_expire;		/* expire function	*/
+   add_timer(&qp->timer);
+ 
+   /* Add this entry to the queue. */
+   qp->prev = NULL;
+   cli();
+   qp->next = ipqueue;
+   if (qp->next != NULL) qp->next->prev = qp;
+   ipqueue = qp;
+   qp->last_seen = 0;
+   sti();
+   return(qp);
+ }
+ 
+ 
+ /* See if a fragment queue is complete. */
+ static int
+ ip_done(struct ipq *qp)
+ {
+   struct ipfrag *fp;
+   int offset;
+ 
+   /* Only possible if we received the final fragment. */
+   if (qp->last_seen == 0) return(0);
+ 
+   /* Check all fragment offsets to see if they connect. */
+   fp = qp->fragments;
+   offset = 0;
+   while (fp != NULL) {
+ 	if (fp->offset > offset) return(0);	/* fragment(s) missing */
+ 	offset = fp->end;
+ 	fp = fp->next;
+   }
+ 
+   /* All fragments are present. */
+   return(1);
+ }
+ 
+ 
+ /* Build a new IP datagram from all its fragments. */
+ static struct sk_buff *
+ ip_glue(struct ipq *qp)
+ {
+   struct sk_buff *skb;
+   struct ip *iph;
+   struct ipfrag *fp;
+   unsigned char *ptr;
+   int count, len;
+ 
+   /* Allocate a new buffer for the datagram. */
+   len = (qp->maclen + qp->ihlen + qp->len);
+   if ((skb = skb_create(len)) == NULL) {
+ 	printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp);
+ 	ip_free(qp);
+ 	return(NULL);
+   }
+ 
+   /* Fill in the basic details. */
+   skb->len = (len - qp->maclen);
+   skb->raw = (unsigned char *) (skb + 1);
+   skb->free = 1;
+   skb->lock = 1;
+ 
+   /* Copy the original MAC and IP headers into the new buffer. */
+   ptr = (unsigned char *) skb->raw;
+   memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen);
+   ptr += qp->maclen;
+   memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
+   ptr += qp->ihlen;
+   skb->raw += qp->maclen;
+   count = 0;
+ 
+   /* Copy the data portions of all fragments into the new buffer. */
+   fp = qp->fragments;
+   while(fp != NULL) {
+ 	if ((count + fp->len) > skb->len) {
+ 		printk("IP: invalid fragment list (data size %d/%d)\n",
+ 			(count + fp->len), skb->len);
+ 		ip_free(qp);
+ 		return(NULL);
+ 	}
+ 	memcpy((ptr + fp->offset), fp->ptr, fp->len);
+ 	count += fp->len;
+ 	fp = fp->next;
+   }
+ 
+   /* We glued together all fragments, so remove the queue entry. */
+   ip_free(qp);
+ 
+   /* Done with all fragments. Fixup the new IP header. */
+   iph = SKBIP(skb);
+   iph->ip_off = 0;
+   iph->ip_len = htons((iph->ip_hl * sizeof(unsigned long)) + count);
+   return(skb);
+ }
+ 
+ 
+ /* Process an incoming IP datagram fragment. */
+ static struct sk_buff *
+ ip_defrag(struct ip *iph, struct sk_buff *skb, struct ifnet *dev)
+ {
+   struct ipfrag *prev, *next;
+   struct ipfrag *tfp;
+   struct ipq *qp;
+   struct sk_buff *skb2;
+   unsigned char *ptr;
+   int flags, offset;
+   int i, ihl, end;
+ 
+   /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
+   qp = ip_find(iph);
+ 
+   /* Is this a non-fragmented datagram? */
+   offset = ntohs(iph->ip_off);
+   flags = offset & ~IP_OFFSET;
+   offset &= IP_OFFSET;
+   if (((flags & IP_MF) == 0) && (offset == 0)) {
+ 	if (qp != NULL) ip_free(qp);	/* Huh? How could this exist?? */
+ 	return(skb);
+   }
+   offset <<= 3;		/* offset is in 8-byte chunks */
+ 
+   /*
+    * If the queue already existed, keep restarting its timer as long
+    * as we still are receiving fragments.  Otherwise, create a fresh
+    * queue entry.
+    */
+   if (qp != NULL) {
+ 	del_timer(&qp->timer);
+ 	qp->timer.expires = IP_FRAG_TIME;	/* about 30 seconds	*/
+ 	qp->timer.data = (unsigned long) qp;	/* pointer to queue	*/
+ 	qp->timer.function = ip_expire;		/* expire function	*/
+ 	add_timer(&qp->timer);
+   } else {
+ 	if ((qp = ip_create(skb, iph)) == NULL) {
+ 		skb->sk = NULL; /* should already be NULL... */
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		return(NULL);
+ 	}
+   }
+ 
+   /* Determine the position of this fragment. */
+   ihl = (iph->ip_hl * sizeof(unsigned long));
+   end = offset + ntohs(iph->ip_len) - ihl;
+ 
+   /* Point into the IP datagram 'data' part. */
+   ptr = ((unsigned char *) (skb + 1)) + dev->if_machlen + ihl;
+   if (end > qp->len) qp->len = end;
+ 
+   /* Is this the final fragment? */
+   if ((flags & IP_MF) == 0) qp->last_seen = 1;
+ 
+   /*
+    * Find out which fragments are in front and at the back of us
+    * in the chain of fragments so far.  We must know where to put
+    * this fragment, right?
+    */
+   prev = NULL;
+   for(next = qp->fragments; next != NULL; next = next->next) {
+ 	if (next->offset > offset) break;	/* bingo! */
+ 	prev = next;
+   }
+ 
+   /*
+    * We found where to put this one.
+    * Check for overlap with preceeding fragment, and, if needed,
+    * align things so that any overlaps are eliminated.
+    */
+   if (prev != NULL && offset < prev->end) {
+ 	i = prev->end - offset;
+ 	offset += i;	/* ptr into datagram */
+ 	ptr += i;	/* ptr into fragment data */
+ 	DPRINTF((DBG_IP, "IP: defrag: fixed low overlap %d bytes\n", i));
+   }
+ 
+   /*
+    * Look for overlap with succeeding segments.
+    * If we can merge fragments, do it.
+    */
+   for(; next != NULL; next = tfp) {
+ 	tfp = next->next;
+ 	if (next->offset >= end) break;		/* no overlaps at all */
+ 
+ 	i = end - next->offset;			/* overlap is 'i' bytes */
+ 	next->len -= i;				/* so reduce size of	*/
+ 	next->offset += i;			/* next fragment	*/
+ 	next->ptr += i;
+ 	
+ 	/* If we get a frag size of <= 0, remove it. */
+ 	if (next->len <= 0) {
+ 		DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n",
+ 							next, next->len));
+ 		if (next->prev != NULL) next->prev->next = next->next;
+ 		  else qp->fragments = next->next;
+ 		if (tfp->next != NULL) next->next->prev = next->prev;
+ 		next->skb->sk = NULL;
+ 		inet_kfree_skb(next->skb, FREE_READ);
+ 		kfree_s(next, sizeof(struct ipq));
+ 	}
+ 	DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i));
+   }
+ 
+   if (end > offset) {				/* was not all overlap	*/
+ 	/* Insert this fragment in the chain of fragments. */
+ 	tfp = NULL;
+ 	if ((tfp = ip_frag_create(offset, end, skb, ptr)) != NULL) {
+ 		tfp->prev = prev;
+ 		tfp->next = next;
+ 		if (prev != NULL) prev->next = tfp;
+ 		  else qp->fragments = tfp;
+ 		if (next != NULL) next->prev = tfp;
+ 	} else {		/* insufficient memory for fragment	*/
+ 		skb->sk = NULL;
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		return(NULL);
+ 	}
+   } else {				/* fragment was overlap only	*/
+ 	skb->sk = NULL;
+   	inet_kfree_skb(skb, FREE_READ);
+   }
+ 
+   /*
+    * OK, so we inserted this new fragment into the chain.
+    * Check if we now have a full IP datagram which we can
+    * bump up to the IP layer...
+    */
+   if (ip_done(qp)) {
+ 	skb2 = ip_glue(qp);		/* glue together the fragments */
+ 	return(skb2);
+   }
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * This IP datagram is too large to be sent in one piece.  Break it up into
+  * smaller pieces (each of size equal to the MAC header plus IP header plus
+  * a block of the data of the original IP data part) that will yet fit in a
+  * single device frame, and queue such a frame for sending by calling the
+  * ip_queue_xmit().  Note that this is recursion, and bad things will happen
+  * if this function causes a loop...
+  */
+ void
+ ip_fragment(struct sock *sk, struct sk_buff *skb, struct ifnet *dev)
+ {
+   struct ip *iph;
+   unsigned char *raw;
+   unsigned char *ptr;
+   struct sk_buff *skb2;
+   int left, mtu, hlen, len;
+   int offset, last_frag;
+ 
+   /* Point into the IP datagram header. */
+   raw = (unsigned char *) (skb + 1);		/* ptr to raw data */
+   iph = (struct ip *) (raw + dev->if_machlen);	/* ptr to IP header */
+ 
+   /* Setup starting values. */
+   hlen = (iph->ip_hl * sizeof(unsigned long));
+   left = ntohs(iph->ip_len) - hlen;
+   hlen += dev->if_machlen;
+   mtu = (dev->if_mtu - hlen);
+   ptr = (raw + hlen);				/* ptr to IP data */
+   last_frag = !(iph->ip_off & htons(IP_MF));
+ 
+   DPRINTF((DBG_IP, "IP: Fragmentation Desired\n"));
+   DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
+ 	dev->if_name, dev->if_mtu, left, in_ntoa(iph->ip_src.s_addr)));
+   DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->ip_dst.s_addr)));
+ 
+   /* Check for any "DF" flag. */
+   if (ntohs(iph->ip_off) & IP_DF) {
+ 	DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n"));
+ 	DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
+ 		dev->if_name, dev->if_mtu, left, in_ntoa(iph->ip_src.s_addr)));
+ 	DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->ip_dst.s_addr)));
+ 
+ 	icmp_send(iph->ip_src.s_addr, ICMP_UNREACH,
+ 		  ICMP_UNREACH_NEEDFRAG, iph);
+ 	return;
+   }
+ 
+   /* Fragment the datagram. */
+   offset = 0;
+   while(left > 0) {
+ 	len = left;
+ 	if (len > mtu) len = mtu;
+ 	if ((left - len) > 0) {
+ 		len /= 8;
+ 		len *= 8;
+ 	}
+ 	DPRINTF((DBG_IP, "IP: frag: creating fragment of %u bytes (%u total)\n",
+ 							len, len + hlen));
+ 
+ 	/* Allocate buffer. */
+ 	if ((skb2 = skb_create(len + hlen)) == NULL) {
+ 		printk("IP: frag: no memory for new fragment!\n");
+ 		break;
+ 	}
+ 	skb2->arp = skb->arp;
+ 	skb2->len = len + hlen;
+ 	SA_FAMILY(skb2->target) = AF_INET;
+ 	SA_ADDR(skb2->target) = SA_ADDR(skb->target);
+ 
+ 	if (sk) sk->wmem_alloc += skb2->mem_len;
+ 
+ 	/* Copy the packet header into the new buffer. */
+ 	memcpy(skb2->raw, raw, hlen);
+ 
+ 	/* Copy a block of the IP datagram. */
+ 	memcpy(skb2->raw + hlen, ptr, len);
+ 	left -= len;
+ 
+ 	/* Fill in the new header fields. */
+ 	iph = (struct ip *) (skb2->raw + dev->if_machlen);
+ 	iph->ip_off = htons((offset >> 3));
+ 
+ 	/*
+ 	 * If we are fragmenting a datagram that was in itself a
+ 	 * fragment, make sure to preserve the MF bit until the
+ 	 * final original fragment.
+ 	 */
+ 	if ((left > 0) || !last_frag) iph->ip_off |= htons(IP_MF);
+ 	ptr += len;
+ 	offset += len;
+ 
+ 	/* Put this fragment into the sending queue. */
+ 	ip_queue_xmit(sk, dev, skb2, 1);
+   }
+ }
+ 
+ 
+ /* Forward an IP datagram to its next destination. */
+ static void
+ ip_forward(struct sk_buff *skb, struct options *opt, struct ifnet *dev)
+ {
+   struct ip *iph;
+ #if IP_HAVE_GW
+   struct ifnet *dev2;
+   struct sk_buff *skb2;
+   struct rtable *rt;
+   struct ifaddr *ifa;
+   unsigned char *ptr;
+   unsigned long raddr;
+   int mac;
+ #endif
+ 
+   /*
+    * According to the RFC, we must first decrease the TTL field. If
+    * that reaches zero, we must reply an ICMP control message telling
+    * that the packet's lifetime expired.
+    */
+   iph = SKBIP(skb);
+ #if IP_HAVE_GW
+   iph->ip_ttl -= IPTTLDEC;
+   if (iph->ip_ttl <= 0) {
+ 	DPRINTF((DBG_IP, "IP: *** datagram expired: TTL=0 (ignored) ***\n"));
+ 	DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->ip_src.s_addr)));
+ 	DPRINTF((DBG_IP, "    DST = %s (ignored)\n",
+ 				in_ntoa(iph->ip_dst.s_addr)));
+ 
+ 	/* Tell the sender its packet died... */
+ 	icmp_send(iph->ip_src.s_addr, ICMP_TIMXCEED,
+ 		  ICMP_TIMXCEED_INTRANS, iph);
+ 	return;
+   }
+ 
+   /* Re-compute the IP header checksum. */
+   ip_send_check(iph);
+ 
+   /*
+    * OK, the packet is still valid.  Fetch its destination address,
+    * and give it to the IP sender for further processing.
+    */
+   rt = inet_route(iph->ip_dst.s_addr, opt);
+   if (rt == NULL) {
+ 	DPRINTF((DBG_IP, "IP: *** routing failed ***\n"));
+ 	/* Tell the sender its packet cannot be delivered... */
+ 	icmp_send(iph->ip_src.s_addr, ICMP_UNREACH, ICMP_UNREACH_NET, iph);
+ 	return;
+   }
+   dev2 = rt->rt_ifp;
+ 
+ #if (IP_GW_SAME == 0)
+   /*
+    * If we are told to route a packet, it might be wise to
+    * refuse to do so if we are to use the same physical
+    * network for the job, to prevent packet storms from
+    * melting the network.  However, this *can* break cer-
+    * tain network setups, hence the config option here -FvK
+    */
+   if (dev2 == dev) return;
+ #endif
+ 
+   /*
+    * Gosh.  Not only is the packet valid; we even know how
+    * to forward it onto its final destination.  Can we say
+    * this is being plain lucky?
+    */
+   if ((ifa = dev_get_addr(dev2, AF_INET)) == NULL) {
+ 	printk("IP: Huh?  No IP address, but still a router?\n");
+ 	return;
+   }
+ 
+   /* If there is a GW record, use the gateway address. */
+   if (rt->rt_flags & RTF_GATEWAY) raddr = SA_ADDR(rt->rt_gateway);
+     else raddr = iph->ip_dst.s_addr;
+ 
+   /*
+    * We now allocate a new buffer, and copy the datagram into it.
+    * If the indicated interface is up and running, kick it.
+    */
+   DPRINTF((DBG_IP, "IP: *** fwd %s -> ", in_ntoa(iph->ip_src.s_addr)));
+   DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
+ 			in_ntoa(raddr), dev2->if_name, skb->len));
+ 
+   if (dev2->if_flags & IFF_UP) {
+ 	skb2 = skb_create(dev2->if_machlen + skb->len);
+ 	if (skb2 == NULL) {
+ 		printk("IP: No memory available for IP forward\n");
+ 		return;
+ 	}
+ 	ptr = (unsigned char *) skb2->raw;
+ 	skb2->len = 0;
+ 
+ 	/* Now build the MAC header. */
+ 	mac = ip_mac_header(skb2, raddr, dev2);
+ 	if (mac < 0) {
+ 		DPRINTF((DBG_IP, "IP: unable to create MAC for FORWARD!\n"));
+ 		skb2->sk = NULL;
+ 		inet_kfree_skb(skb2, FREE_WRITE);
+ 		return;
+ 	}
+ 	skb2->len += mac;
+ 	ptr += mac;
+ 
+ 	/* Copy the packet data into the new buffer. */
+ 	memcpy(ptr, iph, skb->len);
+ 	skb2->len += skb->len;
+ 		
+ 	/* Check for IP fragmentation here. */
+ 	if (skb2->len > dev2->if_mtu) {
+ 		ip_fragment(NULL, skb2, dev2);
+ 		inet_kfree_skb(skb2, FREE_WRITE);
+ 		return;
+ 	}
+ 
+ 	/* Kick the new datgram to the world... */
+ 	dev2->send(dev2, skb2, &(skb2->target), SOPRI_NORMAL);
+   }
+ #else
+   DPRINTF((DBG_IP, "IP: *** Will not forward datagrams! ***\n"));
+   DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->ip_src.s_addr)));
+   DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->ip_dst.s_addr)));
+ #endif	/* IP_HAVE_GW */
+ }
+ 
+ 
+ /* This function receives all incoming IP datagrams. */
+ static void
+ ip_recv(struct ifnet *dev, struct sk_buff *skb, struct packet_type *pt)
+ {
+   struct sk_buff *skb2;
+   struct options opt;
+   struct ip *iph;
+   int found, brd;
+ 
+   iph = SKBIP(skb);
+   memset((char *) &opt, 0, sizeof(struct options));
+   DPRINTF((DBG_IP, "<<\n"));
+ #ifdef INET_DEBUG
+   if (inet_debug & (1 << DBG_IP)) ip_print(iph);
+ #endif
+ 
+   /* A little sanity check for packets that arrive early. */
+   if (dev == NULL || dev->if_addr == NULL) {
+ 	DPRINTF((DBG_IP, "IP: recv: dev%s = NULL !\n",
+ 				dev==NULL ? "" : "->if_addr"));
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   /* Is the datagram acceptable? */
+   if ((skb->len < (iph->ip_hl * sizeof(unsigned long))) ||
+       skb->len < sizeof(struct ip) || ip_csum(iph) ||
+       ip_do_options(iph, &opt) || iph->ip_v != ip_parms.version) {
+ #if 1	/* This should save me 35 e-mail messages a week :-) */
+ 	if ((iph->ip_hl == ip_parms.version) && (iph->ip_v >= 5)) {
+ 		printk("IP: Warning !!!!!\n");
+ 		printk("    It seems to me that you have compiled this\n");
+ 		printk("    kernel with a wrong setting of the __BYTE_ORDER\n");
+ 		printk("    in the /usr/include/endian.h file.  This probably\n");
+ 		printk("    means that you have an old include file.  Check\n");
+ 		printk("    out the NET-2 HOWTO and the NET-2e Release Notes!\n");
+ 		printk("\n");
+ 	}
+ #endif
+ 	DPRINTF((DBG_IP, "IP: *** datagram error ***\n"));
+ 	DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->ip_src.s_addr)));
+ 	DPRINTF((DBG_IP, "    DST = %s (ignored)\n",
+ 				in_ntoa(iph->ip_dst.s_addr)));
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   /*
+    * Do any IP forwarding required.
+    * Note that we cannot be a forwarder on an interface that has
+    * the PROMISCUOUS mode enabled.  This mode means we reeive ALL
+    * packets on the network, so forwarding them (again) would
+    * mean a certain network meltdown...
+    */
+   if ((brd = in_chk_addr(iph->ip_dst.s_addr)) == 0) {
+ 	if (! (dev->if_flags & (IFF_PROMISC | IFF_ALLMULTI)))
+ 						ip_forward(skb, &opt, dev);
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   /* Handle incoming IP datagram fragments. */
+   skb2 = ip_defrag(iph, skb, dev);
+   if (skb2 == NULL) return;
+   iph = SKBIP(skb2);
+ 
+   /* Show the new (reassembled) datagram. */
+ #ifdef INET_DEBUG
+   if (skb2 != skb) {
+ 	DPRINTF((DBG_IP, "<<\n"));
+ 	if (inet_debug & (1 << DBG_IP)) ip_print(iph);
+   }
+ #endif
+   skb = skb2;
+ 
+   /*
+    * If the packet seems to be smaller than the original size,
+    * something must have gone wrong.  Discard the packet...
+    */
+   if (skb->len < ntohs(iph->ip_len)) {
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   /*
+    * Truncate packet to ip_len (remove any extraneous garbage
+    * that might occur in a SLIP packet.)
+    */
+   skb->len = ntohs(iph->ip_len);
+ 
+   /* Pass off the datagram to the next protocol layer. */
+   found = inet_proto_recv(dev, skb, iph, &opt);
+ 
+   /*
+    * All protocols checked.
+    * If this packet was a broadcast, we may *not* reply to it,
+    * since that causes (proven, grin) ARP storms and a leakage
+    * of memory (i.e. all ICMP reply messages get queued up for
+    * transmission...)
+    */
+   if (found == 0) {
+ 	if (brd != IS_BROADCAST) {
+ 		icmp_send(iph->ip_src.s_addr, ICMP_UNREACH,
+ 			  ICMP_UNREACH_PROTOCOL, iph);
+ 	}
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+   }
+ }
+ 
+ 
+ /*
+  * Queue a packet to be sent, and starts the transmitter if necessary.
+  * If `free' = 1 then we free the block after transmit, otherwise we
+  * don't. This routine also puts in the total length, and computes the
+  * IP checksum.
+  */
+ void
+ ip_queue_xmit(struct sock *sk, struct ifnet *dev, 
+ 	      struct sk_buff *skb, int free)
+ {
+   struct ip *iph;
+   unsigned char *ptr;
+ 
+   if (sk == NULL) free = 1;
+   skb->free = free;
+   if (dev == NULL) {
+ 	printk("IP: queue_xmit: dev = NULL\n");
+ 	inet_kfree_skb(skb, FREE_WRITE);
+ 	return;
+   }
+ 
+   /* Point into the IP datagram header. */
+   ptr = (unsigned char *)(skb + 1);
+   ptr += dev->if_machlen;
+   iph = (struct ip *)ptr;
+ 
+   /*
+    * Adjust the packet length to exclude the MAC header length.
+    * This is only needed in this layer because we have to kludge
+    * around with the (fixed-size) "skb" buffers, and we should
+    * not rely on skb->len including this length here.  At the
+    * MAC header level (i.e. an Ethernet driver), this length is
+    * again added to this field.  Until we have mbuf's... :-)
+    */
+ #if 0
+   skb->len -= dev->if_machlen;
+ #endif
+   iph->ip_len = ntohs(skb->len - dev->if_machlen);
+ 
+   skb->dev = dev;
+   skb->when = jiffies;
+ 
+   /* Compute the IP checksum of this packet. */
+   ip_send_check(iph);
+   DPRINTF((DBG_IP, ">>\n"));
+ #ifdef INET_DEBUG
+   if (inet_debug & (1 << DBG_IP)) ip_print(iph);
+ #endif
+   skb->sk = sk;
+ 
+   /*
+    * If this datagram is of the "resendable" type (indicated by the
+    * `free' parameter of this function, and thus the `free' field of
+    * the skb), initialize the socket queue.
+    *
+    * I really think this should be moved to some upper layer like
+    * TCP, as that is (currently) the only protocol dealing with
+    * resending packets... -FvK
+    */
+   if (! free) {
+ 	skb->link3 = NULL;
+ 	sk->packets_out++;
+ 	cli();
+ 	if (sk->send_head == NULL) {
+ 		sk->send_tail = skb;
+ 		sk->send_head = skb;
+ 	} else {
+ 		/* See if we've got a problem. */
+ 		if (sk->send_tail == NULL) {
+ 			printk("IP: ***bug sk->send_tail=NULL!=sk->send_head\n");
+ 			ip_sort_send(sk);
+ 		} else {
+ 			sk->send_tail->link3 = skb;
+ 			sk->send_tail = skb;
+ 		}
+ 	}
+ 	sti();
+ 
+ 	/* Start the "retransmit" timer for this packet. */
+ 	inet_reset_timer(sk, TIME_WRITE,
+ 			 ip_backoff(sk->backoff) * (sk->mdev + sk->rtt));
+   }
+ 
+   /* Check for IP fragmentation here. */
+   if (skb->len > dev->if_mtu) {
+ 	ip_fragment(sk, skb, dev);
+ 	if (free) inet_kfree_skb(skb, FREE_WRITE);
+ 	return;
+   }
+ 
+   if (sk == NULL) {
+ 	if (iph->ip_tos & IPTOS_LOWDELAY)
+ 		dev->send(dev, skb, &(skb->target), SOPRI_INTERACTIVE);
+ 	  else if (iph->ip_tos & IPTOS_THROUGHPUT)
+ 		dev->send(dev, skb, &(skb->target), SOPRI_BACKGROUND);
+ 	  else
+ 		dev->send(dev, skb, &(skb->target), SOPRI_NORMAL);
+   } else {
+ 	if (free) sk->wmem_alloc -= skb->mem_len;
+ 	dev->send(dev, skb, &(skb->target), sk->priority);
+   }
+ }
+ 
+ 
+ /*
+  * Re-transmit one or all packets of a single socket.
+  * This function is called by the timer module when an upper-level
+  * protocol thinks we should resend a packet (because it probably
+  * got lost somewhere before).  Note that this can lead to big
+  * trouble if some lower layer was stupid enough to FREE a packet
+  * after sending it- in that case, we would be stuck with an in-
+  * valid pointer here... snifffff :-(( -FvK
+  */
+ void
+ ip_retransmit(struct sock *sk, int all)
+ {
+   struct sk_buff * skb;
+   struct proto *prot;
+   struct ifnet *dev;
+ 
+   prot = sk->prot;
+   skb = sk->send_head;
+   while (skb != NULL) {
+ 	dev = skb->dev;		/* see for which device this packet was */
+ 	skb->when = jiffies;	/* reset the time of when we sent it	*/
+ 
+ 	/* If the interface is (still) up and running, kick it. */
+ 	if (dev->if_flags & IFF_UP) {
+ 		/* Check for IP fragmentation here. */
+ 		if (skb->len > dev->if_mtu) {
+ 			ip_fragment(sk, skb, dev);
+ 		} else {
+ 			dev->send(dev, skb, &(skb->target), sk->priority);
+ 		}
+ 
+ 		/* Update the counters. */
+ 		sk->retransmits++;
+ 		sk->prot->retransmits++;
+ 		if (! all) break;
+ 
+ 		/* Are we going too fast here? */
+ 		if (sk->retransmits > sk->cong_window) break;
+ 	}
+ 
+ 	/* Fetch next packet in retransmit-queue. */
+ 	skb = skb->link3;
+   }
+ 
+   /*
+    * Increase the RTT time every time we retransmit. 
+    * This will cause exponential backoff on how hard we
+    * try to get through again.  Once we get through, the
+    * RTT will settle back down reasonably quickly.
+    */
+   sk->backoff++;
+   inet_reset_timer(sk, TIME_WRITE,
+ 		   ip_backoff(sk->backoff) * (sk->mdev + sk->rtt));
+ }
+ 
+ 
+ /* Socket option code for IP. */
+ int
+ ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
+ {
+   int val, err;
+ 
+   if (optval == NULL) return(-EINVAL);
+ 
+   err = verify_area(VERIFY_READ, optval, sizeof(int));
+   if (err < 0) return(err);
+   val = get_fs_long((unsigned long *) optval);
+ 
+   if (level != SOL_IP) return(-EOPNOTSUPP);
+ 
+   switch(optname) {
+ 	case IP_TOS:
+ 		if (val < 0 || val > 255) return(-EINVAL);
+ 		sk->tos = val;
+ 		return(0);
+ 
+ 	case IP_TTL:
+ 		if (val < 1 || val > 255) return(-EINVAL);
+ 		sk->ttl = val;
+ 		return(0);
+ 
+ 	/* IP_OPTIONS and friends go here eventually */
+ 	default:
+ 		return(-ENOPROTOOPT);
+   }
+ }
+ 
+ 
+ int
+ ip_getsockopt(struct sock *sk, int level, int optname,
+ 	      char *optval, int *optlen)
+ {
+   int val, err;
+ 	
+   if (level != SOL_IP) return(-EOPNOTSUPP);
+ 		
+   switch(optname) {
+ 	case IP_TOS:
+ 		val = sk->tos;
+ 		break;
+ 
+ 	case IP_TTL:
+ 		val = sk->ttl;
+ 		break;
+ 
+ 	default:
+ 		return(-ENOPROTOOPT);
+     }
+ 
+   err = verify_area(VERIFY_WRITE, optlen, sizeof(int));
+   if (err < 0) return(err);
+   put_fs_long(sizeof(int), (unsigned long *) optlen);
+ 
+   err = verify_area(VERIFY_WRITE, optval, sizeof(int));
+   if (err < 0) return(err);
+   put_fs_long(val, (unsigned long *) optval);
+ 
+   return(0);
+ }
+ 
+ 
+ int
+ ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
+ {
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		return(inet_dbg_ioctl(arg, DBG_IP));
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+ }
+ 
+ 
+ struct packet_type ip_packet_type = {
+   "IP",
+   ETH_P_IP,
+   0,
+   ip_recv
+ };
*** original/net/inet/proc.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/proc.c	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,160 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		This file implements the various access functions for the
+  *		PROC file system.  It is mainly used for debugging and
+  *		statistics.
+  *
+  * *NOTE*	This module will be changed to use VFS character devices
+  *		pretty soon.  Do *NOT* rely on its implementation as it is
+  *		right now!
+  *
+  * Version:	@(#)proc.c	1.1.9	01/20/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *		Pauline Middelink, <pauline@calvin.iaf.nl>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *		Portions Copyright 1993 Gerald J. Heim
+  *		Portions Copyright 1993,1994 Fred Baumgarten
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "raw.h"
+ #include "sock.h"
+ #include <linux/param.h>
+ #include <linux/proc_fs.h>
+ 
+ 
+ /*
+  * Return an ASCII readable representation of some socket table.
+  * This table is returned if one reads the /dev/net/{tcp|udp|raw}
+  * VFS device files.  It is mainly used by the netstat(1) program.
+  * It returns a pointer to a page list
+  */
+ static struct pagelist *
+ inet_getinfo(struct proto *pro, int format)
+ {
+   struct sock **s_array;
+   struct sock *sp;
+   char *pos;
+   int i, timer_active;
+   unsigned long  dest, src;
+   unsigned short destp, srcp;
+   struct pagelist *fp, *pp;
+ 
+   fp = (struct pagelist *) __get_free_page(GFP_KERNEL);
+   if (fp == NULL) return(NULL);
+   pp = fp;
+   pp->page = (char *) __get_free_page(GFP_KERNEL);
+   if (pp->page == NULL) {
+ 	free_page((unsigned long) pp);
+ 	return(NULL);
+   }
+   pp->next = NULL;
+   pp->prev = NULL;
+   s_array = pro->sock_array;
+   pos = pp->page;
+   pos += sprintf(pos, "sl  local_address rem_address   st tx_queue rx_queue tr tm->when uid\n");
+   for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
+ 	cli();
+ 	sp = s_array[i];
+ 	while(sp != NULL) {
+ 		dest  = sp->daddr;
+ 		src   = sp->saddr;
+ 		destp = sp->dummy_th.dest;
+ 		srcp  = sp->dummy_th.source;
+ 
+ 		/* Since we are Little Endian we need to swap the bytes :-( */
+ 		destp = ntohs(destp);
+ 		srcp  = ntohs(srcp);
+ 
+ 		/*
+ 		 * FIXME:
+ 		 * This seems to still cause trouble. -FvK
+ 		 */
+ 		timer_active = del_timer(&sp->timer);
+ 		if (! timer_active) sp->timer.expires = 0;
+ 
+ 		pos += sprintf(pos,
+ 		    "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %02X %d %02X\n",
+ 			i, src, srcp, dest, destp, sp->state, 
+ 			(format == 0) ? sp->send_seq-sp->rcv_ack_seq
+ 				      : sp->rmem_alloc, 
+ 			(format == 0) ? sp->acked_seq-sp->copied_seq
+ 				      : sp->wmem_alloc,
+ 			timer_active, sp->timer.expires, sp->retransmits,
+ 			SOCK_INODE(sp->socket)->i_uid, sp->timeout);
+ 
+ 		/*
+ 		 * FIXME:
+ 		 * This seems to still cause trouble. -FvK
+ 		 */
+ 		if (timer_active) add_timer(&sp->timer);
+   
+ 		/* If no buffer space remains, get next free page. */
+ 		if (pos > pp->page + (PAGE_SIZE - 90)) {
+ 			pp->length = (long)pos - (long)pp->page;
+ 			pp->next = (struct pagelist *)
+ 				__get_free_page(GFP_KERNEL);
+ 			if (pp->next == NULL) {
+ 				DPRINTF((DBG_INET,
+ 					"INET: netinfo: out of memory !\n"));
+ 				return(fp);
+ 			}
+ 			pp->next->prev = pp;
+ 			pp = pp->next;
+ 			pp->next = NULL;
+ 			pp->page = (char *) __get_free_page(GFP_KERNEL);
+ 			if (pp->page == NULL) {
+ 				DPRINTF((DBG_INET,
+ 					"INET: netinfo: out of memory !\n"));
+ 				pp->prev->next = NULL;
+ 				free_page((unsigned long) pp);
+ 				return(fp);
+ 			}
+ 			pos = pp->page;
+ 		}
+ 
+ 		/*
+ 		 * All sockets with (port mod SOCK_ARRAY_SIZE) = i
+ 		 * are kept in sock_array[i], so we must follow the
+ 		 * 'next' link to get them all.
+ 		 */
+ 		sp = sp->next;
+ 	}
+ 	sti();
+   }
+   pp->length = (long)pos - (long)pp->page;
+   return(fp);
+ }
+ 
+ 
+ struct pagelist *
+ tcp_get_info(void)
+ {
+   return(inet_getinfo(&tcp_prot, 0));
+ }
+ 
+ 
+ struct pagelist *
+ udp_get_info(void)
+ {
+   return(inet_getinfo(&udp_prot, 1));
+ }
+ 
+ 
+ struct pagelist *
+ raw_get_info(void)
+ {
+   return(inet_getinfo(&raw_prot, 1));
+ }
*** original/net/inet/proto.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/proto.c	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,163 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		INET protocol dispatch tables.
+  *
+  * Version:	@(#)proto.c	1.1.8	12/17/93
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "sock.h"
+ 
+ 
+ static struct ipprot *inet_proto_base = NULL;
+ 
+ 
+ struct ipprot *
+ inet_get_proto(unsigned char prot)
+ {
+   struct ipprot *p;
+ 
+   DPRINTF((DBG_PROTO, "INET: get_proto (%d)\n", prot));
+   for (p = inet_proto_base; p != NULL; p = p->next) {
+ 	DPRINTF((DBG_PROTO, "trying protocol %d\n", p->protocol));
+ 	if (p->protocol == prot) return(p);
+   }
+   return(NULL);
+ }
+ 
+ 
+ int
+ inet_add_proto(struct ipprot *prot)
+ {
+   struct ipprot *p2;
+ 
+   prot->next = inet_proto_base;
+   inet_proto_base = prot;
+   prot->copy = 0;
+ 
+   /* Set the copy bit if we need to. */
+   p2 = prot->next;
+   while(p2 != NULL) {
+ 	if (p2->protocol == prot->protocol) {
+ 		prot->copy = 1;
+ 		break;
+ 	}
+ 	p2 = p2->next;
+   }
+   return(0);
+ }
+ 
+ 
+ int
+ inet_del_proto(struct ipprot *prot)
+ {
+   struct ipprot *p;
+   struct ipprot *lp = NULL;
+ 
+   if (prot == inet_proto_base) {
+ 	inet_proto_base = inet_proto_base->next;
+ 	return(0);
+   }
+ 
+   p = inet_proto_base;
+   while(p != NULL) {
+ 	/*
+ 	 * We have to worry if the protocol being deleted is
+ 	 * the last one on the list, then we may need to reset
+ 	 * someones copied bit.
+ 	 */
+ 	if (p->next != NULL && p->next == prot) {
+ 		/*
+ 		 * if we are the last one with this protocol and
+ 		 * there is a previous one, reset its copy bit.
+ 		 */
+ 	     if (p->copy == 0 && lp != NULL) lp->copy = 0;
+ 	     p->next = prot->next;
+ 	     return(0);
+ 	}
+ 
+ 	if (p->next != NULL && p->next->protocol == prot->protocol) {
+ 		lp = p;
+ 	}
+ 
+ 	p = p->next;
+   }
+   return(-1);
+ }
+ 
+ 
+ int
+ inet_proto_recv(struct ifnet *dev, struct sk_buff *skb,
+ 		struct ip *iph, struct options *opt)
+ {
+   struct ipprot *pp;
+   int found = 0;
+ 
+   for (pp = inet_proto_base; pp != NULL; pp = pp->next) {
+ 	struct sk_buff *skb2;
+ 
+ 	if (pp->protocol != iph->ip_p) continue;
+ 	DPRINTF((DBG_PROTO, "INET: proto_recv: proto = 0x%X:\n", pp));
+ 	DPRINTF((DBG_PROTO, "handler = 0x%X, protocol = %d, copy=%d\n",
+ 		pp->handler, pp->protocol, pp->copy));
+ 
+ 	/*
+ 	 * See if we need to make a copy of it.  This will
+ 	 * only be set if more than one protocol wants it. 
+ 	 * and then not for the last one.
+ 	 */
+ 	if (pp->copy) {
+ 		skb2 = skb_duplicate(skb);
+ 		skb2->lock = 1;
+ 	} else {
+ 		skb2 = skb;
+ 	}
+ 	found = 1;
+ 
+ 	/*
+ 	 * Pass on the datagram to each protocol that wants it,
+ 	 * based on the datagram protocol.  We should really
+ 	 * check the protocol handler's return values here...
+ 	 */
+ 	pp->handler(skb2, dev, opt, iph->ip_dst.s_addr,
+ 		ntohs(iph->ip_len), iph->ip_src.s_addr, 0, pp);
+   }
+ 
+   return(found);
+ }
+ 
+ 
+ int
+ inet_proto_errcv(int type, int code, int err, int fatal, struct ip *iph)
+ {
+   struct ipprot *pp;
+   int found = 0;
+ 
+   for (pp = inet_proto_base; pp != NULL; pp = pp->next) {
+ 	if (pp->protocol != iph->ip_p) continue;
+ 	DPRINTF((DBG_PROTO, "INET: proto_errcv: proto = 0x%X:\n", pp));
+ 	DPRINTF((DBG_PROTO, "      err_handler=0x%X protocol=%d copy=%d\n",
+ 		pp->err_handler, pp->protocol, pp->copy));
+ 	found = 1;
+ 
+ 	/* Is the protocol interested in an error report? */
+ 	if (pp->err_handler == NULL) continue;
+ 
+ 	pp->err_handler(type, code, err, fatal, iph, pp);
+   }
+ 
+   return(found);
+ }
*** original/net/inet/raw.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/raw.c	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,552 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		RAW - implementation of IP "raw" sockets.
+  *
+  * NOTE:	A problem still exists with raw sockets and loopback
+  *		packets, as showed by "ping -v localhost".  There is
+  *		a race somewhere caused by calling release_sock()
+  *		twice- FvK
+  *
+  * Version:	@(#)raw.c	1.1.19	03/27/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "sock.h"
+ 
+ 
+ static unsigned long
+ min(unsigned long a, unsigned long b)
+ {
+   if (a < b) return(a);
+   return(b);
+ }
+ 
+ 
+ /* raw_err gets called by the ICMP module. */
+ static void
+ raw_err(int type, int code, int err, int fatal,
+ 	struct ip *iph, struct ipprot *protocol)
+ {
+   struct sock *sk;
+    
+   DPRINTF((DBG_RAW,
+ 	"INET: raw_err: type=%d code=%d err=%d fat=%d iph=0x%X saddr=%s ",
+ 	type, code, err, fatal, iph, in_ntoa(iph->ip_src.s_addr)));
+   DPRINTF((DBG_RAW, "daddr=%s protocol=%s)\n",
+ 	in_ntoa(iph->ip_dst.s_addr), protocol->name));
+ 
+   if (protocol == NULL) return;
+   sk = (struct sock *) protocol->data;
+   if (sk == NULL) return;
+ 
+   /* This is meaningless in raw sockets. */
+   if (type == ICMP_SOURCEQUENCH) {
+ 	if (sk->cong_window > 1) sk->cong_window = (sk->cong_window / 2);
+ 	return;
+   }
+   sk->err = err;
+ 
+   /* None of them are fatal for raw sockets. */
+ #if 0
+   if (fatal) {
+ 	sk->prot->close(sk, 0);
+   }
+ #endif
+ }
+ 
+ 
+ static int
+ raw_select(struct sock *sk, int sel_type, select_table *wait)
+ {
+   select_wait(sk->sleep, wait);
+   switch(sel_type) {
+ 	case SEL_IN:
+ 		if (sk->rqueue != NULL) return(1);
+ 		break;
+ 
+ 	case SEL_OUT:
+ 		if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE) return(1);
+ 		break;
+ 	
+ 	case SEL_EX:
+ 		if (sk->err) return(1); /* can this ever happen? */
+ 		break;
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ raw_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
+ {
+   struct sockaddr_in sin;
+   int err;
+ 
+   if (addr_len < sizeof(struct sockaddr_in)) return(-EINVAL);
+ 
+   err = verify_area(VERIFY_READ, usin, sizeof(struct sockaddr_in));
+   if (err < 0) return(err);
+ 
+   memcpy_fromfs(&sin, usin, sizeof(struct sockaddr_in));
+   if (sin.sin_family && sin.sin_family != AF_INET) return(-EAFNOSUPPORT);
+   sk->daddr = sin.sin_addr.s_addr;
+   sk->dummy_th.dest = sin.sin_port;
+ 
+   /* If local address is not yet specified, set it now. */
+   if (sk->saddr == INADDR_ANY) {
+ 	sk->saddr = in_local_addr(sk->daddr);
+ 	if (sk->saddr == INADDR_ANY) return(-ENETUNREACH);
+   }
+   sk->state = TCP_ESTABLISHED;
+   return(0);
+ }
+ 
+ 
+ /*
+  * This should be the easiest of all, all we do is
+  * copy it into a buffer.
+  */
+ static int
+ raw_recv(struct sk_buff *skb, struct ifnet *dev, struct options *opt,
+ 	unsigned long daddr, unsigned short len, unsigned long saddr,
+ 	int redo, struct ipprot *protocol)
+ {
+   struct sock *sk;
+ 
+   DPRINTF((DBG_RAW, "INET: raw_recv: skb=0x%X dev=0x%X opt=0x%X saddr=%s\n",
+ 					skb, dev, opt, in_ntoa(saddr)));
+   DPRINTF((DBG_RAW, "                daddr=%s len=%d redo=%d protocol=0x%X)\n",
+ 	   			in_ntoa(daddr), len, redo, protocol));
+ 
+   if (skb == NULL) return(0);
+   if (protocol == NULL) {
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return(0);
+   }
+   sk = (struct sock *) protocol->data;
+   if (sk == NULL) {
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return(0);
+   }
+ 
+   /* Now we need to copy this into memory. */
+   skb->sk = sk;
+   skb->len = len;
+   skb->dev = dev;
+   skb->saddr = daddr;
+   skb->daddr = saddr;
+ 
+   DPRINTF((DBG_RAW, "RAW: sk=0x%08X skb=0x%08X redo=%d inuse=%d\n",
+ 					sk, skb, redo, sk->inuse));
+ 
+   if (! redo) {
+ 	/* Now see if we are in use. */
+ 	cli();
+ 	if (sk->inuse) {
+ 		DPRINTF((DBG_RAW, "INET: raw_recv: adding to backlog.\n"));
+ 		if (sk->back_log == NULL) {
+ 			sk->back_log = skb;
+ 			skb->qnext = skb;
+ 			skb->qprev = skb;
+ 		} else {
+ 			skb->qnext = sk->back_log;
+ 			skb->qprev = sk->back_log->qprev;
+ 			skb->qprev->qnext = skb;
+ 			skb->qnext->qprev = skb;
+ 		}
+ 		sti();
+ 		return(0);
+ 	}
+ 	sk->inuse = 1;
+ 	sti();
+   }
+ 
+   /* Charge the packet memory size to the socket. */
+   if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX) {
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return(0);
+   }
+   sk->rmem_alloc += skb->mem_len;
+ 
+   /* Now just put it onto the queue. */
+   if (sk->rqueue == NULL) {
+ 	sk->rqueue = skb;
+ 	skb->qnext = skb;
+ 	skb->qprev = skb;
+   } else {
+ 	skb->qnext = sk->rqueue;
+ 	skb->qprev = sk->rqueue->qprev;
+ 	skb->qprev->qnext = skb;
+ 	skb->qnext->qprev = skb;
+   }
+ 
+   /* Tell this socket that data arrived. */
+   wake_up_interruptible(sk->sleep);
+   release_sock(sk);
+   return(0);
+ }
+ 
+ 
+ static int
+ raw_sendto(struct sock *sk, unsigned char *from, int len,
+ 	   int noblock,
+ 	   unsigned flags, struct sockaddr_in *usin, int addr_len)
+ {
+   struct sk_buff *skb;
+   struct ifnet *dev = NULL;
+   struct sockaddr_in sin;
+   unsigned long saddr;
+   int err, tmp, dnr;
+ 
+   DPRINTF((DBG_RAW, "INET: raw_sendto: sk=0x%X from=0x%X len=%d noblock=%d\n",
+ 						sk, from, len, noblock));
+   DPRINTF((DBG_RAW, "                  flags=0x%04X usin=0x%X addr_len=%d\n",
+ 						flags, usin, addr_len));
+ 
+   /* Check the parameters. */
+   if (len < 0) return(-EINVAL);
+ 
+   if (flags & MSG_OOB) return(-EINVAL);
+   if (flags & MSG_PEEK) return(-EINVAL);
+ 
+   dnr = sk->dontroute;
+   if (flags & MSG_DONTROUTE) dnr = 1;
+ 
+   /* Get and verify the address and protocol info. */
+   if (usin) {
+ 	if (addr_len < sizeof(sin)) return(-EINVAL);
+ 
+ 	err = verify_area(VERIFY_READ, usin, sizeof(struct sockaddr_in));
+ 	if (err < 0) return(err);
+ 	memcpy_fromfs(&sin, usin, sizeof(struct sockaddr_in));
+ 	if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL);
+   } else {
+ 	if (sk->state != TCP_ESTABLISHED) return(-EINVAL);
+ 	sin.sin_family = AF_INET;
+ 	sin.sin_port = sk->protocol;
+ 	sin.sin_addr.s_addr = sk->daddr;
+   }
+   if (sin.sin_port == 0) sin.sin_port = sk->protocol;
+ 
+   /* You can't broadcast when not allowed to. */
+   if ((sk->broadcast == 0) &&
+       (in_chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST)) return(-EACCES);
+ 
+   /* If local address is not yet specified, set it now. */
+   saddr = sk->saddr;
+   if (saddr == INADDR_ANY) {
+ 	saddr = in_local_addr(sin.sin_addr.s_addr);
+ 	if (saddr == INADDR_ANY) return(-ENETUNREACH);
+   }
+ 
+   /* Get a memory buffer for this packet. */
+   sk->inuse = 1;
+   skb = NULL;
+   while (skb == NULL) {
+ 	skb = (struct sk_buff *) sk->prot->wmalloc(sk,
+ 						   sk->prot->max_header + len,
+ 						   0, GFP_KERNEL);
+ 	/*
+ 	 * FIXME:
+ 	 * This shouldn't happen, but it could.
+ 	 * Need to change this to sleep.
+ 	 */
+ 	if (skb == NULL) {
+ 		int tmp;
+ 
+ 		DPRINTF((DBG_RAW, "INET: raw_sendto: write buffer full?\n"));
+ 		if (noblock) return(-EAGAIN);
+ 		tmp = sk->wmem_alloc;
+ 		release_sock(sk);
+ 		cli();
+ 		if (tmp <= sk->wmem_alloc) {
+ 			interruptible_sleep_on(sk->sleep);
+ 			if (current->signal & ~current->blocked) {
+ 				sti();
+ 				return(-ERESTARTSYS);
+ 			}
+ 		}
+ 		sk->inuse = 1;
+ 		sti();
+ 	}
+   }
+   skb->len = len;
+   skb->sk = sk;
+ 
+   /* Create the IP (and MAC) headers. */
+   tmp = ip_build_header(skb, saddr, sin.sin_addr.s_addr,
+ 			&dev, sk->protocol, sk->opt, sk->tos, sk->ttl, dnr);
+   if (tmp < 0) {
+ 	DPRINTF((DBG_RAW, "INET: raw_sendto: error building IP header.\n"));
+ 	sk->prot->wfree(sk, (unsigned char *) skb->mem_addr, skb->mem_len);
+ 	release_sock(sk);
+ 	return(tmp);
+   }
+   skb->len += tmp;
+   DPRINTF((DBG_RAW, "RAW: mac header is %d bytes, len now %d\n",
+ 							tmp, skb->len));
+ 
+   err = verify_area(VERIFY_READ, from, len);
+   if (err < 0) return(err);
+   memcpy_fromfs((unsigned char *) (skb->raw + tmp), from, len);
+ 
+   /*
+    * If we are using IPPROTO_RAW, we need to fill in the source
+    * IP address in the application-supplied IP header.
+    */
+   if (sk->protocol == IPPROTO_RAW) {
+ 	unsigned char *buff;
+ 	struct ip *iph;
+ 
+ 	buff = skb->raw;
+ 	buff += tmp;
+ 	iph = (struct ip *) buff;
+ 
+ 	/*
+ 	 * Traceroute has the "source" option, I assume this
+ 	 * is a good thing to check on :) -FvK
+ 	 */
+ 	if (iph->ip_src.s_addr == INADDR_ANY)
+ 			iph->ip_src.s_addr = saddr;
+   }
+ 
+   /* Kick the packet to IP for sending. */
+   ip_queue_xmit(sk, dev, skb, 1);
+ 
+   release_sock(sk);
+   return(len);
+ }
+ 
+ 
+ static int
+ raw_write(struct sock *sk, unsigned char *buff, int len, int noblock,
+ 	   unsigned flags)
+ {
+   return(raw_sendto(sk, buff, len, noblock, flags, NULL, 0));
+ }
+ 
+ 
+ static void
+ raw_close(struct sock *sk, int timeout)
+ {
+   sk->inuse = 1;
+   sk->state = TCP_CLOSE;
+ 
+   DPRINTF((DBG_RAW, "INET: raw_close: deleting protocol %d\n",
+ 	   ((struct ipprot *)sk->pair)->protocol));
+ 
+   if (inet_del_proto((struct ipprot *) sk->pair) < 0)
+ 		DPRINTF((DBG_RAW, "INET: raw_close: del_proto failed.\n"));
+ 
+   kfree((char *) sk->pair);
+   sk->pair = NULL;
+ 
+   release_sock(sk);
+ }
+ 
+ 
+ static int
+ raw_init(struct sock *sk)
+ {
+   struct ipprot *p;
+ 
+   p = (struct ipprot *) kmalloc(sizeof(struct ipprot), GFP_KERNEL);
+   if (p == NULL) return(-ENOMEM);
+ 
+   memset(p, 0, sizeof(struct ipprot));
+   p->handler = raw_recv;
+   p->protocol = sk->protocol;
+   p->data = (void *)sk;
+   p->err_handler = raw_err;
+   (void) inet_add_proto(p);
+    
+   /* We need to remember this somewhere. */
+   sk->pair = (struct sock *) p;
+ 
+   DPRINTF((DBG_RAW, "INET: raw_init: added protocol %d\n", sk->protocol));
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * This should be easy, if there is something there
+  * we return it, otherwise we block.
+  */
+ static int
+ raw_recvfrom(struct sock *sk, unsigned char *to, int len,
+ 	     int noblock, unsigned flags, struct sockaddr_in *sin,
+ 	     int *addr_len)
+ {
+   struct sk_buff *skb;
+   int err, copied = 0;
+ 
+   DPRINTF((DBG_RAW, "INET: raw_recvfrom: sk=0x%X to=0x%X len=%d noblock=%d\n",
+ 						sk, to, len, noblock));
+   DPRINTF((DBG_RAW, "                    flags=0x%04X sin=0x%X addr_len=%d\n",
+ 					flags, sin, addr_len));
+ 
+   if (len == 0) return(0);
+   if (len < 0) return(-EINVAL);
+   if (sk->shutdown & RCV_SHUTDOWN) return(0);
+ 
+   /* Fetch address info from user space. */
+   if (addr_len) {
+ 	err = verify_area(VERIFY_WRITE, addr_len, sizeof(int));
+ 	if (err < 0) return(err);
+ 	put_fs_long(sizeof(struct sockaddr_in), (int *) addr_len);
+   }
+ 
+   /* Check user buffer area. */
+   err = verify_area(VERIFY_WRITE, to, len);
+   if (err < 0) return(err);
+ 
+   /* Mark the socket as busy and wait for a packet to arrive. */
+   sk->inuse = 1;
+   while (sk->rqueue == NULL) {
+ 	if (noblock) {
+ 		release_sock(sk);
+ 		if (copied) return(copied);
+ 		return(-EAGAIN);
+ 	}
+ 	release_sock(sk);
+ 	cli();
+ 	if (sk->rqueue == NULL) {
+ 		interruptible_sleep_on(sk->sleep);
+ 		if (current->signal & ~current->blocked) {
+ 			sti();
+ 			return(-ERESTARTSYS);
+ 		}
+ 	}
+ 	sk->inuse = 1;
+ 	sti();
+   }
+ 
+   /* We got a packet.  Fetch the first entry from the queue. */
+   skb = sk->rqueue;
+ 
+   /* If we were not just peeking into the queue, advance queue ptr. */
+   if (! (flags & MSG_PEEK)) {
+ 	if (skb->qnext == skb) {
+ 		sk->rqueue = NULL;
+ 	} else {
+ 		sk->rqueue = sk->rqueue->qnext;
+ 		skb->qprev->qnext = skb->qnext;
+ 		skb->qnext->qprev = skb->qprev;
+ 	}
+   }
+ 
+   DPRINTF((DBG_RAW,
+ 	"INET: raw: sock=0x%08X: found packet at skb=0x%08X plen=%d len=%d\n",
+ 						sk, skb, skb->len, len));
+   /*
+    * Copy the packet data into user space.
+    * The packet we are referring here is at (skb + 1), starting
+    * with the MAC layer header (if any).  The raw MAC data is at
+    * (skb->raw).  This is the COMPLETE packet, including IP header.
+    * The length of the packet (as determined by the skb->len field)
+    * is the total length of the IP datagram, including the length of
+    * the IP header.
+    */
+   copied = min(len, skb->len);	/* enough room in user buffer? */
+   memcpy_tofs(to, skb->raw,  copied);
+ 
+   /* Copy the address info into user space. */
+   if (sin) {
+ 	struct sockaddr_in addr;
+ 
+ 	addr.sin_family = AF_INET;
+ 	addr.sin_addr.s_addr = skb->daddr;
+ 	err = verify_area(VERIFY_WRITE, sin, sizeof(struct sockaddr_in));
+ 	if (err < 0) return(err);
+ 	memcpy_tofs(sin, &addr, sizeof(struct sockaddr_in));
+   }
+ 
+   /* If we were not just peeking, free the packet memory. */
+   if (! (flags & MSG_PEEK)) {
+ 	inet_kfree_skb(skb, FREE_READ);
+   }
+   release_sock(sk);
+   DPRINTF((DBG_RAW,
+ 	"INET: raw: received %d bytes from skb=0x%08X on sk=0x%X\n",
+ 						copied, skb, sk));
+   return(copied);
+ }
+ 
+ 
+ static int
+ raw_read(struct sock *sk, unsigned char *buff, int len, int noblock,
+ 	  unsigned flags)
+ {
+   return(raw_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+ }
+ 
+ 
+ int
+ raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
+ {
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		return(inet_dbg_ioctl(arg, DBG_RAW));
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+   return(0);
+ }
+ 
+ 
+ struct proto raw_prot = {
+   sock_wmalloc,		sock_rmalloc,		sock_wfree,
+   sock_rfree,		sock_rspace,		sock_wspace,
+   raw_close,		raw_read,		raw_write,
+   raw_sendto,		raw_recvfrom,		raw_connect,
+   NULL,			NULL,			NULL,
+   raw_recv,		raw_select,		raw_ioctl,
+   raw_init,		NULL,			NULL,
+   ip_setsockopt,	ip_getsockopt,		128,
+   0,			{NULL,},		"RAW"
+ };
+ 
+ 
+ #if 0
+ int
+ raw_devread(char *buf, int count)
+ {
+   int i;
+ 
+   DPRINTF((DBG_RAW, "RAW: devread(0x%lX, %d)\n", (long) buf, count));
+ 
+   /* Verify addresses. */
+   i = verify_area(VERIFY_WRITE, (void *) buf, count);
+   if (i < 0) return(i);
+ 
+   return(inet_getinfo(&raw_prot, buf, count, 1));
+ }
+ #endif
+ 
+ 
+ void
+ raw_proto_init(void)
+ {
+   int i;
+ 
+   /* Clear all RAW sockets. */
+   for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
+ 	raw_prot.sock_array[i] = NULL;
+   }
+ }
*** original/net/inet/raw.h	Thu Jan  1 01:00:00 1970
--- linux/net/inet/raw.h	Mon May  9 13:13:11 1994
***************
*** 0 ****
--- 1,33 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Definitions for the RAW-IP module.
+  *
+  * Version:	@(#)raw.h	1.1.2	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _INET_RAW_H
+ #define _INET_RAW_H
+ 
+ 
+ extern struct proto	raw_prot;
+ 
+ 
+ extern int		raw_devread(char *, int);
+ extern int		raw_ioctl(struct sock *sk, int cmd, unsigned long arg);
+ 
+ extern void		raw_proto_init(void);
+ 
+ 
+ #endif	/* _INET_RAW_H */
*** original/net/inet/route.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/route.c	Mon May  9 13:13:12 1994
***************
*** 0 ****
--- 1,389 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		ROUTE - implementation of the IP router.
+  *
+  * Version:	@(#)route.c	1.1.17	03/17/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "route.h"
+ #include "sock.h"
+ 
+ 
+ /* Dump the contents of a routing table entry. */
+ #ifdef NET_DEBUG
+ static void
+ inet_rt_print(struct rtable *rt)
+ {
+   if (rt == NULL || ((inet_debug & (1 << DBG_RT)) == 0)) return;
+ 
+   printk("INET: route: 0x%06X NXT=0x%06X FLAGS=0x%04X\n",
+ 		(long) rt, (long) rt->rt_next, rt->rt_flags);
+   printk("             TARGET=%s ", in_ntoa(SA_ADDR(rt->rt_dst)));
+   printk("GW=%s ", in_ntoa(SA_ADDR(rt->rt_gateway)));
+   printk("MASK=%s ", in_ntoa(SA_ADDR(rt->rt_genmask)));
+   printk("             DEV=%s USE=%ld REF=%ld METRIC=%d\n",
+ 	(rt->rt_ifp == NULL) ? "NONE" : rt->rt_ifp->if_name,
+ 	rt->rt_use, rt->rt_refcnt, rt->rt_metric);
+ }
+ #endif
+ 
+ 
+ /* Remove a routing table entry. */
+ static int
+ inet_rt_del(unsigned long dst)
+ {
+   struct rtable **pr;
+   struct rtable *r;
+   int found = 0;
+ 
+   DPRINTF((DBG_RT, "INET: rt_del: flushing for dst %s\n", in_ntoa(dst)));
+ 
+   cli();
+   for (pr = &route_base; (r = *pr); ) {
+   	if (r->rt_dst.sa_family == AF_INET && SA_ADDR(r->rt_dst) == dst) {
+ 		*pr = r->rt_next;
+ 		kfree_s(r, sizeof(struct rtable));
+ 		++found;
+ 	} else {
+ 		pr = &r->rt_next;
+ 	}
+   }
+   sti();
+   return((found == 0) ? -ESRCH : 0);
+ }
+ 
+ 
+ void
+ inet_rt_add(short flags, unsigned long dst, unsigned long gw,
+ 	    unsigned long mask, int metric, struct ifnet *dev)
+ {
+   struct rtable *rt;
+   struct rtable *r;
+   struct rtable **pr;
+   int did_print = 0;
+ 
+   /*
+    * If an entry already exists delete it.  Any duplicates found
+    * later are also deleted.  This way operation of 'route add'
+    * is always 'most recent added route has priority' rather than
+    * problematic the way it was before.
+    */
+   (void) inet_rt_del(dst);
+ 
+   /* Create a new entry. */
+   rt = (struct rtable *)kmalloc(sizeof(struct rtable), GFP_ATOMIC);
+   if (rt == NULL) {
+ 	DPRINTF((DBG_RT, "INET: rt_add: no memory for new route!\n"));
+ 	return;
+   }
+ 
+   /* Fill it in. */
+   memset(rt, 0, sizeof(struct rtable));
+   rt->rt_flags = RTF_UP;
+   rt->rt_flags |= flags;
+   if (dst == INADDR_ANY) rt->rt_flags &= ~RTF_HOST;
+   if (gw != 0L) rt->rt_flags |= RTF_GATEWAY;
+   rt->rt_ifp = dev;
+   SA_FAMILY(rt->rt_gateway) = AF_INET;
+   SA_ADDR(rt->rt_gateway) = gw;
+   SA_FAMILY(rt->rt_genmask) = AF_INET;
+   SA_ADDR(rt->rt_genmask) = mask;
+   rt->rt_metric = metric;
+ 
+   /*
+    * If this is coming from an ICMP redirect message, truncate
+    * the TARGET if we are creating an entry for a NETWORK.
+    */
+   SA_FAMILY(rt->rt_dst) = AF_INET;
+   if (flags & RTF_DYNAMIC) {
+ 	if (flags & RTF_HOST) {
+ 		SA_ADDR(rt->rt_dst) = dst;
+ 	} else {
+ 		mask = in_getmask(dst, dev, NULL);
+ 		SA_ADDR(rt->rt_dst) = (dst & mask);
+ 		dst = SA_ADDR(rt->rt_dst);
+ 	}
+   } else {
+ 	SA_ADDR(rt->rt_dst) = dst;
+   }
+ 
+   /*
+    * Because routing programs like "route" and "routed" also use
+    * the "GW" address as a hint about which interface to use (by
+    * setting the IF address (or P-P address) of an interface as
+    * the "GW" address), we only use the GW address if it is not
+    * the same as either the IF address or the P-P address, which
+    * is possible if the interface is running as POINTOPOINT.
+    */
+   if (rt->rt_flags & RTF_GATEWAY) {
+ 	struct ifaddr *ifa;
+ 
+ 	if ((ifa = dev_get_addr(dev, AF_INET)) != NULL) {
+ 		if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) {
+ 			if (SA_ADDR(rt->rt_gateway) == IN_IFDSTADDR(ifa)) {
+ 				rt->rt_flags &= ~RTF_GATEWAY;
+ 				SA_ADDR(rt->rt_gateway) = INADDR_ANY;
+ 			}
+ 		} else {
+ 			if (SA_ADDR(rt->rt_gateway) == IN_IFADDR(ifa)) {
+ 				rt->rt_flags &= ~RTF_GATEWAY;
+ 				SA_ADDR(rt->rt_gateway) = INADDR_ANY;
+ 			}
+ 		}
+ 	}
+   }
+ 
+ #ifdef NET_DEBUG
+   inet_rt_print(rt);
+ #endif
+ 
+   /*
+    * What we have to do is loop though this until we have
+    * found the first address which has the same generality
+    * as the one in rt.  Then we can put rt in before it.
+    *
+    * XXX we should actually be determining some kind of routing
+    * priority -- more specific routes before less specific routes
+    * XXX FIXME!
+    */
+   DPRINTF((DBG_RT, "INET: rt_add: mask = 0x%X\n", mask));
+   cli();
+ 
+   for (pr = &route_base; (r = *pr); ) {
+ 	/* Delete any duplicates, continue scan. */
+ 	if ((SA_FAMILY(r->rt_dst) == AF_INET) && (SA_ADDR(r->rt_dst) == dst)) {
+ 		*pr = r->rt_next;
+ 		kfree_s(r, sizeof(struct rtable));
+ 		continue;
+ 	}
+ 
+ 	/*
+ 	 * Insert before similar entry, if found.  If no entries
+ 	 * are ever found the route is appended.
+ 	 */
+ 	if (!(SA_ADDR(r->rt_genmask) & ~mask)) {
+ 		DPRINTF((DBG_RT, "INET: rt_add: adding before r=0x%X\n", r));
+ 		did_print = 1;
+ #ifdef NET_DEBUG
+ 		inet_rt_print(r);
+ #endif
+ 		break;
+ 	}
+ 	pr = &r->rt_next;
+   }
+   rt->rt_next = *pr;
+   *pr = rt;
+   sti();
+ 
+   if (did_print == 0) {
+ 	DPRINTF((DBG_RT, "INET: rt_add: added after end\n"));
+   }
+ }
+ 
+ 
+ /* Fetch the contents of a user-passed device name. */
+ static struct ifnet *
+ inet_rt_udev(unsigned long addr)
+ {
+   char name[IFNAMSIZ];
+ 
+   /* Is the buffer OK? */
+   if (verify_area(VERIFY_READ, (char *) addr, IFNAMSIZ) < 0) return(NULL);
+ 
+   memcpy_fromfs(name, (char *) addr, IFNAMSIZ);
+   DPRINTF((DBG_RT, "get_udev: \"%s\"\n", name));
+   return(dev_get_name(name));
+ }
+ 
+ 
+ static int
+ inet_rt_create(struct rtentry *r)
+ {
+   struct ifnet *dev;
+   unsigned long mask;
+   unsigned long dst;
+ 
+   if ((SA_FAMILY(r->rt_dst) != AF_INET) ||
+       ((r->rt_flags & RTF_GATEWAY) && (SA_FAMILY(r->rt_gateway) != AF_INET))) {
+ 	DPRINTF((DBG_RT, "INET: rt_create: we only know about AF_INET !\n"));
+ 	return(-EAFNOSUPPORT);
+   }
+ 
+   /*
+    * I admit that the following bits of code were "inspired" by
+    * the Berkeley UNIX system source code.  I could think of no
+    * other way to find out how to make it compatible with it (I
+    * want this to be compatible to get "routed" up and running).
+    * The idea is as follows.
+    *
+    *	If the rt_metric field is set to 0, then we must use the
+    *	"primary interface" of the system for this route. I think
+    *    this is a gross hack, but who am I to tell you? -FvK
+    *
+    *	If the route has the "gw" flag (and address) set, use that
+    *	as the target to find a device for.  Otherwise, use the
+    *	route itself as the lookup target.  If the "gw" flag is an
+    *	exact match of the interface address, then clear this flag
+    *	in the routing table entry, as the user only specified the
+    *	"gw" address as a hint as to which interface to use.
+    *
+    * If one of these matches succeeds, we found a possible
+    * interface for this route.  Note, however, that this does
+    * not mean that it is the *only* possible choice- the code
+    * can easily be confused by wrong setups...
+    */
+   if (r->rt_flags & RTF_GATEWAY) dst = SA_ADDR(r->rt_gateway);
+     else dst = SA_ADDR(r->rt_dst);
+ 
+   if (r->rt_metric == -1) {
+ 	DPRINTF((DBG_RT, ">> IROUTE: Metric0 hack detected !\n"));
+ 	dev = in_dev_primary();
+ 	r->rt_metric = 1;
+   } else {
+ 	if (r->rt_ifp != NULL) {
+ 		DPRINTF((DBG_RT, ">> IROUTE: DEV hack detected !\n"));
+ 		dev = inet_rt_udev((unsigned long) r->rt_ifp);
+ 	} else {
+ 		if (r->rt_metric == 0) r->rt_metric = 1;
+ 		dev = in_dev_check(dst);
+ 	}
+   }
+ 
+   DPRINTF((DBG_RT, "INET: rt_create: dev for %s gw ",
+ 	in_ntoa(SA_ADDR(r->rt_dst))));
+   DPRINTF((DBG_RT, "%s (0x%04X) is 0x%X (%s)\n",
+ 	in_ntoa(SA_ADDR(r->rt_gateway)),
+ 	r->rt_flags, dev, (dev == NULL) ? "NONE" : dev->if_name));
+ 
+   if (dev == NULL) return(-ENETUNREACH);
+ 
+   /* Calculate the network mask for this route. */
+   if (r->rt_flags & RTF_HOST) mask = 0xFFFFFFFF;
+     else mask = in_getmask(SA_ADDR(r->rt_dst), dev, NULL);
+   DPRINTF((DBG_RT, "RT: Default Mask is %s\n", in_ntoa(mask)));
+   if ((SA_FAMILY(r->rt_genmask) != AF_UNSPEC) &&
+       (SA_ADDR(r->rt_genmask) != INADDR_ANY)) mask = SA_ADDR(r->rt_genmask);
+   DPRINTF((DBG_RT, "RT: Final Mask is %s\n", in_ntoa(mask)));
+ 
+   /* Call rt_add() to actually add the route. */
+   inet_rt_add((r->rt_flags & ~RTF_DYNAMIC),
+ 	 SA_ADDR(r->rt_dst), 
+ 	 SA_ADDR(r->rt_gateway), 
+ 	 mask, 
+ 	 r->rt_metric, 
+ 	 dev);
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_rt_delete(struct rtentry *r)
+ {
+   return(inet_rt_del(SA_ADDR(r->rt_dst)));
+ }
+ 
+ 
+ struct rtable *
+ inet_route(unsigned long daddr, struct options *opt)
+ {
+   struct rtable *rt;
+   unsigned long mask;
+   int use_loopback = 0;
+ 
+   DPRINTF((DBG_RT, "INET: rt_route(%s)\n", in_ntoa(daddr)));
+ 
+   /*
+    * If the target address is an interface address, we are done
+    * already.  All local addresses get routed over the loopback
+    * device...
+    */
+   if (in_chk_addr(daddr) == IS_MYADDR) use_loopback = 1;
+ 
+   /*
+    * Loop over the IP routing table to find a route suitable
+    * for this packet.  Note that we really should have a look
+    * at the IP options to see if we have been given a hint as
+    * to what kind of path we should use...
+    */
+   cli();
+   for (rt = route_base; rt != NULL; rt = rt->rt_next) {
+ 	if (SA_FAMILY(rt->rt_dst) != AF_INET) continue;
+ 	DPRINTF((DBG_RT, "INET: rt_route: %s: ",
+ 		in_ntoa(SA_ADDR(rt->rt_dst))));
+ 
+ 	/*
+ 	 * If we're looking for the LOOPBACK route, do a quick check.
+ 	 * Note that this piece of code depends on the LOCALHOST route
+ 	 * actually being set to the LOOPBACK device.  I think this is
+ 	 * valid, and it is faster than forcing the target address to
+ 	 * be the "localhost" address, causing extra address compares
+ 	 * to be performed.
+ 	 */
+ 	if (use_loopback) {
+ 		if (! (rt->rt_ifp->if_flags & IFF_LOOPBACK)) {
+ 			DPRINTF((DBG_RT, "NO\n"));
+ 			continue;
+ 		}
+ 		DPRINTF((DBG_RT, "%s (LOOPBACK)\n", rt->rt_ifp->if_name));
+ 		break;
+ 	}
+ 
+ 	/* Check if we fit in this network. */
+ 	mask = SA_ADDR(rt->rt_genmask);
+ 	if ((daddr & mask) != (SA_ADDR(rt->rt_dst) & mask)) {
+ 		/* Perhaps we do, but only as a broadcast address? */
+ 		DPRINTF((DBG_RT, "NO\n"));
+ 		continue;
+ 	}
+ 	DPRINTF((DBG_RT, "%s (%s)\n",
+ 		rt->rt_ifp->if_name, in_ntoa(SA_ADDR(rt->rt_gateway))));
+ 	break;
+   }
+ 
+   if (rt != NULL) {
+ 	rt->rt_use++;
+   } else {
+ 	DPRINTF((DBG_RT, "NONE\n"))
+ 		;
+   }
+   sti();
+   return(rt);
+ }
+ 
+ 
+ int
+ inet_rt_ioctl(unsigned int cmd, unsigned long arg)
+ {
+   int ret;
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		ret = inet_dbg_ioctl(arg, DBG_RT);
+ 		break;
+ 
+ 	default:
+ 		ret = -EINVAL;
+ 		break;
+   }
+   return(ret);
+ }
+ 
+ 
+ struct route_aftype inet_route_type = {
+   "DARPA Internet",	AF_INET,	in_sprint,
+   inet_rt_create,	inet_rt_delete,	NULL
+ };
*** original/net/inet/route.h	Thu Jan  1 01:00:00 1970
--- linux/net/inet/route.h	Mon May  9 13:13:12 1994
***************
*** 0 ****
--- 1,38 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET  is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Definitions for the IP router.
+  *
+  * Version:	@(#)route.h	1.1.3	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _INET_ROUTE_H
+ #define _INET_ROUTE_H
+ 
+ 
+ #include <linux/route.h>
+ 
+ 
+ extern struct route_aftype	inet_route_type;
+ 
+ 
+ extern void			inet_rt_add(short flags, unsigned long dst,
+ 					    unsigned long gw,
+ 					    unsigned long mask,
+ 					    int metric, struct ifnet *dev);
+ struct rtable			*inet_route(unsigned long daddr,
+ 					    struct options *opt);
+ extern int			inet_rt_ioctl(unsigned int cmd, unsigned long arg);
+ 
+ 
+ #endif	/* _INET_ROUTE_H */
*** original/net/inet/sock.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/sock.c	Mon May  9 13:13:12 1994
***************
*** 0 ****
--- 1,1858 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		SOCK - AF_INET protocol family socket handler.
+  *
+  * Version:	@(#)sock.c	1.3.36	04/25/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Florian La Roche, <flla@stud.uni-sb.de>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *		Rick Sladkey, <jrs@world.std.com>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *		Pauline Middelink, <pauline@calvin.iaf.nl>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "raw.h"
+ #include "route.h"
+ #include "sock.h"
+ #include <linux/if_ether.h>
+ 
+ 
+ #define min(a,b)	((a)<(b)?(a):(b))
+ 
+ 
+ static struct arp_aftype inet_arp_type = {
+   "DARPA Internet",	AF_INET,	sizeof(unsigned long),
+   ETH_P_IP,		in_sprint,	in_check,
+   in_compare,		in_mksock,	in_unsock,
+   NULL
+ };
+ 
+ 
+ /* Not used at present. */
+ #if 0
+ void
+ print_sk(struct sock *sk)
+ {
+   if (!sk) {
+ 	printk("  print_sk(NULL)\n");
+ 	return;
+   }
+   printk("  wmem_alloc = %d\n", sk->wmem_alloc);
+   printk("  rmem_alloc = %d\n", sk->rmem_alloc);
+   printk("  send_head = %X\n", sk->send_head);
+   printk("  state = %d\n", sk->state);
+   printk("  wback = %X, rqueue = %X\n", sk->wback, sk->rqueue);
+   printk("  wfront = %X\n", sk->wfront);
+   printk("  daddr = %X, saddr = %X\n", sk->daddr,sk->saddr);
+   printk("  num = %d", sk->num);
+   printk(" next = %X\n", sk->next);
+   printk("  send_seq = %d, acked_seq = %d, copied_seq = %d\n",
+ 	  sk->send_seq, sk->acked_seq, sk->copied_seq);
+   printk("  rcv_ack_seq = %d, window_seq = %d, fin_seq = %d\n",
+ 	  sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
+   printk("  prot = %X\n", sk->prot);
+   printk("  pair = %X, back_log = %X\n", sk->pair,sk->back_log);
+   printk("  inuse = %d , blog = %d\n", sk->inuse, sk->blog);
+   printk("  dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
+   printk("  retransmits = %d, timeout = %d\n", sk->retransmits, sk->timeout);
+   printk("  cong_window = %d, packets_out = %d\n", sk->cong_window,
+ 	  sk->packets_out);
+   printk("  urg = %d shutdown=%d\n", sk->urg, sk->shutdown);
+ }
+ #endif
+ 
+ 
+ static int
+ sk_inuse(struct proto *prot, int num)
+ {
+   struct sock *sk;
+ 
+   cli();
+   for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
+ 	sk != NULL;
+ 	sk = sk->next
+   ) {
+ 	if (sk->num == num) {
+ 		sti();
+ 		return(1);
+ 	}
+   }
+   sti();
+   return(0);
+ }
+ 
+ 
+ unsigned short
+ get_new_socknum(struct proto *prot, unsigned short base)
+ {
+   static int start = 0;
+ 
+   /*
+    * Used to cycle through the port numbers so the
+    * chances of a confused connection drop.
+    */
+   int i, j;
+   int best = 0;
+   int size = 32767; /* a big num. */
+   struct sock *sk;
+ 
+   if (base == 0) base = PROT_SOCK + 1 + (start % 1024);
+   if (base <= PROT_SOCK) {
+ 	base += PROT_SOCK + (start % 1024);
+   }
+ 
+   /* Now look through the entire array and try to find an empty ptr. */
+   for(i=0; i < SOCK_ARRAY_SIZE; i++) {
+ 	j = 0;
+ 	cli();
+ 	sk = prot->sock_array[(i + base + 1) & (SOCK_ARRAY_SIZE -1)];
+ 	while(sk != NULL) {
+ 		sk = sk->next;
+ 		j++;
+ 	}
+ 	sti();
+ 	if (j == 0) {
+ 		start = ((i + 1 + start ) % 1024);
+ 		DPRINTF((DBG_INET,
+ 			"INET: get_new_socknum returning %d, start = %d\n",
+ 							i + base + 1, start));
+ 		return(i + base + 1);
+ 	}
+ 	if (j < size) {
+ 		best = i;
+ 		size = j;
+ 	}
+   }
+ 
+   /* Now make sure the one we want is not in use. */
+   while(sk_inuse(prot, base + best + 1)) {
+ 	best += SOCK_ARRAY_SIZE;
+   }
+   DPRINTF((DBG_INET, "INET: get_new_socknum returning %d, start = %d\n",
+ 						best + base + 1, start));
+   return(best + base + 1);
+ }
+ 
+ 
+ void
+ put_sock(unsigned short num, struct sock *sk)
+ {
+   struct sock *sk1;
+   struct sock *sk2;
+ #if 0
+   int mask;
+ #endif
+ 
+   DPRINTF((DBG_INET, "INET: put_sock(num = %d, sk = 0x%X)\n", num, sk));
+   sk->num = num;
+   sk->next = NULL;
+   num = num & (SOCK_ARRAY_SIZE -1);
+ 
+   /* We can't have an interupt re-enter here. */
+   cli();
+   if (sk->prot->sock_array[num] == NULL) {
+ 	sk->prot->sock_array[num] = sk;
+ 	sti();
+ 	return;
+   }
+   sti();
+ #if 0
+   for(mask = 0xFF000000UL; mask != 0xFFFFFFFFUL; mask = (mask >> 8) | mask) {
+ 	if ((mask & sk->saddr) &&
+ 	    (mask & sk->saddr) != (mask & 0xFFFFFFFFUL)) {
+ 		mask = (mask << 8);
+ 		break;
+ 	}
+   }
+   DPRINTF((DBG_INET, "INET: mask = 0x%X\n", mask));
+ #endif
+ 
+   cli();
+   sk1 = sk->prot->sock_array[num];
+   for(sk2 = sk1; sk2 != NULL; sk2 = sk2->next) {
+ #if 0
+ 	if (!(sk2->saddr & mask)) {
+ #else
+ 	/* Insert sk before sk2 if sk2 has not a more specific address. */
+ 	/* if sk->daddr != NULL, then sk->saddr != NULL */
+ 	if ((sk->daddr || !sk2->daddr)
+ 	  && (sk->saddr || !sk2->saddr)) {
+ #endif
+ 		if (sk2 == sk1) {
+ 			sk->next = sk->prot->sock_array[num];
+ 			sk->prot->sock_array[num] = sk;
+ 			sti();
+ 			return;
+ 		}
+ 		sk->next = sk2;
+ 		sk1->next = sk;
+ 		sti();
+ 		return;
+ 	}
+ 	sk1 = sk2;
+   }
+ 
+   /* Goes at the end. */
+   sk->next = NULL;
+   sk1->next = sk;
+   sti();
+ }
+ 
+ 
+ static void
+ remove_sock(struct sock *sk1)
+ {
+   struct sock *sk2;
+ 
+   DPRINTF((DBG_INET, "INET: remove_sock(sk1 = 0x%X)\n", sk1));
+   if (sk1 == NULL) {
+ 	printk("INET: remove_sock: sk1 == NULL\n");
+ 	return;
+   }
+ 
+   if (sk1->prot == NULL) {
+ 	printk("INET: remove_sock: sk1->prot == NULL\n");
+ 	return;
+   }
+ 
+   /* We can't have this changing out from under us. */
+   cli();
+   sk2 = sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)];
+   if (sk2 == sk1) {
+ 	sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)] = sk1->next;
+ 	sti();
+ 	return;
+   }
+ 
+   while(sk2 && sk2->next != sk1) {
+ 	sk2 = sk2->next;
+   }
+ 
+   if (sk2) {
+ 	sk2->next = sk1->next;
+ 	sti();
+ 	return;
+   }
+   sti();
+ 
+   if (sk1->num != 0)
+ 	DPRINTF((DBG_INET, "INET: remove_sock: sock not found.\n"));
+ }
+ 
+ 
+ void
+ destroy_sock(struct sock *sk)
+ {
+   struct sk_buff *skb;
+ 
+   DPRINTF((DBG_INET, "INET: destroying socket 0x%X\n", sk));
+   sk->inuse = 1;			/* just to be safe. */
+ 
+   /* Incase it's sleeping somewhere. */
+   if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+   remove_sock(sk);
+ 
+   /* Now we can no longer get new packets. */
+   inet_delete_timer(sk);
+ 
+   cli();
+   if (sk->send_tmp != NULL) {
+ 	inet_kfree_skb(sk->send_tmp, FREE_WRITE);
+ 	sk->send_tmp = NULL;
+   }
+ 
+   /* Cleanup up the write buffer. */
+   for(skb = sk->wfront; skb != NULL; ) {
+ 	struct sk_buff *skb2;
+ 
+ 	skb2 = skb->qnext;
+ 	inet_kfree_skb(skb, FREE_WRITE);
+ 	skb = skb2;
+   }
+   sk->wfront = NULL;
+   sk->wback = NULL;
+ 
+   sti(); /* icky */
+   nop();
+   cli();
+ 
+   if (sk->rqueue != NULL) {
+ 	skb = sk->rqueue;
+ 	do {
+ 		struct sk_buff *skb2;
+ 
+ 		skb2 = skb->qnext;
+ 
+ 		/*
+ 		 * This will take care of closing sockets that were
+ 		 * listening and didn't accept everything.
+ 		 */
+ 		if (skb->sk != NULL && skb->sk != sk) {
+ 			skb->sk->dead = 1;
+ 			skb->sk->prot->close(skb->sk, 0);
+ 		}
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		skb = skb2;
+ 	} while(skb != sk->rqueue);
+   }
+   sk->rqueue = NULL;
+ 
+   sti(); /* icky */
+   nop();
+   cli();
+ 
+   /* Now we need to clean up the send head. */
+   for(skb = sk->send_head; skb != NULL; ) {
+ 	struct sk_buff *skb2;
+ 
+ 	/* We need to remove skb from the transmit queue. */
+ 	dev_delque(skb);
+ 	skb2 = skb->link3;
+ 	inet_kfree_skb_sk(sk, skb, FREE_WRITE);
+ 	skb = skb2;
+   }
+ 
+   sk->send_head = NULL;
+   sti();
+ 
+   /* And now the backlog. */
+   if (sk->back_log != NULL) {
+ 	/* This should never happen. */
+ 	printk("cleaning back_log.\n");
+ 	cli();
+ 	skb = sk->back_log;
+ 	do {
+ 		struct sk_buff *skb2;
+ 
+ 		skb2 = skb->qnext;
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		skb = skb2;
+ 	} while(skb != sk->back_log);
+ 	sti();
+   }
+   sk->back_log = NULL;
+ 
+   /* Now if it has a half accepted/ closed socket. */
+   if (sk->pair) {
+ 	sk->pair->dead = 1;
+ 	sk->pair->prot->close(sk->pair, 0);
+ 	sk->pair = NULL;
+   }
+ 
+   /*
+    * Now if everything is gone we can free the socket
+    * structure, otherwise we need to keep it around until
+    * everything is gone.
+    */
+   if (sk->dead && sk->rmem_alloc == 0 && sk->wmem_alloc == 0) {
+ 	inet_delete_timer(sk);
+ 	kfree_s((void *) sk, sizeof(*sk));
+   } else {
+ 	/* this should never happen. */
+ 	/* actually it can if an ack has just been sent. */
+ 	DPRINTF((DBG_INET, "INET: destroy_sock: possible memory leak in socket = %X\n", sk));
+ 	sk->destroy = 1;
+ 	sk->ack_backlog = 0;
+ 	sk->inuse = 0;
+ 	sk->keepopen = 0;
+ 	inet_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
+   }
+   DPRINTF((DBG_INET, "INET: destroy_sock: leaving destroy_sock\n"));
+ }
+ 
+ 
+ static int
+ inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: fcntl: warning: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   switch(cmd) {
+ 	case F_SETOWN:
+ 		/*
+ 		 * This is a little restrictive, but it's the only
+ 		 * way to make sure that you can't send a sigurg to
+ 		 * another process.
+ 		 */
+ 		if (!suser() && current->pgrp != -arg &&
+ 				current->pid != arg) return(-EPERM);
+ 		sk->proc = arg;
+ 		return(0);
+ 
+ 	case F_GETOWN:
+ 		return(sk->proc);
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+ }
+ 
+ 
+ /* Set socket options on an inet socket. */
+ static int
+ inet_setsockopt(struct socket *sock, int level, int optname,
+ 		char *optval, int optlen)
+ {
+   struct linger ling;
+   struct sock *sk;  
+   int val;
+ 
+   DPRINTF((DBG_INET,
+ 	"INET: setsockopt: sock=0x%X level=%d name=%d val=0x%X len=%d\n",
+ 				sock, level, optname, optval, optlen));
+ 
+   if (optval == NULL) return(-EINVAL);
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: setsockopt: warning: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* Are we supposed to do generic socket-level stuff? */
+   if (level == SOL_SOCKET) switch(optname) {
+ 	case SO_TYPE:
+ 		DPRINTF((DBG_INET, "INET: setsockopt: SO_TYPE !\n"));
+ 	  	return(-ENOPROTOOPT);
+ 
+ 	case SO_ERROR:
+ 		DPRINTF((DBG_INET, "INET: setsockopt: SO_ERROR !\n"));
+ 	  	return(-ENOPROTOOPT);
+ 
+ 	case SO_DONTROUTE:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_DONTROUTE(%d) !\n", val));
+ 		sk->dontroute = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	case SO_SNDBUF:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET, "INET: setsockopt: SO_SNDBUF(%d) !\n", val));
+ 		if (val > 32767) val = 32767;
+ 		if (val < 256) val = 256;
+ 		sk->sndbuf = val;
+ 		return(0);
+ 
+ 	case SO_RCVBUF:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET, "INET: setsockopt: SO_RCVBUF(%d) !\n", val));
+ 		if (val > 32767) val = 32767;
+ 		if (val < 256) val = 256;
+ 		sk->rcvbuf = val;
+ 		return(0);
+ 
+ 	case SO_DEBUG:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET, "INET: setsockopt: SO_DEBUG(%d) !\n", val));
+ 		sk->debug = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	case SO_BROADCAST:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_BROADCAST(%d) !\n", val));
+ 		sk->broadcast = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	case SO_REUSEADDR:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_REUSEADDR(%d) !\n", val));
+ 		sk->reuse = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	case SO_KEEPALIVE:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_KEEPALIVE(%d) !\n", val));
+ 		sk->keepopen = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	 case SO_OOBINLINE:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_OOBINLINE(%d) !\n", val));
+ 		sk->urginline = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	 case SO_NO_CHECK:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_NO_CHECK(%d) !\n", val));
+ 		sk->no_check = (val) ? 1 : 0;
+ 		return(0);
+ 
+ 	 case SO_PRIORITY:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(int));
+ 		if (val < 0) return(val);
+ 		val = get_fs_long((unsigned long *)optval);
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: SO_PRIORITY(%d) !\n", val));
+ 		if (val >= 0 && val < IFF_MAX_PRIO) sk->priority = val;
+ 		  else return(-EINVAL);
+ 		return(0);
+ 
+ 	case SO_LINGER:
+ 		val = verify_area(VERIFY_READ, optval, sizeof(ling));
+ 		if (val < 0) return(val);
+ 		memcpy_fromfs(&ling, (struct linger *) optval,
+ 			      sizeof(struct linger));
+ 		if (ling.l_onoff == 0) {
+ 			sk->linger = 0;
+ 		} else {
+ 			sk->linger = 1;
+ 			sk->lingertime = ling.l_linger;
+ 		}
+ 		DPRINTF((DBG_INET, "INET: setsockopt: SO_LINGER(%d, %lu) !\n",
+ 						sk->linger, sk->lingertime));
+ 		return(0);
+ 
+ 	default:
+ 		DPRINTF((DBG_INET,
+ 			"INET: setsockopt: funny option %d !\n", optname));
+ 	  	return(-ENOPROTOOPT);
+   }
+ 
+   /* No, it is protocol dependent. */
+   DPRINTF((DBG_INET, "INET: setsockopt: proto func=0x%X\n",
+ 						sk->prot->setsockopt));
+   if (sk->prot->setsockopt == NULL) return(-EOPNOTSUPP);
+     else return(sk->prot->setsockopt(sk, level, optname, optval, optlen));
+ }
+ 
+ 
+ /* Get socket options from an inet socket. */
+ static int
+ inet_getsockopt(struct socket *sock, int level, int optname,
+ 		char *optval, int *optlen)
+ {
+   struct linger ling;
+   struct sock *sk;  
+   int err, val;
+ 
+   DPRINTF((DBG_INET,
+ 	"INET: getsockopt: sock=0x%X level=%d name=%d val=0x%X len=%d\n",
+ 				sock, level, optname, optval, optlen));
+ 
+   if (optval == NULL) return(-EINVAL);
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: getsockopt: warning: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* Are we supposed to do generic socket-level stuff? */
+   if (level == SOL_SOCKET) {
+ 	switch(optname) {
+ 		case SO_TYPE:
+ 			val = sk->type;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_TYPE(%d)\n", val));
+ 			break;
+ 
+ 		case SO_ERROR:
+ 			val = sk->err;
+ 			sk->err = 0;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_ERROR(%d)\n", val));
+ 			break;
+ 
+ 		case SO_DONTROUTE:
+ 			val = sk->dontroute;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_DONTROUTE(%d)\n", val));
+ 			break;
+ 
+ 		case SO_SNDBUF:
+ 			val = sk->sndbuf;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_SNDBUF(%d)\n", val));
+ 			break;
+ 
+ 		case SO_RCVBUF:
+ 			val = sk->rcvbuf;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_RCVBUF(%d)\n", val));
+ 			break;
+ 
+ 		case SO_DEBUG:
+ 			val = sk->debug;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_DEBUG(%d)\n", val));
+ 			break;
+ 
+ 		case SO_BROADCAST:
+ 			val = sk->broadcast;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_BROADCAST(%d)\n", val));
+ 			break;
+ 
+ 		case SO_REUSEADDR:
+ 			val = sk->reuse;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_REUSEADDR(%d)\n", val));
+ 			break;
+ 
+ 		case SO_KEEPALIVE:
+ 			val = sk->keepopen;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_KEEPALIVE(%d)\n", val));
+ 			break;
+ 
+ 		case SO_OOBINLINE:
+ 			val = sk->urginline;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_OOBINLINE(%d)\n", val));
+ 			break;
+ 
+ 		case SO_NO_CHECK:
+ 			val = sk->no_check;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_NO_CHECK(%d)\n", val));
+ 			break;
+ 
+ 		case SO_PRIORITY:
+ 			val = sk->priority;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_LINGER(%d)\n", val));
+ 			break;
+ 
+ 		case SO_LINGER:
+ 			val = verify_area(VERIFY_WRITE, optval, sizeof(ling));
+ 			if (val < 0) return(val);
+ 			val = verify_area(VERIFY_WRITE, optlen, sizeof(int));
+ 			if (val < 0) return(val);
+ 			put_fs_long(sizeof(ling), (unsigned long *) optlen);
+ 			ling.l_onoff = sk->linger;
+ 			ling.l_linger = sk->lingertime;
+ 			DPRINTF((DBG_INET,
+ 				"INET: getsockopt: SO_LINGER(%d, %lu)\n",
+ 					ling.l_onoff, ling.l_linger));
+ 			memcpy_tofs((struct linger *) optval, &ling,
+ 				    sizeof(ling));
+ 			return(0);
+ 
+ 		default:
+ 			DPRINTF((DBG_INET,
+ 			    "INET: getsockopt: funny option %d !\n", optname));
+ 			return(-ENOPROTOOPT);
+ 	}
+ 
+ 	err = verify_area(VERIFY_WRITE, optlen, sizeof(int));
+ 	if (err < 0) return(err);
+ 	put_fs_long(sizeof(int), (unsigned long *) optlen);
+ 
+ 	err = verify_area(VERIFY_WRITE, optval, sizeof(int));
+ 	if (err < 0) return(err);
+ 	put_fs_long(val, (unsigned long *) optval);
+ 
+ 	return(0);
+   }
+ 
+   /* No, it is protocol dependent. */
+   DPRINTF((DBG_INET, "INET: getsockopt: proto func=0x%X\n",
+ 						sk->prot->setsockopt));
+   if (sk->prot->getsockopt == NULL) return(-EOPNOTSUPP);
+     else return(sk->prot->getsockopt(sk, level, optname, optval, optlen));
+ }
+ 
+ 
+ static int
+ inet_listen(struct socket *sock, int backlog)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: listen: warning: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = ntohs(sk->num);
+   }
+ 
+   /* We might as well re use these. */ 
+   sk->max_ack_backlog = backlog;
+   if (sk->state != TCP_LISTEN) {
+ 	sk->ack_backlog = 0;
+ 	sk->state = TCP_LISTEN;
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_create(struct socket *sock, int protocol)
+ {
+   struct sock *sk;
+   struct proto *prot;
+   int err;
+ 
+   sk = (struct sock *) kmalloc(sizeof(struct sock), GFP_KERNEL);
+   if (sk == NULL) return(-ENOMEM);
+   memset(sk, 0, sizeof(struct sock));
+   sk->num = 0;
+   sk->reuse = 0;
+ 
+   switch(sock->type) {
+ 	case SOCK_STREAM:
+ 	case SOCK_SEQPACKET:
+ 		if (protocol && protocol != IPPROTO_TCP) {
+ 			kfree_s((void *) sk, sizeof(*sk));
+ 			return(-EPROTONOSUPPORT);
+ 		}
+ 		protocol = IPPROTO_TCP;
+ 		prot = &tcp_prot;
+ 		sk->no_check = TCP_NO_CHECK;
+ 		break;
+ 
+ 	case SOCK_DGRAM:
+ 		if (protocol && protocol != IPPROTO_UDP) {
+ 			kfree_s((void *) sk, sizeof(*sk));
+ 			return(-EPROTONOSUPPORT);
+ 		}
+ 		protocol = IPPROTO_UDP;
+ 		prot = &udp_prot;
+ 		sk->no_check = UDP_NO_CHECK;
+ 		break;
+       
+ 	case SOCK_RAW:
+ 		if (! suser()) {
+ 			kfree_s((void *) sk, sizeof(*sk));
+ 			return(-EPERM);
+ 		}
+ 		if (protocol == 0) {
+ 			kfree_s((void *) sk, sizeof(*sk));
+ 			return(-EPROTONOSUPPORT);
+ 		}
+ 		prot = &raw_prot;
+ 		sk->reuse = 1;
+ 		sk->num = protocol;
+ 		sk->no_check = 0;
+ 		break;
+ 
+ 	default:
+ 		kfree_s((void *) sk, sizeof(*sk));
+ 		return(-ESOCKTNOSUPPORT);
+   }
+ 
+   /*
+    * Fill in the structure.
+    * First, do the basic INET socket stuff.
+    */
+   sk->protocol = protocol;
+   sk->prot = prot;
+   sk->socket = sock;
+   sk->type = sock->type;
+   sk->wmem_alloc = 0;
+   sk->rmem_alloc = 0;
+   sk->sndbuf = SK_WMEM_MAX;
+   sk->rcvbuf = SK_RMEM_MAX;
+   sk->pair = NULL;
+   sk->opt = NULL;
+   sk->proc = 0;
+   sk->state = TCP_CLOSE;
+   sk->dead = 0;
+ 
+   /* Socket flags. */
+   sk->linger = 0;
+   sk->inuse = 0;
+   sk->debug = 0;
+   sk->broadcast = 0;
+   sk->keepopen = 0;
+   sk->dontroute = 0;
+   sk->priority = SOPRI_NORMAL;
+   sk->urginline = 0;
+ 
+   /* Queues and backlogs. */
+   sk->send_tail = NULL;
+   sk->send_head = NULL;
+   sk->rqueue = NULL;
+   sk->wfront = NULL;
+   sk->wback = NULL;
+   sk->send_tmp = NULL;
+   sk->back_log = NULL;
+   sk->blog = 0;
+ 
+   /* Socket timer fields. */
+   sk->timer.data = (unsigned long)sk;
+   sk->timer.function = inet_timer;
+   sk->timeout = 0;
+ 
+   /* Socket control fields. */
+   sk->tos = 0;
+   sk->ttl = ip_parms.defttl;
+   sk->rtt = TCP_WRITE_TIME;
+   sk->mdev = 0;
+   sk->backoff = 0;
+   sk->sleep = sock->wait;
+   sk->daddr = 0;
+   sk->saddr = INADDR_ANY;
+   sk->err = 0;
+   sk->next = NULL;
+   sk->packets_out = 0;
+   sk->cong_window = 1;	/* start with only sending one packet at a time	*/
+   sk->exp_growth = 1;			/* if set cong_window grow	*/
+ 					/* exponentially every time	*/
+ 					/* we get an ACK		*/
+ 
+   /*
+    * Misc fields.
+    * This should be changed into a per-protocol "socket control
+    * block" which gets filled in by the protocol handler, rather
+    * than by this layer, as most of these are only used by the
+    * TCP protocol at present.  This would considerably clean up
+    * the mess in this layer... -FvK
+    */
+   sk->intr = 0;
+   sk->destroy = 0;
+   sk->shutdown = 0;
+   sk->urg = 0;
+   sk->done = 0;
+   sk->window = 0;
+   sk->ack_backlog = 0;
+   sk->ack_timed = 0;
+   sk->bytes_rcv = 0;
+   sk->pair = NULL;
+   sk->send_seq = 0;
+   sk->acked_seq = 0;
+   sk->copied_seq = 0;
+   sk->fin_seq = 0;
+   sk->mss = 576;			/* we will try not to send any	*/
+ 					/* packets smaller than this	*/
+   sk->max_unacked = 2048;		/* this is how many unacked	*/
+ 					/* bytes we will accept for	*/
+ 					/* this socket. Needs to be at	*/
+ 					/* most 2 full packets		*/
+   sk->max_ack_backlog = 0;		/* how many packets we should	*/
+ 					/* send before forcing an ACK.	*/
+ 					/* If this is set to zero it is	*/
+ 					/* the same as delay_acks = 0	*/
+   sk->delay_acks = 0;
+   sk->dummy_th.doff = sizeof(sk->dummy_th) / sizeof(unsigned long);
+   sk->dummy_th.res1 = 0;
+   sk->dummy_th.res2 = 0;
+   sk->dummy_th.urg_ptr = 0;
+   sk->dummy_th.fin = 0;
+   sk->dummy_th.syn = 0;
+   sk->dummy_th.rst = 0;
+   sk->dummy_th.psh = 0;
+   sk->dummy_th.ack = 0;
+   sk->dummy_th.urg = 0;
+   sk->dummy_th.dest = 0;
+ 
+   sock->data = (void *) sk;
+ 
+   if (sk->num != 0) {
+ 	/*
+ 	 * It assumes that any protocol which allows the user to assign
+ 	 * a number at socket creation time automatically shares.
+ 	 */
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = htons(sk->num);
+   }
+ 
+   if (sk->prot->init) {
+ 	err = sk->prot->init(sk);
+ 	if (err != 0) {
+ 		sk->dead = 1;	/* ??? */
+ 		destroy_sock(sk);
+ 		return(err);
+ 	}
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_dup(struct socket *newsock, struct socket *oldsock)
+ {
+   return(inet_create(newsock,
+ 		   ((struct sock *)(oldsock->data))->protocol));
+ }
+ 
+ 
+ /* The peer socket should always be NULL. */
+ static int
+ inet_release(struct socket *sock, struct socket *peer)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) return(0);
+ 
+   DPRINTF((DBG_INET, "INET: release: sock=0x%X, peer=0x%X\n", sock, peer));
+   wake_up_interruptible(sk->sleep);
+ 
+   /*
+    * Start closing the connection; this may take a while.
+    * If SO_LINGER is set, we don't return until the close
+    * is complete.  Otherwise we return immediately.  The
+    * actually closing is done the same either way.
+    */
+   if (sk->linger == 0) {
+ 	sk->prot->close(sk, 0);
+ 	sk->dead = 1;
+   } else {
+ 	DPRINTF((DBG_INET, "INET: release: SO_LINGER set.\n"));
+ 	sk->prot->close(sk, 0);
+ 	cli();
+ 	if (sk->lingertime > 0)
+ 		current->timeout = jiffies + (sk->lingertime * 100);
+ 
+ 	while((sk->state != TCP_CLOSE) && (current->timeout > 0)) {
+ 		interruptible_sleep_on(sk->sleep);
+ 		if (current->signal & ~current->blocked) {
+ 			break;
+ #if 0	/* not working now - closes cannot be restarted */
+ 			sti();
+ 			current->timeout = 0;
+ 			return(-ERESTARTSYS);
+ #endif
+ 		}
+ 	}
+ 	current->timeout = 0;
+ 	sti();
+ 	sk->dead = 1;
+   }
+   sk->inuse = 1;
+ 
+   /* This will destroy it. */
+   release_sock(sk);
+   sock->data = NULL;
+   DPRINTF((DBG_INET, "INET: release: returning\n"));
+   return(0);
+ }
+ 
+ 
+ /*
+  * This needs to be changed to dissallow the rebinding of
+  * sockets.  What error should it return?
+  */
+ static int
+ inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+ {
+   struct sockaddr_in addr;
+   struct sock *sk, *sk2;
+   unsigned short snum;
+   int err;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: bind: warning: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* FIXME: check this error. */
+   if (sk->state != TCP_CLOSE) return(-EIO);
+   if (sk->num != 0) return(-EINVAL);
+ 
+   err = verify_area(VERIFY_READ, uaddr, addr_len);
+   if (err < 0) return(err);
+   memcpy_fromfs(&addr, uaddr, min(sizeof(addr), addr_len));
+ 
+ #if SK_BSD_BUG
+   if (addr.sin_family && addr.sin_family != AF_INET) {
+ 	/*
+ 	 * This is really a bug in BSD which we need to emulate because
+ 	 * ftp(1) expects it.  Although the more recent versions seem to
+ 	 * do it right -FvK
+ 	 */
+ 	return(-EINVAL);
+   }
+ #endif
+ 
+   snum = ntohs(addr.sin_port);
+   sk = (struct sock *) sock->data;
+   DPRINTF((DBG_INET, "INET: bind: sk=0x%X to port=%d\n", sk, snum));
+ 
+   /*
+    * We can't just leave the socket bound wherever it is, it might
+    * be bound to a privileged port. However, since there seems to
+    * be a bug here, we will leave it if the port is not privileged.
+    */
+   if (snum == 0) {
+ 	snum = get_new_socknum(sk->prot, 0);
+   }
+   if (snum < PROT_SOCK && !suser()) return(-EACCES);
+ 
+   /*
+    * The source address of a BIND operation MUST be one of our own...
+    * Please note that this check breaks a lot of BSD-ish server code,
+    * as they often don't initialize the sin_addr field to INADDR_ANY
+    * or (simpler) all zeroes. Syslogd 5.27 is one of those programs!
+    */
+   if ((addr.sin_addr.s_addr != INADDR_ANY) &&
+       in_chk_addr(addr.sin_addr.s_addr) != IS_MYADDR) return(-EADDRNOTAVAIL);
+   if ((addr.sin_addr.s_addr == INADDR_ANY) ||
+       in_chk_addr(addr.sin_addr.s_addr)) sk->saddr = addr.sin_addr.s_addr;
+   DPRINTF((DBG_INET, "INET: bind: sock_array[%d] = 0x%X:\n",
+ 		snum & (SOCK_ARRAY_SIZE -1),
+ 	  	sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]));
+ 
+   /* Make sure we are allowed to bind here. */
+ outside_loop:
+   cli();
+   for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
+ 					sk2 != NULL; sk2 = sk2->next) {
+ 	if (sk2->num != snum) continue;
+ 	if (sk2->dead) {
+ 		destroy_sock(sk2);	/* we need to search the list again */
+ 		goto outside_loop;
+ 	}
+ 
+ 	/*
+ 	 * Port reuse is allowed if the new socket has
+ 	 * SO_REUSEADDR and either the old socket is
+ 	 * connected, or the old socket has a different
+ 	 * address.
+ 	 */
+ 	if (sk->reuse) {
+ 		if ((sk2->socket->state == SS_CONNECTED) ||
+ 		    (sk2->saddr != sk->saddr)) continue;
+ 	}
+ 
+ 	sti();
+ 	return(-EADDRINUSE);
+   }
+   sti();
+ 
+   remove_sock(sk);
+   put_sock(snum, sk);
+   sk->dummy_th.source = htons(sk->num);
+   sk->daddr = INADDR_ANY;
+   sk->dummy_th.dest = htons(0);		/* OKOK, call me paraoid... */
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_connect(struct socket *sock, struct sockaddr *uaddr,
+ 	     int addr_len, int flags)
+ {
+   struct sock *sk;
+   int err;
+ 
+   sock->conn = NULL;
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: connect: warning: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* Connection completing after a connect/EINPROGRESS/select/connect? */
+   if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED) {
+ 	sock->state = SS_CONNECTED;
+ 	return(0);
+   }
+ 
+   if (sock->state != SS_CONNECTING) {
+ 	/* We may need to bind the socket. */
+ 	if (sk->num == 0) {
+ 		sk->num = get_new_socknum(sk->prot, 0);
+ 		if (sk->num == 0) return(-EAGAIN);
+ 		put_sock(sk->num, sk);
+ 		sk->dummy_th.source = htons(sk->num);
+ 	}
+ 	if (sk->prot->connect == NULL) return(-EOPNOTSUPP);
+   
+ 	err = sk->prot->connect(sk, (struct sockaddr_in *)uaddr, addr_len);
+ 	if (err < 0) return(err);
+   
+ 	sock->state = SS_CONNECTING;
+   }
+ 
+   if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+ 						return(-EINPROGRESS);
+ 
+   cli(); /* avoid the race condition */
+   while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+ 	interruptible_sleep_on(sk->sleep);
+ 	if (current->signal & ~current->blocked) {
+ 		sti();
+ 		return(-ERESTARTSYS);
+ 	}
+ 
+ 	/*
+ 	 * This fixes a nasty race in the TCP/IP code. There
+ 	 * is a hideous hassle with ICMP error packets wanting
+ 	 * to close a TCP or UDP socket.
+ 	 */
+ 	if (sk->err && sk->protocol == IPPROTO_TCP) {
+ 		DPRINTF((DBG_INET, "INET: connect: woke up, err is %d\n",
+ 							sk->err));
+ 		err = -sk->err;
+ 		sk->err = 0;
+ 		sti();
+ 		sk->prot->close(sk, 0);		/* kill it */
+ 		sock->state = SS_UNCONNECTED;
+ 		return(err);			/* set by tcp_err() */
+ 	}
+   }
+ 
+   sti();
+   sock->state = SS_CONNECTED;
+ 
+   if (sk->state != TCP_ESTABLISHED && sk->err) {
+ 	sock->state = SS_UNCONNECTED;
+ 	err = -sk->err;
+ 	sk->err = 0;
+ 	return(err);
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_socketpair(struct socket *sock1, struct socket *sock2)
+ {
+   return(-EOPNOTSUPP);
+ }
+ 
+ 
+ static int
+ inet_accept(struct socket *sock, struct socket *newsock, int flags)
+ {
+   struct sock *sk1, *sk2;
+   int err;
+ 
+   sk1 = (struct sock *) sock->data;
+   if (sk1 == NULL) {
+ 	printk("INET: accept: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /*
+    * We've been passed an extra socket.
+    * We need to free it up because the TCP module creates
+    * it's own when it accepts one.
+    */
+   if (newsock->data) {
+   	struct sock *sk = (struct sock *) newsock->data;
+ 
+   	newsock->data = NULL;
+ 	sk->dead = 1;
+   	destroy_sock(sk);
+   }
+ 
+   if (sk1->prot->accept == NULL) return(-EOPNOTSUPP);
+ 
+   /* Restore the state if we have been interrupted, and then returned. */
+   if (sk1->pair != NULL ) {
+ 	sk2 = sk1->pair;
+ 	sk1->pair = NULL;
+   } else {
+ 	sk2 = sk1->prot->accept(sk1, flags);
+ 	if (sk2 == NULL) {
+ 		err = -sk1->err;
+ 		sk1->err = 0;
+ 		return(err);
+ 	}
+   }
+   newsock->data = (void *) sk2;
+   sk2->sleep = newsock->wait;
+   newsock->conn = NULL;
+   if (flags & O_NONBLOCK) return(0);
+ 
+   cli(); /* avoid the race. */
+   while(sk2->state == TCP_SYN_RECV) {
+ 	interruptible_sleep_on(sk2->sleep);
+ 	if (current->signal & ~current->blocked) {
+ 		sti();
+ 		sk1->pair = sk2;
+ 		sk2->sleep = NULL;
+ 		newsock->data = NULL;
+ 		return(-ERESTARTSYS);
+ 	}
+   }
+   sti();
+ 
+   if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) {
+ 	int err2;
+ 
+ 	err2 = -sk2->err;
+ 	sk2->err = 0;
+ 	destroy_sock(sk2);
+ 	newsock->data = NULL;
+ 	return(err2);
+   }
+   newsock->state = SS_CONNECTED;
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_getname(struct socket *sock, struct sockaddr *uaddr,
+ 	     int *uaddr_len, int peer)
+ {
+   struct sockaddr_in sin;
+   struct sock *sk;
+   int err, len;
+ 
+   /* FIXME: check this error. */
+   err = verify_area(VERIFY_WRITE, uaddr_len, sizeof(int));
+   if (err < 0) return(err);
+   len = get_fs_long((int *) uaddr_len);
+   if (len < sizeof(struct sockaddr_in)) return(-EINVAL);
+   err = verify_area(VERIFY_WRITE, uaddr, len);
+   if (err < 0) return(err);
+ 
+   sin.sin_family = AF_INET;
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: getpeername: sock->data = NULL !\n");
+ 	return(0);
+   }
+   if (peer) {
+ 	if (! tcp_connected(sk->state)) return(-ENOTCONN);
+ 	sin.sin_port = sk->dummy_th.dest;
+ 	sin.sin_addr.s_addr = sk->daddr;
+   } else {
+ 	sin.sin_port = sk->dummy_th.source;
+ 	sin.sin_addr.s_addr = sk->saddr;	/* might be INADDR_ANY */
+   }
+   len = sizeof(sin);
+   memcpy_tofs(uaddr, &sin, len);
+   put_fs_long(len, (int *) uaddr_len);
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_read(struct socket *sock, char *ubuf, int size, int noblock)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: read: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = ntohs(sk->num);
+   }
+   return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock,0));
+ }
+ 
+ 
+ static int
+ inet_recv(struct socket *sock, void *ubuf, int size, int noblock,
+ 	  unsigned flags)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: recv: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = ntohs(sk->num);
+   }
+   return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock, flags));
+ }
+ 
+ 
+ static int
+ inet_write(struct socket *sock, char *ubuf, int size, int noblock)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: write: sock->data = NULL !\n");
+ 	return(0);
+   }
+   if (sk->shutdown & SEND_SHUTDOWN) {
+ 	send_sig(SIGPIPE, current, 1);
+ 	return(-EPIPE);
+   }
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = ntohs(sk->num);
+   }
+ 
+   return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, 0));
+ }
+ 
+ 
+ static int
+ inet_send(struct socket *sock, void *ubuf, int size, int noblock, 
+ 	       unsigned flags)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: send: sock->data = NULL !\n");
+ 	return(0);
+   }
+   if (sk->shutdown & SEND_SHUTDOWN) {
+ 	send_sig(SIGPIPE, current, 1);
+ 	return(-EPIPE);
+   }
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = ntohs(sk->num);
+   }
+ 
+   return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));
+ }
+ 
+ 
+ static int
+ inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, 
+ 	    unsigned flags, struct sockaddr *sin, int addr_len)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: sendto: sock->data = NULL !\n");
+ 	return(0);
+   }
+   if (sk->shutdown & SEND_SHUTDOWN) {
+ 	send_sig(SIGPIPE, current, 1);
+ 	return(-EPIPE);
+   }
+   if (sk->prot->sendto == NULL) return(-EOPNOTSUPP);
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = ntohs(sk->num);
+   }
+ 
+   return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, 
+ 			   (struct sockaddr_in *)sin, addr_len));
+ }
+ 
+ 
+ static int
+ inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, 
+               unsigned flags, struct sockaddr *sin, int *addr_len)
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: recvfrom: sock->data = NULL !\n");
+ 	return(0);
+   }
+   if (sk->prot->recvfrom == NULL) return(-EOPNOTSUPP);
+ 
+   /* We may need to bind the socket. */
+   if (sk->num == 0) {
+ 	sk->num = get_new_socknum(sk->prot, 0);
+ 	if (sk->num == 0) return(-EAGAIN);
+ 	put_sock(sk->num, sk);
+ 	sk->dummy_th.source = htons(sk->num);
+   }
+ 
+   return(sk->prot->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags,
+ 			     (struct sockaddr_in*)sin, addr_len));
+ }
+ 
+ 
+ static int
+ inet_shutdown(struct socket *sock, int how)
+ {
+   struct sock *sk;
+ 
+   /*
+    * FIXME:
+    * This should really check to make sure the socket is a TCP socket.
+    */
+   how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
+ 	  *	 1->2 bit 2 snds.
+ 	  *	 2->3
+ 	  */
+   if (how & ~SHUTDOWN_MASK) return(-EINVAL);
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: shutdown: sock->data = NULL !\n");
+ 	return(0);
+   }
+   if (sock->state == SS_CONNECTING &&
+ 	sk->state == TCP_ESTABLISHED) sock->state = SS_CONNECTED;
+ 
+   if (! tcp_connected(sk->state)) return(-ENOTCONN);
+   sk->shutdown |= how;
+   if (sk->prot->shutdown) sk->prot->shutdown(sk, how);
+   return(0);
+ }
+ 
+ 
+ static int
+ inet_select(struct socket *sock, int sel_type, select_table *wait )
+ {
+   struct sock *sk;
+ 
+   sk = (struct sock *) sock->data;
+   if (sk == NULL) {
+ 	printk("INET: select: sock->data = NULL !\n");
+ 	return(0);
+   }
+ 
+   if (sk->prot->select == NULL) {
+ 	DPRINTF((DBG_INET, "INET: select: non-selectable socket.\n"));
+ 	return(0);
+   }
+   return(sk->prot->select(sk, sel_type, wait));
+ }
+ 
+ 
+ void *
+ sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
+ {
+   struct sk_buff *skb;
+ 
+   if (sk != NULL) {
+ 	DPRINTF((DBG_INET, "INET: wmalloc: alloc=%d lim=%d size=%d\n",
+ 					sk->wmem_alloc, sk->sndbuf, size));
+ 	if ((sk->wmem_alloc + size < sk->sndbuf) || force) {
+ 		skb = skb_pcreate(size, priority);
+ 		cli();
+ 		if (skb != NULL) sk->wmem_alloc += skb->mem_len;
+ 		sti();
+ 		return((void *) skb);
+ 	}
+ 	DPRINTF((DBG_INET, "INET: wmalloc(0x%X, %d, %d, %d) returning NULL\n",
+ 						sk, size, force, priority));
+ 	return(NULL);
+   }
+   return((void *) skb_pcreate(size, priority));
+ }
+ 
+ 
+ void *
+ sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
+ {
+   struct sk_buff *skb;
+ 
+   if (sk != NULL) {
+ 	DPRINTF((DBG_INET, "INET: rmalloc: alloc=%d lim=%d size=%d\n",
+ 				sk->rmem_alloc, sk->rcvbuf, size));
+ 	if ((sk->rmem_alloc + size < sk->rcvbuf) || force) {
+ 		skb = skb_pcreate(size, priority);
+ 		cli();
+ 		if (skb != NULL) sk->rmem_alloc += skb->mem_len;
+ 		sti();
+ 		return((void *) skb);
+ 	}
+ 	DPRINTF((DBG_INET, "INET: rmalloc(0x%X, %d, %d, %d) returning NULL\n",
+ 						sk, size, force, priority));
+ 	return(NULL);
+   }
+   return((void *) skb_pcreate(size, priority));
+ }
+ 
+ 
+ unsigned long
+ sock_rspace(struct sock *sk)
+ {
+   int amt;
+ 
+   if (sk != NULL) {
+ 	/* FIXME: twice MIN_WINDOW ? -FvK */
+ 	if (sk->rmem_alloc >= (sk->rcvbuf - 2 * MIN_WINDOW)) return(0);
+ 	amt = min((sk->rcvbuf - sk->rmem_alloc) - MIN_WINDOW, MAX_WINDOW);
+ 	if (amt < 0) return(0);
+ 	return(amt);
+   }
+   return(0);
+ }
+ 
+ 
+ unsigned long
+ sock_wspace(struct sock *sk)
+ {
+   if (sk != NULL) {
+ 	if (sk->shutdown & SEND_SHUTDOWN) return(0);
+ 	if (sk->wmem_alloc >= sk->sndbuf) return(0);
+ 	return(sk->sndbuf - sk->wmem_alloc);
+   }
+   return(0);
+ }
+ 
+ 
+ void
+ sock_wfree(struct sock *sk, void *mem, unsigned long size)
+ {
+   DPRINTF((DBG_INET, "INET: wfree(sk=0x%X mem=0x%X size=%d)\n",
+ 						sk, mem, size));
+   skb_free((struct sk_buff *) mem);
+   if (sk != NULL) {
+ 	sk->wmem_alloc -= size;
+ 
+ 	/* In case it might be waiting for more memory. */
+ 	if (! sk->dead)
+ 		wake_up_interruptible(sk->sleep);
+ 	if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
+ 		DPRINTF((DBG_INET,
+ 			"INET: wfree: recovered lost memory, destroying sock = %X\n", sk));
+ 		inet_delete_timer(sk);
+ 		kfree_s((void *)sk, sizeof(*sk));
+ 	}
+ 	return;
+   }
+ }
+ 
+ 
+ void
+ sock_rfree(struct sock *sk, void *mem, unsigned long size)
+ {
+   DPRINTF((DBG_INET, "INET: rfree(sk=0x%X mem=0x%X size=%d)\n",
+ 						sk, mem, size));
+   skb_free((struct sk_buff *) mem);
+   if (sk != NULL) {
+ 	sk->rmem_alloc -= size;
+ 	if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
+ 		inet_delete_timer(sk);
+ 		kfree_s((void *)sk, sizeof(*sk));
+ 	}
+   }
+ }
+ 
+ 
+ /*
+  * Compare the local address of a socket.
+  * We now use "strict" matching.
+  */
+ static int
+ sock_check(unsigned long laddr, unsigned long raddr)
+ {
+   /* If the local address is INADDR_ANY, all is OK. */
+   if (laddr == INADDR_ANY) return(1);
+ 
+   /* If they match exactly... */
+   return(laddr == raddr);
+ }
+ 
+ 
+ /*
+  * This routine must find a socket given a TCP or UDP header.
+  * Everyhting is assumed to be in net order.
+  */
+ struct sock *
+ get_sock(struct proto *prot, unsigned short num, unsigned long raddr,
+ 	 unsigned short rnum, unsigned long laddr)
+ {
+   struct sock *s;
+   unsigned short hnum;
+ 
+   hnum = ntohs(num);
+   DPRINTF((DBG_INET, "INET: get_sock: prot=0x%X num=%d raddr=%s rnum=%d",
+ 		prot, hnum, in_ntoa(raddr), htons(rnum)));
+   DPRINTF((DBG_INET, " laddr=%s\n", in_ntoa(laddr)));
+ 
+   /*
+    * SOCK_ARRAY_SIZE must be a power of two.  This will work better
+    * than a prime unless 3 or more sockets end up using the same
+    * array entry.  This should not be a problem because most well
+    * known sockets don't overlap that much, and for the other ones,
+    * we can just be careful about picking our socket number when we
+    * choose an arbitrary one.
+    */
+   for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
+       s != NULL; s = s->next) {
+ 	if (s->num != hnum) continue;
+ 	if (s->dead && (s->state == TCP_CLOSE)) continue;
+ 
+ #if 0
+ 	/* If this is an UDP socket, all is OK now. */
+ 	if (prot == &udp_prot) return(s);
+ #endif
+ 
+ 	/* We need to see if this is the TCP socket that we want. */
+ 	if (! sock_check(s->daddr, raddr)) continue;
+ 
+ 	if ((s->dummy_th.dest != rnum) &&
+ 	    (s->dummy_th.dest != 0)) continue;
+ 
+ 	if (! sock_check(s->saddr, laddr)) continue;
+ 	return(s);
+   }
+   return(NULL);
+ }
+ 
+ 
+ void
+ release_sock(struct sock *sk)
+ {
+   if (sk == NULL) {
+ 	printk("INET: release_sock: sk == NULL\n");
+ 	return;
+   }
+   if (sk->prot == NULL) {
+ 	printk("INET: release_sock: sk->prot == NULL\n");
+ 	return;
+   }
+ 
+   if (sk->blog) return;
+ 
+   /* See if we have any packets built up. */
+   cli();
+   sk->inuse = 1;
+   while(sk->back_log != NULL) {
+ 	struct sk_buff *skb;
+ 
+ 	sk->blog = 1;
+ 	skb = sk->back_log;
+ 	DPRINTF((DBG_INET, "release_sock: skb = %X:\n", skb));
+ 	if (skb->qnext != skb) {
+ 		sk->back_log = skb->qnext;
+ 		skb->qprev->qnext = skb->qnext;
+ 		skb->qnext->qprev = skb->qprev;
+ 	} else {
+ 		sk->back_log = NULL;
+ 	}
+ 	sti();
+ 	DPRINTF((DBG_INET, "sk->back_log = %X\n", sk->back_log));
+ 	if (sk->prot->recv)
+ 		sk->prot->recv(skb, skb->dev, sk->opt, skb->saddr,
+ 		skb->len, skb->daddr, 1, (struct ipprot *)sk->pair); 
+ 	cli();
+   }
+   sk->blog = 0;
+   sk->inuse = 0;
+   sti();
+   if (sk->dead && sk->state == TCP_CLOSE) {
+ 	/* Should be about 2 rtt's */
+ 	inet_reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
+   }
+ }
+ 
+ 
+ #if 0
+ static int
+ inet_fread(struct inode *inode, struct file *file, char *buf, int count)
+ {
+   int minor, ret;
+ 
+   /* Extract the minor number on which we work. */
+   minor = MINOR(inode->i_rdev);
+ 
+   /* Now dispatch on the minor device. */
+   switch(minor) {
+ 	case 0:		/* INET */
+ 		ret = 0;
+ 		break;
+ 
+ 	case 1:		/* IP */
+ 		ret = 0;
+ 		break;
+ 
+ 	case 2:		/* ICMP */
+ 		ret = 0;
+ 		break;
+ 
+ 	case 3:		/* TCP */
+ 		ret = tcp_devread(buf, count);
+ 		break;
+ 
+ 	case 4:		/* UDP */
+ 		ret = udp_devread(buf, count);
+ 		break;
+ 
+ 	case 5:		/* RAW */
+ 		ret = raw_devread(buf, count);
+ 		break;
+ 
+ 	default:
+ 		ret = -ENODEV;
+   }
+ 
+   /* Update VFS file position. */
+   if (ret > 0) file->f_pos = ret;
+ 
+   return(ret);
+ }
+ #endif
+ 
+ 
+ 
+ static int
+ inet_fioctl(struct inode *inode, struct file *file,
+ 	 unsigned int cmd, unsigned long arg)
+ {
+   int minor, ret;
+ 
+   /* Extract the minor number on which we work. */
+   minor = MINOR(inode->i_rdev);
+ 
+   /* Now dispatch on the minor device. */
+   switch(minor) {
+ 	case 0:		/* INET */
+ 		ret = inet_ioctl(NULL, cmd, arg);
+ 		break;
+ 
+ 	case 1:		/* IP */
+ 		ret = ip_ioctl(NULL, cmd, arg);
+ 		break;
+ 
+ 	case 2:		/* ICMP */
+ 		ret = icmp_ioctl(NULL, cmd, arg);
+ 		break;
+ 
+ 	case 3:		/* TCP */
+ 		ret = tcp_ioctl(NULL, cmd, arg);
+ 		break;
+ 
+ 	case 4:		/* UDP */
+ 		ret = udp_ioctl(NULL, cmd, arg);
+ 		break;
+ 
+ 	case 5:		/* RAW */
+ 		ret = raw_ioctl(NULL, cmd, arg);
+ 		break;
+ 
+ 	default:
+ 		ret = -ENODEV;
+   }
+ 
+   return(ret);
+ }
+ 
+ 
+ static struct file_operations inet_fops = {
+   NULL,		/* LSEEK	*/
+ #if 0
+   inet_fread,	/* READ		*/
+ #else
+   NULL,		/* READ		*/
+ #endif
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   inet_fioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   NULL,		/* OPEN		*/
+   NULL		/* CLOSE	*/
+ };
+ 
+ 
+ static struct proto_ops inet_proto_ops = {
+   AF_INET,
+   inet_create,		inet_dup,		inet_release,
+   inet_bind,		inet_connect,		inet_socketpair,
+   inet_accept,		inet_getname,		inet_read,
+   inet_write,		inet_select,		inet_ioctl,
+   inet_listen,		inet_send,		inet_recv,
+   inet_sendto,		inet_recvfrom,		inet_shutdown,
+   inet_setsockopt,	inet_getsockopt,	inet_fcntl,
+ };
+ 
+ 
+ int
+ inet_proto_init(struct ddi *pro)
+ {
+   /* Set up our INET VFS major device. */
+   if (register_chrdev(pro->major, pro->name, &inet_fops) < 0) {
+ 	printk("%s: cannot register device %d !\n", pro->name, pro->major);
+ 	return(-ENODEV);
+   }
+ 
+   /* Tell NET that we are alive... */
+   if (net_af_reg(inet_proto_ops.family, &inet_proto_ops) != 0) {
+ 	printk("%s: cannot register address family %d !\n",
+ 					pro->name, inet_proto_ops.family);
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Tell ARP that we exist... */
+   if (arp_add_af(&inet_arp_type) != 0) {
+ 	printk("%s: cannot register family 0x%04X with ARP!\n",
+ 					pro->name, inet_arp_type.af);
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Tell ROUTE that we exist... */
+   if (route_add_af(&inet_route_type) != 0) {
+ 	printk("%s: cannot register family 0x%04X with ROUTE!\n",
+ 					pro->name, inet_route_type.af);
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Tell DEV that we wish to receive IP packets. */
+   net_add_packet(&ip_packet_type);
+ 
+   /*
+    * Add all the IP protocols.
+    * These calls initialize the individual protocols that live
+    * under the IP datagram protocol.  Add here for more, but
+    * please let us set up some form of central authority to do
+    * so... -FvK
+    */
+   raw_proto_init();		/* DARPA Internet Protocol (raw)	*/
+   icmp_proto_init();		/* DARPA Control Message Protocol	*/
+   tcp_proto_init();		/* DARPA Transmission Control Protocol	*/
+   udp_proto_init();		/* DARPA User Datagram Protocol		*/
+ 
+   return(0);
+ }
*** original/net/inet/sock.h	Thu Jan  1 01:00:00 1970
--- linux/net/inet/sock.h	Mon May  9 13:13:13 1994
***************
*** 0 ****
--- 1,210 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Definitions for the AF_INET socket handler.
+  *
+  * Version:	@(#)sock.h	1.1.13	04/25/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Corey Minyard <wf-rch!minyard@relay.EU.net>
+  *		Florian La Roche <flla@stud.uni-sb.de>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *		Pauline Middelink, <pauline@calvin.iaf.nl>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _INET_SOCK_H
+ #define _INET_SOCK_H
+ 
+ 
+ #define SOCK_ARRAY_SIZE		256
+ 
+ 
+ #define SK_RMEM_MAX		32768
+ #define SK_WMEM_MAX		32768
+ 
+ 
+ /*
+  * This structure really needs to be cleaned up.
+  * Most of it is for TCP, and not used by any of
+  * the other protocols.
+  */
+ struct sock {
+   struct options		*opt;
+   volatile unsigned long	wmem_alloc;
+   volatile unsigned long	rmem_alloc;
+   int				sndbuf;
+   int				rcvbuf;
+   unsigned long			send_seq;
+   unsigned long			acked_seq;
+   unsigned long			copied_seq;
+   unsigned long			rcv_ack_seq;
+   unsigned long			window_seq;
+   unsigned long			fin_seq;
+   unsigned long			syn_seq;  /* for finding duplicate SYNs */
+   /*
+    * Not all are volatile, but some are, so we
+    * might as well say they all are.
+    */
+   volatile char                 inuse,
+ 				dead,
+ 				urginline,
+ 				intr,
+ 				blog,
+ 				done,
+ 				reuse,
+ 				debug,
+ 				broadcast,
+ 				keepopen,
+ 				linger,
+ 				delay_acks,
+ 				dontroute,
+ 				destroy,
+ 				ack_timed,
+ 				no_check,
+ 				exp_growth;
+   int				proc;
+   struct sock			*next;
+   struct sock			*pair;
+ 
+   struct sk_buff		*volatile send_tail;
+   struct sk_buff		*volatile send_head;
+   struct sk_buff		*volatile back_log;
+   struct sk_buff		*send_tmp;
+   struct sk_buff		*volatile wback,
+ 				*volatile wfront,
+ 				*volatile rqueue;
+ 
+   struct proto			*prot;
+   int				type;
+   struct wait_queue		**sleep;
+   unsigned long			daddr;
+   unsigned long			saddr;
+   long				retransmits;
+   unsigned short		max_unacked;
+   unsigned short		window;
+   unsigned short		bytes_rcv;
+   unsigned short		num;
+   volatile unsigned short	cong_window;
+   volatile unsigned short	packets_out;
+   volatile unsigned short	urg;
+   volatile unsigned short	shutdown;
+   int				tos;
+   int				ttl;
+   unsigned short		mss;
+   volatile unsigned long	rtt;
+   volatile unsigned long	mdev;
+   volatile unsigned short	backoff;
+   volatile short		err;
+   unsigned char			protocol;
+   volatile unsigned char	state;
+   volatile unsigned char	ack_backlog;
+   unsigned char			max_ack_backlog;
+   unsigned char			priority;
+   struct tcphdr			dummy_th;
+   struct socket			*socket;
+ 
+   int				timeout;	/* What are we waiting for? */
+   struct timer_list		timer;
+   volatile long			intcnt;		/* increment every interrupt */
+   unsigned long			lingertime;
+ };
+ 
+ struct proto {
+   void			*(*wmalloc)(struct sock *sk,
+ 				    unsigned long size, int force,
+ 				    int priority);
+   void			*(*rmalloc)(struct sock *sk,
+ 				    unsigned long size, int force,
+ 				    int priority);
+   void			(*wfree)(struct sock *sk, void *mem,
+ 				 unsigned long size);
+   void			(*rfree)(struct sock *sk, void *mem,
+ 				 unsigned long size);
+   unsigned long		(*rspace)(struct sock *sk);
+   unsigned long		(*wspace)(struct sock *sk);
+   void			(*close)(struct sock *sk, int timeout);
+   int			(*read)(struct sock *sk, unsigned char *to,
+ 				int len, int nonblock, unsigned flags);
+   int			(*write)(struct sock *sk, unsigned char *to,
+ 				 int len, int nonblock, unsigned flags);
+   int			(*sendto)(struct sock *sk,
+ 				  unsigned char *from, int len, int noblock,
+ 				  unsigned flags, struct sockaddr_in *usin,
+ 				  int addr_len);
+   int			(*recvfrom)(struct sock *sk,
+ 				    unsigned char *from, int len, int noblock,
+ 				    unsigned flags, struct sockaddr_in *usin,
+ 				    int *addr_len);
+   int			(*connect)(struct sock *sk,
+ 				  struct sockaddr_in *usin, int addr_len);
+   struct sock		*(*accept) (struct sock *sk, int flags);
+   void			(*write_wakeup)(struct sock *sk);
+   void			(*read_wakeup)(struct sock *sk);
+   int			(*recv)(struct sk_buff *buff, struct ifnet *dev,
+ 			       struct options *opt, unsigned long daddr,
+ 			       unsigned short len, unsigned long saddr,
+ 			       int redo, struct ipprot *protocol);
+   int			(*select)(struct sock *sk, int which,
+ 				  select_table *wait);
+   int			(*ioctl)(struct sock *sk, int cmd,
+ 				 unsigned long arg);
+   int			(*init)(struct sock *sk);
+   void			(*shutdown)(struct sock *sk, int how);
+   void			(*retransmit)(struct sock *sk, int all);
+   int			(*setsockopt)(struct sock *sk, int level, int optname,
+ 				      char *optval, int optlen);
+   int			(*getsockopt)(struct sock *sk, int level, int optname,
+ 				      char *optval, int *option);  	 
+   unsigned short	max_header;
+   unsigned long		retransmits;
+   struct sock		*sock_array[SOCK_ARRAY_SIZE];
+   char			name[80];
+ };
+ 
+ #define TIME_WRITE	1
+ #define TIME_CLOSE	2
+ #define TIME_KEEPOPEN	3
+ #define TIME_DESTROY	4
+ #define TIME_DONE	5	/* used to absorb those last few packets */
+ #define SOCK_DESTROY_TIME 1000	/* about 10 seconds			*/
+ 
+ 
+ #define PROT_SOCK	1024
+ #define SHUTDOWN_MASK	0x03
+ #define RCV_SHUTDOWN	0x01
+ #define SEND_SHUTDOWN	0x02
+ 
+ 
+ extern void			print_sk(struct sock *);
+ extern void			destroy_sock(struct sock *sk);
+ extern unsigned short		get_new_socknum(struct proto *, unsigned short);
+ extern void			put_sock(unsigned short, struct sock *); 
+ extern void			release_sock(struct sock *sk);
+ extern struct sock		*get_sock(struct proto *, unsigned short,
+ 					  unsigned long, unsigned short,
+ 					  unsigned long);
+ extern void			*sock_wmalloc(struct sock *sk,
+ 					      unsigned long size, int force,
+ 					      int priority);
+ extern void			*sock_rmalloc(struct sock *sk,
+ 					      unsigned long size, int force,
+ 					      int priority);
+ extern void			sock_wfree(struct sock *sk, void *mem,
+ 					   unsigned long size);
+ extern void			sock_rfree(struct sock *sk, void *mem,
+ 					   unsigned long size);
+ extern unsigned long		sock_rspace(struct sock *sk);
+ extern unsigned long		sock_wspace(struct sock *sk);
+ 
+ 
+ #endif	/* _INET_SOCK_H */
*** original/net/inet/tcp.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/tcp.c	Mon May  9 13:13:13 1994
***************
*** 0 ****
--- 1,3432 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Implementation of the Transmission Control Protocol
+  *		(TCP) as per RFC-793.
+  *
+  * Version:	@(#)tcp.c	1.1.48	04/25/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Mark Evans, <evansmp@uhura.aston.ac.uk>
+  *		Corey Minyard <wf-rch!minyard@relay.EU.net>
+  *		Florian La Roche, <flla@stud.uni-sb.de>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "sock.h"
+ 
+ 
+ #define SEQ_TICK	(250000 / HZ)
+ 
+ 
+ #define SKBTCP(x)	((struct tcphdr *) ((x)->raw))
+ #define SKB_ENDSEQ(x)	\
+ 	(ntohl(SKBTCP(x)->seq) + (x)->len + SKBTCP(x)->syn + SKBTCP(x)->fin)
+ 
+ 
+ static unsigned long	seq_offset;
+ static int		tcp_delay_acks = TCP_DELAY_ACK;
+   
+ 
+ static void tcp_options(struct sock *sk, struct tcphdr *th);
+ 
+ 
+ static __inline__ int 
+ min(unsigned int a, unsigned int b)
+ {
+   if (a < b) return(a);
+   return(b);
+ }
+ 
+ 
+ static void
+ print_th(struct tcphdr *th)
+ {
+   unsigned char *ptr;
+ 
+   if ((inet_debug & (1 << DBG_TCP)) == 0) return;
+ 
+   printk("TCP: ");
+   ptr = (unsigned char *)(th + 1);
+   printk("source=%d dest=%d seq=%d, ack_seq=%d\n",
+ 	ntohs(th->source), ntohs(th->dest),
+ 	ntohl(th->seq), ntohl(th->ack_seq));
+   printk("     FIN=%d SYN=%d RST=%d PSH=%d ACK=%d URG=%d RES1=%d RES2=%d\n",
+ 	th->fin, th->syn, th->rst, th->psh, th->ack,
+ 	th->urg, th->res1, th->res2);
+   printk("     window=%d check=%d urg_ptr=%d\n",
+ 	ntohs(th->window), ntohs(th->check), ntohs(th->urg_ptr));
+   printk("     doff=%d options= %d %d %d %d\n",
+ 		th->doff, ptr[0], ptr[1], ptr[2], ptr[3]);
+  }
+ 
+ 
+ /* This routine grabs the first thing off of a rcv queue. */
+ static struct sk_buff *
+ get_firstr(struct sock *sk)
+ {
+   struct sk_buff *skb;
+ 
+   skb = sk->rqueue;
+   if (skb == NULL) return(NULL);
+   sk->rqueue = skb->qnext;
+   if (sk->rqueue == skb) {
+ 	sk->rqueue = NULL;
+   } else {
+ 	sk->rqueue->qprev = skb->qprev;
+ 	sk->rqueue->qprev->qnext = sk->rqueue;
+   }
+   return(skb);
+ }
+ 
+ 
+ /*
+  * Diff must return the relative difference between two sequence
+  * numbers, taking into account sequence space wrapping.  It is
+  * just a simple two's complement subtraction cast to a long!
+  */
+ static long
+ diff(unsigned long seq1, unsigned long seq2)
+ {
+   long d;
+ 
+   d = (seq1 - seq2);
+   return(d);
+ }
+ 
+ 
+ /* Enter the time wait state. */
+ static void
+ tcp_time_wait(struct sock *sk)
+ {
+   sk->state = TCP_TIME_WAIT;
+   sk->shutdown = SHUTDOWN_MASK;
+   if (! sk->dead) wake_up_interruptible(sk->sleep);
+   inet_reset_timer(sk, TIME_DONE, TCP_TIMEWAIT_LEN);
+ }
+ 
+ 
+ static void
+ tcp_retransmit(struct sock *sk, int all)
+ {
+   if (all) {
+ 	ip_retransmit(sk, all);
+ 	return;
+   }
+ 
+   if (sk->cong_window > 4)
+ 	sk->cong_window = (sk->cong_window / 2);
+   sk->exp_growth = 0;
+ 
+   /* Do the actuall retransmit. */
+   ip_retransmit(sk, all);
+ }
+ 
+ 
+ /*
+  * This routine must find a socket given a TCP header.
+  * Everything is assumed to be in net order.
+  * The rules are a little different for new connection requests:
+  * the socket doesn't need to be bound & connected, and we can ignore
+  * sockets in the TCP_TIME_WAIT state if we make sure that no packets
+  * of the old connection can be erroneously used for the new one.
+  * Initially *syn_seq is the sequence of the SYN, it is set to the
+  * sequence to use for sending our SYN.
+  */
+ static struct sock *
+ tcp_get_sock(unsigned short num, unsigned long raddr,
+ 	     unsigned short rnum, unsigned long laddr, int syn, int *syn_seq)
+ {
+   struct sock *s;
+   unsigned short hnum;
+   int new_syn_seq;
+ 
+   new_syn_seq = 0;
+   if (syn && syn_seq) {
+ 	new_syn_seq = *syn_seq;
+ 	*syn_seq = jiffies * SEQ_TICK - seq_offset;
+   }
+ 
+   hnum = ntohs(num);
+   DPRINTF((DBG_TCP, "TCP: get_sock: num=%d raddr=%s rnum=%d, syn=%d, syn_seq=%d, def_seq=%d",
+ 		hnum, in_ntoa(raddr), htons(rnum), syn, new_syn_seq, *syn_seq));
+   DPRINTF((DBG_TCP, " laddr=%s\n", in_ntoa(laddr)));
+ 
+   /*
+    * SOCK_ARRAY_SIZE must be a power of two.  This will work better
+    * than a prime unless 3 or more sockets end up using the same
+    * array entry.  This should not be a problem because most well
+    * known sockets don't overlap that much, and for the other ones,
+    * we can just be careful about picking our socket number when we
+    * choose an arbitrary one.
+    */
+   for(s = tcp_prot.sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
+       s != NULL; s = s->next) {
+ 	if (s->num != hnum) continue;
+ 	if (s->dead && (s->state == TCP_CLOSE)) continue;
+ 	if (s->dummy_th.dest != rnum && (s->dummy_th.dest != 0 || !syn))
+ 		continue;
+ 
+ 	/* We need to see if this is the TCP socket that we want. */
+ 	/* An unconnected socket can only receive SYNs */
+ 	if ((s->daddr != raddr) && (!syn || s->daddr != INADDR_ANY))
+ 		continue;
+ 
+ 	if ((s->saddr != laddr) && (!syn || s->saddr != INADDR_ANY))
+ 		continue;
+ 
+ 	/* Special case: If we get a SYN, we can ignore a socket in the
+ 	 * TCP_TIME_WAIT state, but we have to check for a duplicate of
+ 	 * the old SYN and have to pass back a new sequence number greater
+ 	 * that the last sequence number of the old connection.
+ 	 * We don't have to kill the old socket, as the new one will come
+ 	 * first in the linked list
+ 	 */
+ 	if (syn && s->state == TCP_TIME_WAIT) {
+ 		if (!syn_seq)
+ 			return s;
+ 		if (new_syn_seq == s->syn_seq)
+ 			return s;	/* retransmitted syn */
+ 		if (((int) (*syn_seq - s->send_seq - 1024)) < 0)
+ 			*syn_seq = s->send_seq + 1024;
+ 		DPRINTF((DBG_TCP, "...ignoring socket %08X, setting syn_seq=%d\n",
+ 			s, *syn_seq));
+ 		continue;
+ 	}
+ 
+ 	return(s);
+   }
+   return(NULL);
+ }
+ 
+ 
+ 
+ /*
+  * This routine is called by the ICMP module when it gets some
+  * sort of error condition.  If err < 0 then the socket should
+  * be closed and the error returned to the user.  If err > 0
+  * it's just the icmp type << 8 | icmp code.  After adjustment
+  * header points to the first 8 bytes of the tcp header.  We need
+  * to find the appropriate port.
+  */
+ static void
+ tcp_err(int type, int code, int err, int fatal,
+ 	struct ip *iph, struct ipprot *protocol)
+ {
+   struct sock *sk;
+   struct tcphdr *th;
+   unsigned char *ptr;
+ 
+   ptr = (unsigned char *) iph;
+   th = (struct tcphdr *) (ptr + (iph->ip_hl * sizeof(unsigned long)));
+ 
+   DPRINTF((DBG_TCP,
+ 	"TCP: err: type=%d code=%d err=%d fat=%d iph=0x%X\n        saddr=%s:%d",
+ 	type, code, err, fatal, iph, in_ntoa(iph->ip_src.s_addr),
+ 	ntohs(th->source)));
+   DPRINTF((DBG_TCP, " daddr=%s:%d protocol=%s\n",
+ 	in_ntoa(iph->ip_dst.s_addr), ntohs(th->dest), protocol->name));
+ #ifdef INET_DEBUG
+   DPRINTF((DBG_TCP, "Header from ICMP:\n"));
+   print_th(th);
+ #endif
+ 
+   sk = tcp_get_sock(th->source, iph->ip_dst.s_addr,
+ 		    th->dest, iph->ip_src.s_addr, 0, NULL);
+   if (sk == NULL) return;
+ 
+   /* How can this be negative?  Alan?? */
+   if (type < 0) {
+ 	sk->err = -type;
+ 	wake_up_interruptible(sk->sleep);
+ 	return;
+   }
+ 
+   if (type == ICMP_SOURCEQUENCH) {
+ 	/*
+ 	 * FIXME:
+ 	 * For now we will just trigger a linear backoff.
+ 	 * The slow start code should cause a real backoff here.
+ 	 */
+ 	if (sk->cong_window > 4) sk->cong_window--;
+ 	return;
+   }
+   sk->err = err;
+   DPRINTF((DBG_TCP, "TCP: err: error %d (%d) for sk=0x%X %s:%u\n",
+ 		err, fatal, sk, in_ntoa(sk->daddr), ntohs(sk->dummy_th.dest)));
+ 
+   /*
+    * If we've already connected we will keep trying
+    * until we time out, or the user gives up.
+    */
+   if (fatal) {
+ 	if (sk->state == TCP_SYN_SENT) {
+ 		sk->state = TCP_CLOSE;
+ 		wake_up_interruptible(sk->sleep);
+ 	}
+   }
+ }
+ 
+ 
+ static int
+ tcp_readable(struct sock *sk)
+ {
+   unsigned long counted;
+   unsigned long amount;
+   struct sk_buff *skb;
+   int sum;
+ 
+   DPRINTF((DBG_TCP, "TCP: readable: sk=0x%X\n", sk));
+ 
+   if (sk == NULL || sk->rqueue == NULL) return(0);
+ 
+   counted = sk->copied_seq + 1;
+   amount = 0;
+   skb = sk->rqueue->qnext;
+ 
+   /* Do until a push or until we are out of data. */
+   do {
+ 	if (before(counted, ntohl(SKBTCP(skb)->seq))) break;
+ 	sum = skb->len -(counted - ntohl(SKBTCP(skb)->seq));
+ 	if (SKBTCP(skb)->syn) sum++;
+ 	if (SKBTCP(skb)->urg) {
+ 		sum -= ntohs(SKBTCP(skb)->urg_ptr);
+ 	}
+ 	if (sum >= 0) {
+ 		amount += sum;
+ 		if (SKBTCP(skb)->syn) amount--;
+ 		counted += sum;
+ 	}
+ 	if (amount && SKBTCP(skb)->psh) break;
+ 	skb = skb->qnext;
+   } while(skb != sk->rqueue->qnext);
+   DPRINTF((DBG_TCP, "TCP: readable: returning %d bytes\n", amount));
+   return(amount);
+ }
+ 
+ 
+ static int
+ tcp_select(struct sock *sk, int sel_type, select_table *wait)
+ {
+   DPRINTF((DBG_TCP, "TCP: select: sk=0x%X sel_type=%d wait=0x%X\n",
+ 	  					sk, sel_type, wait));
+   sk->inuse = 1;
+   switch(sel_type) {
+ 	case SEL_IN:
+ 		select_wait(sk->sleep, wait);
+ 		if (sk->rqueue != NULL) {
+ 			if (sk->state == TCP_LISTEN || tcp_readable(sk)) {
+ 				release_sock(sk);
+ 				return(1);
+ 			}
+ 		}
+ 		if (sk->err != 0) {	/* receiver error */
+ 			release_sock(sk);
+ 			return(1);
+ 		}
+ 		if (sk->shutdown & RCV_SHUTDOWN) {
+ 			release_sock(sk);
+ 			return(1);
+ 		} else {
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 	case SEL_OUT:
+ 		select_wait(sk->sleep, wait);
+ 		if (sk->shutdown & SEND_SHUTDOWN) {
+ 			DPRINTF((DBG_TCP,
+ 				"write select on shutdown socket.\n"));
+ 
+ 			/* FIXME: should this return an error? */
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		/*
+ 		 * FIXME:
+ 		 * Hack so it will probably be able to write
+ 		 * something if it says it's ok to write.
+ 		 */
+ 		if (sk->prot->wspace(sk) >= sk->mss) {
+ 			release_sock(sk);
+ 			/* This should cause connect to work OK. */
+ 			if (sk->state == TCP_SYN_RECV ||
+ 			    sk->state == TCP_SYN_SENT) return(0);
+ 			return(1);
+ 		}
+ 		DPRINTF((DBG_TCP,
+ 			"TCP: select: sleeping on write sk->wmem_alloc=%d, "
+ 			"sk->packets_out=%d\n"
+ 			"sk->wback=0x%X sk->wfront=0x%X\n"
+ 			"sk->send_seq=%u sk->window_seq=%u\n", 
+ 				sk->wmem_alloc, sk->packets_out,
+ 				sk->wback, sk->wfront,
+ 				sk->send_seq, sk->window_seq));
+ 
+ 		release_sock(sk);
+ 		return(0);
+ 
+ 	case SEL_EX:
+ 		select_wait(sk->sleep,wait);
+ 		if (sk->err) {
+ 			release_sock(sk);
+ 			return(1);
+ 		}
+ 		release_sock(sk);
+ 		return(0);
+   }
+ 
+   release_sock(sk);
+   return(0);
+ }
+ 
+ 
+ int
+ tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+ {
+   struct sk_buff *skb;
+   unsigned long amount;
+   int err, answ = 0;
+ 
+   DPRINTF((DBG_TCP, "TCP: ioctl: sk=0x%X cmd=%d arg=0x%X\n", sk, cmd, arg));
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		return(inet_dbg_ioctl(arg, DBG_TCP));
+ 
+ 	case TIOCINQ:	/* == FIONREAD */
+ 		if (sk->state == TCP_LISTEN) return(-EINVAL);
+ 
+ 		amount = 0;
+ 		sk->inuse = 1;
+ 		if (sk->rqueue != NULL) {
+ 			amount = tcp_readable(sk);
+ 		}
+ 		release_sock(sk);
+ 		DPRINTF((DBG_TCP, "TCP: ioctl: returning %d\n", amount));
+ 		err = verify_area(VERIFY_WRITE, (int *) arg,
+ 				  sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		put_fs_long(amount, (int *) arg);
+ 		return(0);
+ 
+ 	case SIOCATMARK:
+ 		/*
+ 		 * Try to figure out if we need to read
+ 		 * some urgent data.
+ 		 */
+ 		sk->inuse = 1;
+ 		if (sk->rqueue != NULL) {
+ 			skb = sk->rqueue->qnext;
+ 			if (sk->copied_seq+1 == ntohl(SKBTCP(skb)->seq) &&
+ 				SKBTCP(skb)->urg) answ = 1;
+ 		}
+ 		release_sock(sk);
+ 		err = verify_area(VERIFY_WRITE, (int *) arg,
+ 				  sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		put_fs_long(answ, (int *) arg);
+ 		return(0);
+ 
+ 	case TIOCOUTQ:
+ 		if (sk->state == TCP_LISTEN) return(-EINVAL);
+ 		amount = sk->prot->wspace(sk)/2;
+ 		err = verify_area(VERIFY_WRITE, (int *) arg,
+ 				  sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		put_fs_long(amount, (int *) arg);
+ 		return(0);
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+ }
+ 
+ 
+ /* This routine computes a TCP checksum. */
+ static unsigned short
+ tcp_check(struct tcphdr *th, int len,
+ 	  unsigned long saddr, unsigned long daddr)
+ {     
+   unsigned long sum = 0;
+    
+   if (saddr == INADDR_ANY) {
+ 	printk("TCP: Huh?  TCP Checksum, but no IP Source Address ??\n");
+ 	return(0);
+   }
+ #ifdef INET_DEBUG
+   print_th(th);
+ #endif
+   __asm__("\t addl %%ecx,%%ebx\n"
+ 	  "\t adcl %%edx,%%ebx\n"
+ 	  "\t adcl $0, %%ebx\n"
+ 	  : "=b"(sum)
+ 	  : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_TCP*256)
+ 	  : "cx","bx","dx" );
+    
+   if (len > 3) {
+ 	__asm__("\tclc\n"
+ 		"1:\n"
+ 		"\t lodsl\n"
+ 		"\t adcl %%eax, %%ebx\n"
+ 		"\t loop 1b\n"
+ 		"\t adcl $0, %%ebx\n"
+ 		: "=b"(sum) , "=S"(th)
+ 		: "0"(sum), "c"(len/4) ,"1"(th)
+ 		: "ax", "cx", "bx", "si" );
+   }
+    
+   /* Convert from 32 bits to 16 bits. */
+   __asm__("\t movl %%ebx, %%ecx\n"
+ 	  "\t shrl $16,%%ecx\n"
+ 	  "\t addw %%cx, %%bx\n"
+ 	  "\t adcw $0, %%bx\n"
+ 	  : "=b"(sum)
+ 	  : "0"(sum)
+ 	  : "bx", "cx");
+    
+   /* Check for an extra word. */
+   if ((len & 2) != 0) {
+ 	__asm__("\t lodsw\n"
+ 		"\t addw %%ax,%%bx\n"
+ 		"\t adcw $0, %%bx\n"
+ 		: "=b"(sum), "=S"(th)
+ 		: "0"(sum) ,"1"(th)
+ 		: "si", "ax", "bx");
+   }
+    
+   /* Now check for the extra byte. */
+   if ((len & 1) != 0) {
+ 	__asm__("\t lodsb\n"
+ 		"\t movb $0,%%ah\n"
+ 		"\t addw %%ax,%%bx\n"
+ 		"\t adcw $0, %%bx\n"
+ 		: "=b"(sum)
+ 		: "0"(sum) ,"S"(th)
+ 		: "si", "ax", "bx");
+   }
+    
+   /* We only want the bottom 16 bits, but we never cleared the top 16. */
+   return((~sum) & 0xFFFF);
+ }
+ 
+ 
+ static void
+ tcp_send_check(struct tcphdr *th, unsigned long saddr, 
+ 	       unsigned long daddr, int len, struct sock *sk)
+ {
+   th->check = 0;
+   th->check = tcp_check(th, len, saddr, daddr);
+   return;
+ }
+ 
+ 
+ static void
+ tcp_send_partial(struct sock *sk)
+ {
+   struct sk_buff *skb;
+   
+   if (sk == NULL || sk->send_tmp == NULL) return;
+   
+   skb = sk->send_tmp;
+ 
+   /* We need to complete and send the packet. */
+   tcp_send_check(SKBTCP(skb), sk->saddr, sk->daddr,
+ 		 skb->len - (unsigned long) SKBTCP(skb) +
+ 			    (unsigned long) (skb+1), sk);
+   
+   skb->seq = sk->send_seq;
+   if (after(sk->send_seq , sk->window_seq) ||
+       sk->packets_out >= sk->cong_window) {
+ 	DPRINTF((DBG_TCP, "sk->cong_window=%d, sk->packets_out=%d\n",
+ 					sk->cong_window, sk->packets_out));
+ 	DPRINTF((DBG_TCP, "sk->send_seq=%d, sk->window_seq=%d\n",
+ 					sk->send_seq, sk->window_seq));
+ 	skb->qnext = NULL;
+ 	if (sk->wback == NULL) {
+ 		sk->wfront = skb;
+ 	} else {
+ 		sk->wback->qnext = skb;
+ 	}
+ 	sk->wback = skb;
+   } else {
+ 	ip_queue_xmit(sk, skb->dev, skb, 0);
+   }
+   sk->send_tmp = NULL;
+ }
+ 
+ 
+ /* This routine sends an ACK and also updates the window. */
+ static void
+ tcp_send_ack(unsigned long sequence, unsigned long ack, struct sock *sk,
+ 	     struct tcphdr *th, unsigned long daddr)
+ {
+   struct sk_buff *buff;
+   struct tcphdr *t1;
+   struct ifnet *dev = NULL;
+   int tmp;
+ 
+   /*
+    * We need to grab some memory, and put together an ACK,
+    * and then put it into the queue to be sent.
+    */
+   buff = (struct sk_buff *) sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
+   if (buff == NULL) {
+ 	/* Force it to send an ACK. */
+ 	sk->ack_backlog++;
+ 	if ((sk->timeout != TIME_WRITE) && tcp_connected(sk->state)) {
+ 		inet_reset_timer(sk, TIME_WRITE, 10);
+ 	}
+ 	return;
+   }
+   buff->len = sizeof(struct tcphdr);
+   buff->sk = sk;
+   t1 = (struct tcphdr *)(buff + 1);
+ 
+   /* Put in the IP header and routing stuff. */
+   tmp = ip_build_header(buff, sk->saddr, daddr, &dev, IPPROTO_TCP,
+ 			sk->opt, sk->tos, sk->ttl, sk->dontroute);
+   if (tmp < 0) {
+ 	sk->prot->wfree(sk, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	return;
+   }
+   buff->len += tmp;
+   t1 = (struct tcphdr *) ((char *)t1 + tmp);
+ 
+ #if 1	/* FIXME: */
+   memcpy(t1, th, sizeof(*t1)); /* this should probably be removed */
+ #endif
+ 
+   /* Fill in the TCP header; swapping the SOURCE and DEST ports. */
+   t1->dest = th->source;
+   t1->source = th->dest;
+   t1->seq = htonl(sequence);
+   t1->ack = 1;
+   sk->window = sk->prot->rspace(sk);
+   t1->window = htons(sk->window);
+   t1->res1 = 0;
+   t1->res2 = 0;
+   t1->rst = 0;
+   t1->urg = 0;
+   t1->syn = 0;
+   t1->psh = 0;
+   t1->fin = 0;
+   if (ack == sk->acked_seq) {
+ 	sk->ack_backlog = 0;
+ 	sk->bytes_rcv = 0;
+ 	sk->ack_timed = 0;
+ 	if (sk->send_head == NULL &&
+ 	    sk->wfront == NULL &&
+ 	    sk->timeout == TIME_WRITE) {
+ 		if (sk->keepopen)
+ 			inet_reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
+ 		  else
+ 			inet_delete_timer(sk);
+ 	}
+   }
+   t1->ack_seq = htonl(ack);
+   t1->doff = sizeof(*t1) / sizeof(unsigned long);
+   tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
+   ip_queue_xmit(sk, dev, buff, 1);
+ }
+ 
+ 
+ /* This routine builds a generic TCP header. */
+ static int
+ tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
+ {
+   int win;
+ 
+   /* FIXME: want to get rid of this. */
+   memcpy(th, (void *) &(sk->dummy_th), sizeof(struct tcphdr));
+   th->seq = htonl(sk->send_seq);
+   th->psh = (push == 0) ? 1 : 0;
+   th->doff = sizeof(struct tcphdr) / sizeof(unsigned long);
+   th->ack = 1;
+   th->fin = 0;
+   sk->ack_backlog = 0;
+   sk->bytes_rcv = 0;
+   sk->ack_timed = 0;
+   th->ack_seq = htonl(sk->acked_seq);
+   win = sk->prot->rspace(sk);
+   if (win > sk->window) sk->window = win;
+   th->window = htons(sk->window);
+ 
+   return(sizeof(struct tcphdr));
+ }
+ 
+ 
+ /*
+  * This routine copies from a user buffer into a socket,
+  * and starts the transmit system.
+  */
+ static int
+ tcp_write(struct sock *sk, unsigned char *from,
+ 	  int len, int nonblock, unsigned flags)
+ {
+   struct ifnet *dev = NULL;
+   struct sk_buff *skb;
+   struct proto *prot;
+   unsigned char *buff;
+   int copied = 0;
+   int copy, tmp;
+   int copymax;
+ 
+   DPRINTF((DBG_TCP,
+ 	"TCP: write: sk=0x%X from=0x%X len=%d nonblock=%d flags=0x%04X\n",
+ 					sk, from, len, nonblock, flags));
+   prot = sk->prot;
+   while(len > 0) {
+ 	long intcnt = sk->intcnt;
+ 
+ 	DPRINTF((DBG_TCP, "TCP: write: topofloop %d left err=%d\n",
+ 							len, sk->err));
+ 	/* Handle case if error. */
+ 	if (sk->err) {
+ 		if (copied) return(copied);
+ 		tmp = -sk->err;
+ 		sk->err = 0;
+ 		return(tmp);
+ 	}
+ 	sk->inuse = 1;		/* prevent other users of socket */
+ 
+ 	/* Handle case if write is shutdown. */
+ 	if (sk->shutdown & SEND_SHUTDOWN) {
+ 		DPRINTF((DBG_TCP, "TCP: write: shutdown\n"));
+ 		release_sock(sk);
+ 		sk->err = EPIPE;
+ 		if (copied) return(copied);
+ 		sk->err = 0;
+ 		return(-EPIPE);
+ 	}
+ 
+ 	/* Handle case if not established. */
+ 	if (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) {
+ 		DPRINTF((DBG_TCP, "TCP: write: not established\n"));
+ 
+ 		release_sock(sk);
+ 		cli();
+ 		if (intcnt == sk->intcnt) {
+ 			if (sk->state != TCP_SYN_SENT && 
+ 			    sk->state != TCP_SYN_RECV) {
+ 				sti();
+ 				if (copied) return(copied);
+ 				if (sk->keepopen) {
+ 					send_sig(SIGPIPE, current, 0);
+ 				}
+ 				return(-EPIPE);
+ 			} else if (nonblock || copied) {
+ 				sti();
+ 				if (copied) return(copied);
+ 				return(-EAGAIN);
+ 			} else {
+ 				interruptible_sleep_on(sk->sleep);
+ 				if (current->signal & ~current->blocked) {
+ 					sti();
+ 					if (copied) return(copied);
+ 					return(-ERESTARTSYS);
+ 				}
+ 			}
+ 		}
+ 		sti();
+ 		sk->inuse = 1;
+ 		continue;
+ 	}
+ 
+ 	/*
+ 	 * Calculate maximum number of bytes we can copy this round, include
+ 	 * information from any partial packet.  Note that send_seq does
+ 	 * take into account data that may already exist in our partial packet.
+ 	 *
+ 	 * copy - maximum number of bytes we can copy
+ 	 * copymax - maximum number of bytes we can copy not taking into
+ 	 *           account 'len'.
+ 	 */
+ 	skb = sk->send_tmp;
+ 
+ 	{
+ 		int wsize;
+ 
+ 		wsize = diff(sk->window_seq, sk->send_seq);
+ 
+ 		copymax = sk->mss;
+ 		if (skb) {
+ 			int skb_room_remaining =
+ 				skb->mem_len - skb->len - sizeof(*skb);
+ 			copymax = min(copymax, skb_room_remaining);
+ 		}
+ 		copymax = min(copymax, wsize);
+ 		if (copymax < 0) copymax = 0;
+ 		copy = min(copymax, len);
+ 	}
+ 
+ 	/*
+ 	 * Attempt to append to any half-built packet.  Note: if sk->mss has
+ 	 * changed or if the other end permanently reduces its window, we
+ 	 * can get into trouble.
+ 	 */
+ 	DPRINTF((DBG_TCP, "TCP: write: send_tmp %08lx\n", sk->send_tmp));
+ 	if (skb != NULL) {
+ 		if (! (flags & MSG_OOB)) {
+ 			DPRINTF((DBG_TCP,
+ 				"TCP: write: send_tmp copy %d (%d-%d-%d,%d)",
+ 					copy, skb->mem_len, skb->len,
+ 					sizeof(*skb), len));
+ 	  
+ 			memcpy_fromfs((unsigned char *) (skb + 1) + skb->len,
+ 				      from, copy);
+ 			skb->len += copy;
+ 			from += copy;
+ 			copied += copy;
+ 			len -= copy;
+ 			sk->send_seq += copy;
+ 		}
+ 
+ 		if ((flags & MSG_OOB) || copy == copymax) {
+ 			tcp_send_partial(sk);
+ 		}
+ 		continue;
+ 	}
+ 
+ 	DPRINTF((DBG_TCP, "TCP: write: copy %d/%d\n", copy, copymax));
+ 
+ 	/*
+ 	 * allocate a new packet.  If we intend to send the packet immediately
+ 	 * we only allocate enough space to hold the data we intend to send,
+ 	 * otherwise we allocate a maximal packet (regardless of the window
+ 	 * space remaining, because the space might increase as we receive
+ 	 * acknowledgements).
+ 	 *
+ 	 * A new partial packet is constructed if the data is not out of band
+ 	 * and there are already packets out there.
+ 	 *
+ 	 * We release the socket in case wmalloc() sleeps, then relock it XXX
+ 	 *
+ 	 * if copymax is 0, we have run out of window space and must sleep 
+ 	 * (skb is already NULL due to the conditional in the previous block)
+ 	 */
+         if (copymax) {
+ 		int bytes = sk->mss + HEADER_SIZE;
+ 		short istmp = 1;
+ 
+ 		if (copy == copymax || sk->packets_out == 0 || (flags & MSG_OOB)) {
+ 			bytes = copy + HEADER_SIZE;
+ 			istmp = 0;
+ 		}
+ 
+ 		release_sock(sk);
+ 		skb = (struct sk_buff *)prot->wmalloc(sk, bytes, 0, GFP_KERNEL);
+ 		sk->inuse = 1;
+ 		if (istmp) sk->send_tmp = skb;
+ 	}
+ 
+ 	/*
+ 	 * This code fragment did not seem to have any purpose, please check
+ 	 * then remove.  This fragment would occur if istmp == 0 (i.e. we
+ 	 * intend to send the packet
+ 	 *
+ 	 *  else if (skb)
+ 	 *	skb->len = copy + prot->max_header;
+ 	 */
+ 
+ 	/*
+ 	 * If we failed to get memory or our transmission
+ 	 * window has been exhausted, we sleep.
+ 	 */
+ 	if (skb == NULL) {
+ 		if (nonblock) {
+ 			release_sock(sk);
+ 			DPRINTF((DBG_TCP, "tcp_write: return %d\n", copied));
+ 			if (copied) return(copied);
+ 			return(-EAGAIN);
+ 		}
+ 
+ 		/*
+ 		 * There is a possible race condition here if an interrupt
+ 		 * releases some space, so we must recheck the sleep
+ 		 * conditions.
+ 		 */
+ 		release_sock(sk);
+ 		cli();
+ 		if (sk->intcnt == intcnt) {
+ 			interruptible_sleep_on(sk->sleep);
+ 			if (current->signal & ~current->blocked) {
+ 				sti();
+ 				DPRINTF((DBG_TCP, "tcp_write: return 5\n"));
+ 				if (copied) return(copied);
+ 				return(-ERESTARTSYS);
+ 			}
+ 			sk->inuse = 1;
+ 		}
+ 		sti();
+ 		continue;
+ 	}
+ 
+ 	/* Allocation succeeded!  Build up the packet. */
+ 	skb->sk = sk;
+ 	skb->free = 0;
+ 	buff = (unsigned char *) (skb+1);
+ 
+ 	/* Create the IP+MAC header. */
+ 	tmp = ip_build_header(skb, sk->saddr, sk->daddr, &dev,
+ 			      IPPROTO_TCP, sk->opt, sk->tos,
+ 			      sk->ttl, sk->dontroute);
+ 	if (tmp < 0 ) {
+ 	        if (sk->send_tmp == skb) sk->send_tmp = NULL;
+ 		prot->wfree(sk, (unsigned char *) skb->mem_addr, skb->mem_len);
+ 		release_sock(sk);
+ 		DPRINTF((DBG_TCP, "TCP: write: unable to create IP header!\n"));
+ 		if (copied) return(copied);
+ 		return(tmp);
+ 	}
+ 	skb->len = tmp;
+ 	skb->dev = dev;
+ 	buff += tmp;
+ 
+ 	/* Create the TCP header. */
+ 	SKBTCP(skb) = (struct tcphdr *) buff;
+ 	tmp = tcp_build_header((struct tcphdr *)buff, sk, (len - copy));
+ 	if (tmp < 0) {
+ 	        if (sk->send_tmp == skb) sk->send_tmp = NULL;
+ 		prot->wfree(sk, (unsigned char *) skb->mem_addr, skb->mem_len);
+ 		release_sock(sk);
+ 		DPRINTF((DBG_TCP, "TCP: write: unable to create TCP header!\n"));
+ 		if (copied) return(copied);
+ 		return(tmp);
+ 	}
+ 
+ 	/* Set the URGent (OOB) flag if needed. */
+ 	if (flags & MSG_OOB) {
+ 		((struct tcphdr *)buff)->urg = 1;
+ 		((struct tcphdr *)buff)->urg_ptr = htons(copy);
+ 	}
+ 
+ 	DPRINTF((DBG_TCP, "TCP: write: update tmp=%d\n", tmp));
+ 	/* Copy the data into the TCP segment. */
+ 	skb->len += tmp;
+ 	skb->tcp_dataindex = skb->len;
+ 	memcpy_fromfs(buff + tmp, from, copy);
+ 	from += copy;
+ 	copied += copy;
+ 	len -= copy;
+ 	skb->len += copy;
+ 	skb->free = 0;
+ 	sk->send_seq += copy;
+ 
+ 	/* If we were doing a partial packet, cycle to end loop. */
+ 	if (sk->send_tmp != NULL) continue;
+ 
+ 	/* This is a new packet, so create TCP checksum. */
+ 	tcp_send_check((struct tcphdr *)buff, sk->saddr, sk->daddr,
+ 		        copy + sizeof(struct tcphdr), sk);
+ 
+ 	/*
+ 	 * Update SEQuence and ACKseq pointers. 
+ 	 * If we can send the frame right now, do it, otherwise
+ 	 * put this frame into the "write" queue.
+ 	 */
+ 	skb->seq = sk->send_seq;
+ 	if (after(sk->send_seq , sk->window_seq) ||
+ 		  sk->packets_out >= sk->cong_window) {
+ 		DPRINTF((DBG_TCP, "sk->cong_window=%d, sk->packets_out=%d\n",
+ 					sk->cong_window, sk->packets_out));
+ 		DPRINTF((DBG_TCP, "sk->send_seq=%d, sk->window_seq=%d\n",
+ 					sk->send_seq, sk->window_seq));
+ 		skb->qnext = NULL;
+ 		if (sk->wback == NULL) {
+ 			sk->wfront = skb;
+ 		} else {
+ 			sk->wback->qnext = skb;
+ 		}
+ 		sk->wback = skb;
+ 	} else {
+ 		DPRINTF((DBG_TCP, "TCP: write: queue it\n"));
+ 		ip_queue_xmit(sk, dev, skb, 0);
+ 	}
+   }
+   sk->err = 0;
+ 
+   /*
+    * Catch race condition: an ACK might have arrived and changed
+    * packets_out when we released the sock to allocate
+    * the partial packet.
+    */
+   if (sk->send_tmp && sk->packets_out < sk->cong_window)
+ 					tcp_send_partial(sk);
+ 
+   release_sock(sk);
+   DPRINTF((DBG_TCP, "TCP: write: done, copied=%d\n", copied));
+   return(copied);
+ }
+ 
+ 
+ static int
+ tcp_sendto(struct sock *sk, unsigned char *from,
+ 	   int len, int nonblock, unsigned flags,
+ 	   struct sockaddr_in *addr, int addr_len)
+ {
+   struct sockaddr_in sin;
+ 
+   if (addr_len < sizeof(sin)) return(-EINVAL);
+   memcpy_fromfs(&sin, addr, sizeof(sin));
+   if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL);
+   if (sin.sin_port != sk->dummy_th.dest) return(-EINVAL);
+   if (sin.sin_addr.s_addr != sk->daddr) return(-EINVAL);
+   return(tcp_write(sk, from, len, nonblock, flags));
+ }
+ 
+ 
+ static void
+ tcp_read_wakeup(struct sock *sk)
+ {
+   int tmp;
+   struct ifnet *dev = NULL;
+   struct tcphdr *t1;
+   struct sk_buff *buff;
+   int win;
+ 
+   DPRINTF((DBG_TCP, "TCP: read_wakeup\n"));
+   if (! sk->ack_backlog) return;
+ 
+   /*
+    * FIXME: we need to put code here to prevent this routine from
+    * being called.  Being called once in a while is ok, so only check
+    * if this is the second time in a row.
+    */
+ 
+   /*
+    * We need to grab some memory, and put together an ack,
+    * and then put it into the queue to be sent.
+    */
+   buff = (struct sk_buff *) sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
+   if (buff == NULL) {
+ 	/* Try again real soon. */
+ 	inet_reset_timer(sk, TIME_WRITE, 10);
+ 	return;
+   }
+   buff->len = sizeof(struct tcphdr);
+   buff->sk = sk;
+ 
+   /* Put in the IP header and routing stuff. */
+   tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev, IPPROTO_TCP,
+ 			sk->opt, sk->tos, sk->ttl, sk->dontroute);
+   if (tmp < 0) {
+ 	sk->prot->wfree(sk, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	return;
+   }
+   buff->len += tmp;
+ 
+   t1 = (struct tcphdr *) ((char *)(buff + 1) + tmp);
+   memcpy(t1, (void *) &sk->dummy_th, sizeof(struct tcphdr));
+   t1->seq = htonl(sk->send_seq);
+   t1->ack = 1;
+   t1->res1 = 0;
+   t1->res2 = 0;
+   t1->rst = 0;
+   t1->urg = 0;
+   t1->syn = 0;
+   t1->psh = 0;
+   sk->ack_backlog = 0;
+   sk->bytes_rcv = 0;
+   win = sk->prot->rspace(sk);
+   if (win > sk->window) sk->window = win;
+   t1->window = htons(sk->window);
+   t1->ack_seq = htonl(sk->acked_seq);
+   t1->doff = sizeof(struct tcphdr) / sizeof(unsigned long);
+   tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(struct tcphdr), sk);
+   ip_queue_xmit(sk, dev, buff, 1);
+ }
+ 
+ 
+ /*
+  * FIXME:
+  * This routine frees used buffers.
+  * It should consider sending an ACK to let the
+  * other end know we now have a bigger window.
+  */
+ static void
+ cleanup_rbuf(struct sock *sk)
+ {
+   int left;
+ 
+   DPRINTF((DBG_TCP, "TCP: cleanup_rbuf: sk=0x%X\n", sk));
+   left = sk->prot->rspace(sk);
+ 
+   /*
+    * We have to loop through all the buffer headers,
+    * and try to free up all the space we can.
+    */
+   while(sk->rqueue != NULL ) {
+ 	struct sk_buff *skb;
+ 
+ 	skb = sk->rqueue->qnext;
+ 	if (! skb->used) break;
+ 	if (sk->rqueue == skb) {
+ 		sk->rqueue = NULL;
+ 	} else {
+ 		skb->qnext->qprev = skb->qprev;
+ 		skb->qprev->qnext = skb->qnext;
+ 	}
+ 	skb->sk = sk;
+ 	inet_kfree_skb(skb, FREE_READ);
+   }
+ 
+   /*
+    * FIXME:
+    * At this point we should send an ACK if the difference
+    * in the window, and the amount of space is bigger than
+    * TCP_WINDOW_DIFF.
+    */
+   DPRINTF((DBG_TCP, "sk->window left = %d, sk->prot->rspace(sk)=%d\n",
+ 			sk->window, sk->prot->rspace(sk)));
+ 
+   if (sk->prot->rspace(sk) != left) {
+ 	/*
+ 	 * This area has caused the most trouble.  The current strategy
+ 	 * is to simply do nothing if the other end has room to send at
+ 	 * least 3 full packets, because the ack from those will auto-
+ 	 * matically update the window.  If the other end doesn't think
+ 	 * we have much space left, but we have room for atleast 1 more
+ 	 * complete packet than it thinks we do, we will send an ack
+ 	 * immediatedly.  Otherwise we will wait up to .5 seconds in case
+ 	 * the user reads some more.
+ 	 */
+ 	sk->ack_backlog++;
+ 	if ((sk->prot->rspace(sk) > (sk->window + sk->mss))) {
+ 		/* Send an ACK right now. */
+ 		tcp_read_wakeup(sk);
+ 	} else {
+ 		/* Force it to send an ACK soon. */
+ 		int was_active = del_timer(&sk->timer);
+ 		if (! was_active || TCP_ACK_TIME < sk->timer.expires) {
+ 			inet_reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
+ 		} else
+ 			add_timer(&sk->timer);
+ 	}
+   }
+ } 
+ 
+ 
+ /* Handle reading urgent data. */
+ static int
+ tcp_read_urg(struct sock * sk, int nonblock,
+ 	     unsigned char *to, int len, unsigned flags)
+ {
+   struct sk_buff *skb;
+   int err, copied = 0;
+ 
+   DPRINTF((DBG_TCP, "TCP: read_urg: sk=0x%X to=0x%X len=%d flags=%X)\n",
+ 							sk, to, len, flags));
+ 
+   while(len > 0) {
+ 	sk->inuse = 1;
+ 	while(sk->urg == 0 || sk->rqueue == NULL) {
+ 		if (sk->err) {
+ 			int tmp;
+ 
+ 			release_sock(sk);
+ 			if (copied) return(copied);
+ 			tmp = -sk->err;
+ 			sk->err = 0;
+ 			return(tmp);
+ 		}
+ 
+ 		if (sk->state == TCP_CLOSE || sk->done) {
+ 			release_sock(sk);
+ 			if (copied) return(copied);
+ 			if (! sk->done) {
+ 				sk->done = 1;
+ 				return(0);
+ 			}
+ 			return(-ENOTCONN);
+ 		}
+ 		 
+ 		if (sk->shutdown & RCV_SHUTDOWN) {
+ 			release_sock(sk);
+ 			if (copied == 0) sk->done = 1;
+ 			return(copied);
+ 		}
+ 
+ 		if (nonblock || copied) {
+ 			release_sock(sk);
+ 			if (copied) return(copied);
+ 			return(-EAGAIN);
+ 		}
+ 
+ 		/* Now at this point, we may have gotten some data. */
+ 		release_sock(sk);
+ 		cli();
+ 		if ((sk->urg == 0 || sk->rqueue == NULL) &&
+ 		    sk->err == 0 && !(sk->shutdown & RCV_SHUTDOWN)) {
+ 			interruptible_sleep_on(sk->sleep);
+ 			if (current->signal & ~current->blocked) {
+ 				sti();
+ 				if (copied) return(copied);
+ 				return(-ERESTARTSYS);
+ 			}
+ 		}
+ 		sk->inuse = 1;
+ 		sti();
+ 	}
+ 
+ 	skb = sk->rqueue->qnext;
+ 	do {
+ 		int amt;
+ 
+ 		if (SKBTCP(skb)->urg && !skb->urg_used) {
+ 			if (SKBTCP(skb)->urg_ptr == 0) {
+ 				SKBTCP(skb)->urg_ptr = htons(skb->len);
+ 			}
+ 			amt = min(htons(SKBTCP(skb)->urg_ptr), len);
+ 			err = verify_area(VERIFY_WRITE, to, amt);
+ 			if (err < 0) return(err);
+ 			memcpy_tofs(to, (unsigned char *)(SKBTCP(skb)) +
+ 				SKBTCP(skb)->doff*sizeof(unsigned long), amt);
+ 
+ 			if (! (flags & MSG_PEEK)) {
+ 				skb->urg_used = 1;
+ 				sk->urg--;
+ 			}
+ 			release_sock(sk);
+ 			copied += amt;
+ 			return(copied);
+ 		}
+ 		skb = skb->qnext;
+ 	} while(skb != sk->rqueue->qnext);
+   }
+   sk->urg = 0;
+   release_sock(sk);
+   return(0);
+ }
+ 
+ 
+ /* This routine copies from a sock struct into the user buffer. */
+ static int
+ tcp_read(struct sock *sk, unsigned char *to,
+ 	 int len, int nonblock, unsigned flags)
+ {
+   struct sk_buff *skb;
+   unsigned long offset;
+   unsigned long used;
+   int err, copied = 0;
+ 
+   if (len == 0) return(0);
+   if (len < 0) return(-EINVAL);
+ 
+   err = verify_area(VERIFY_WRITE, to, len);
+   if (err < 0) return(err);
+   
+   /* This error should be checked. */
+   if (sk->state == TCP_LISTEN) return(-ENOTCONN);
+ 
+   /* Urgent data needs to be handled specially. */
+   if ((flags & MSG_OOB)) return(tcp_read_urg(sk, nonblock, to, len, flags));
+ 
+   /* So no-one else will use this socket. */
+   sk->inuse = 1;
+   if (sk->rqueue != NULL) skb = sk->rqueue->qnext;
+     else skb = NULL;
+ 
+   DPRINTF((DBG_TCP,
+ 	"TCP: read: sk=0x%X to=0x%X len=%d nonblock=%d flags=0x%X)\n",
+ 						sk, to, len, nonblock, flags));
+ 
+   while(len > 0) {
+ 	/* skb->used just checks to see if we've gone all the way around. */
+ 	while(skb == NULL ||
+ 	      before(sk->copied_seq+1, ntohl(SKBTCP(skb)->seq)) || skb->used) {
+ 		DPRINTF((DBG_TCP, "TCP: read: skb=0x%X\n", skb));
+ 		cleanup_rbuf(sk);
+ 		if (sk->err) {
+ 			int tmp;
+ 
+ 			release_sock(sk);
+ 			if (copied) {
+ 				DPRINTF((DBG_TCP, "TCP: read: returning %d\n",
+ 									copied));
+ 				return(copied);
+ 			}
+ 			tmp = -sk->err;
+ 			sk->err = 0;
+ 			return(tmp);
+ 		}
+ 
+ 		if (sk->state == TCP_CLOSE) {
+ 			release_sock(sk);
+ 			if (copied) {
+ 				DPRINTF((DBG_TCP, "TCP: read: returning %d\n",
+ 								copied));
+ 				return(copied);
+ 			}
+ 			if (! sk->done) {
+ 				sk->done = 1;
+ 				return(0);
+ 			}
+ 			return(-ENOTCONN);
+ 		}
+ 
+ 		if (sk->shutdown & RCV_SHUTDOWN) {
+ 			release_sock(sk);
+ 			if (copied == 0) sk->done = 1;
+ 			DPRINTF((DBG_TCP, "TCP: read: returning %d\n", copied));
+ 			return(copied);
+ 		}
+ 			
+ 		if (nonblock || copied) {
+ 			release_sock(sk);
+ 			if (copied) {
+ 				DPRINTF((DBG_TCP, "TCP: read: returning %d\n",
+ 								copied));
+ 				return(copied);
+ 			}
+ 			return(-EAGAIN);
+ 		}
+ 
+ 		if ((flags & MSG_PEEK) && copied != 0) {
+ 			release_sock(sk);
+ 			DPRINTF((DBG_TCP, "TCP: read: returning %d\n", copied));
+ 			return(copied);
+ 		}
+ 		 
+ 		DPRINTF((DBG_TCP, "TCP: read about to sleep; state=%d\n",
+ 								sk->state));
+ 		release_sock(sk);
+ 
+ 		/*
+ 		 * Now we may have some data waiting or we could
+ 		 * have changed state.
+ 		 */
+ 		cli();
+ 		if ((sk->shutdown & RCV_SHUTDOWN) || sk->err != 0) {
+ 			sk->inuse = 1;
+ 			sti();
+ 			continue;
+ 		}
+ 
+ 		if (sk->rqueue == NULL ||
+ 		    before(sk->copied_seq+1, ntohl(SKBTCP(sk->rqueue->qnext)->seq))) {
+ 			interruptible_sleep_on(sk->sleep);
+ 			if (current->signal & ~current->blocked) {
+ 				sti();
+ 				if (copied) {
+ 					DPRINTF((DBG_TCP,
+ 						"TCP: read: returning %d\n",
+ 								copied));
+ 					return(copied);
+ 				}
+ 				return(-ERESTARTSYS);
+ 			}
+ 		}
+ 		sk->inuse = 1;
+ 		sti();
+ 		DPRINTF((DBG_TCP, "TCP: read woke up. \n"));
+ 
+ 
+ 		if (sk->rqueue == NULL) skb = NULL;
+ 		  else skb = sk->rqueue->qnext;
+ 	}
+ 
+ 	/*
+ 	 * Copy anything from the current block that needs
+ 	 * to go into the user buffer.
+ 	 */
+ 	 offset = sk->copied_seq+1 - ntohl(SKBTCP(skb)->seq);
+ 
+ 	 if (SKBTCP(skb)->syn) offset--;
+ 	 if (offset < skb->len) {
+ 		/*
+ 		 * If there is urgent data we must either
+ 		 * return or skip over it.
+ 		 */
+ 		if (SKBTCP(skb)->urg) {
+ 			if (skb->urg_used) {
+ 				sk->copied_seq += ntohs(SKBTCP(skb)->urg_ptr);
+ 				offset += ntohs(SKBTCP(skb)->urg_ptr);
+ 				if (offset >= skb->len) {
+ 					skb->used = 1;
+ 					skb = skb->qnext;
+ 					continue;
+ 				}
+ 			} else {
+ 				release_sock(sk);
+ 				if (copied) return(copied);
+ 				send_sig(SIGURG, current, 0);
+ 				return(-EINTR);
+ 			}
+ 		}
+ 		used = min(skb->len - offset, len);
+ 		memcpy_tofs(to, ((unsigned char *)SKBTCP(skb)) +
+ 			    SKBTCP(skb)->doff*4 + offset, used);
+ 		copied += used;
+ 		len -= used;
+ 		to += used;
+ 		if (! (flags & MSG_PEEK)) sk->copied_seq += used;
+ 	      
+ 		/*
+ 		 * Mark this data used if we are really reading it,
+ 		 * and if it doesn't contain any urgent data. And we
+ 		 * have used all the data.
+ 		 */
+ 		if (! (flags & MSG_PEEK) &&
+ 		   ((! SKBTCP(skb)->urg) || skb->urg_used) &&
+ 		   (used + offset >= skb->len)) skb->used = 1;
+ 	      
+ 		/*
+ 		 * See if this is the end of a message or if the
+ 		 * remaining data is urgent.
+ 		 */
+ 		if (SKBTCP(skb)->psh || SKBTCP(skb)->urg) break;
+ 	} else {	/* already used this data, must be a retransmit */
+ 		skb->used = 1;
+ 	}
+ 	skb = skb->qnext;
+   }
+   cleanup_rbuf(sk);
+   release_sock(sk);
+   DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied));
+   if (copied == 0 && nonblock) return(-EAGAIN);
+   return(copied);
+ }
+ 
+   
+ /*
+  * Send a FIN without closing the connection.
+  * Not called at interrupt time.
+  */
+ static void
+ tcp_shutdown(struct sock *sk, int how)
+ {
+   struct sk_buff *buff;
+   struct tcphdr *t1, *th;
+   struct proto *prot;
+   int tmp;
+   struct ifnet *dev = NULL;
+   int win;
+ 
+   /*
+    * We need to grab some memory, and put together a FIN,
+    * and then put it into the queue to be sent.
+    * FIXME:
+    *	Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
+    *	Most of this is guesswork, so maybe it will work...
+    */
+   /* If we've already sent a FIN, return. */
+   switch (sk->state) {
+ 	case TCP_LISTEN:
+ 	case TCP_SYN_SENT:
+ 		return;	/* error: illegal in this state */
+ 
+ 	case TCP_FIN_WAIT1:
+ 	case TCP_FIN_WAIT2:
+ 	case TCP_CLOSING:
+ 	case TCP_LAST_ACK:
+ 	case TCP_TIME_WAIT:	/* These two	    */
+ 	case TCP_CLOSE:		/* shouldn't happen */
+ 		return;
+   }
+   if (! (how & SEND_SHUTDOWN)) return;
+   sk->inuse = 1;
+ 
+   /* Clear out any half completed packets. */
+   if (sk->send_tmp) tcp_send_partial(sk);
+ 
+   prot = sk->prot;
+   th = &sk->dummy_th;
+   release_sock(sk); /* incase the malloc sleeps. */
+   buff = (struct sk_buff *) prot->wmalloc(sk, MAX_RESET_SIZE, 1, GFP_KERNEL);
+   if (buff == NULL) return;
+   sk->inuse = 1;
+ 
+   DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff));
+   buff->sk = sk;
+   buff->len = sizeof(struct tcphdr);
+ 
+   /* Put in the IP header and routing stuff. */
+   tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev, IPPROTO_TCP,
+ 			sk->opt, sk->tos, sk->ttl, sk->dontroute);
+   if (tmp < 0) {
+ 	prot->wfree(sk, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	release_sock(sk);
+ 	DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
+ 	return;
+   }
+   buff->len += tmp;
+   buff->dev = dev;
+ 
+   t1 = (struct tcphdr *) ((char *)(buff + 1) + tmp);
+   memcpy(t1, th, sizeof(*t1));
+   t1->seq = htonl(sk->send_seq);
+   sk->send_seq++;
+   buff->seq = sk->send_seq;
+   t1->ack = 1;
+   t1->ack_seq = htonl(sk->acked_seq);
+   win = sk->prot->rspace(sk);
+   if (win > sk->window) sk->window = win;
+   t1->window = htons(sk->window);
+   t1->fin = 1;
+   t1->rst = 0;
+   t1->doff = sizeof(struct tcphdr) / sizeof(unsigned long);
+   tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
+ 
+   /*
+    * Can't just queue this up.
+    * It should go at the end of the write queue.
+    */
+   if (sk->wback != NULL) {
+ 	buff->free = 0;
+ 	buff->qnext = NULL;
+ 	sk->wback->qnext = buff;
+ 	sk->wback = buff;
+   } else {
+ 	ip_queue_xmit(sk, dev, buff, 0);
+   }
+ 
+   switch (sk->state) {
+ 	case TCP_ESTABLISHED:
+ 	case TCP_SYN_RECV:
+ 		sk->state = TCP_FIN_WAIT1;
+ 		break;
+ 
+ 	case TCP_CLOSE_WAIT:
+ 		sk->state = TCP_LAST_ACK;
+ 		break;
+   }
+ 
+   release_sock(sk);
+ }
+ 
+ 
+ static int
+ tcp_recvfrom(struct sock *sk, unsigned char *to,
+ 	     int to_len, int nonblock, unsigned flags,
+ 	     struct sockaddr_in *addr, int *addr_len)
+ {
+   struct sockaddr_in sin;
+   int err, len;
+   int result;
+ 
+   /*
+    * We have to check all parameters before we try
+    * to read any data, otherwise data could be lost
+    * if an error occurs, and this is wrong.
+    */
+   err = verify_area(VERIFY_WRITE, addr_len, sizeof(int));
+   if (err < 0) return(err);
+   len = get_fs_long((int *) addr_len);
+   if (len > sizeof(sin)) len = sizeof(sin);
+   err = verify_area(VERIFY_WRITE, addr, len);
+   if (err < 0) return(err);
+ 
+   result = tcp_read(sk, to, to_len, nonblock, flags);
+   if (result < 0) return(result);
+ 
+   sin.sin_family = AF_INET;
+   sin.sin_port = sk->dummy_th.dest;
+   sin.sin_addr.s_addr = sk->daddr;
+   memcpy_tofs(addr, &sin, len);
+   put_fs_long(len, (int *) addr_len);
+   return(result);
+ }
+ 
+ 
+ /* This routine will send an RST to the other tcp. */
+ static void
+ tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
+ 	  struct proto *prot, struct options *opt, struct ifnet *dev,
+ 	  int tos, int ttl)
+ {
+   struct sk_buff *buff;
+   struct tcphdr *t1;
+   int tmp;
+ 
+   /*
+    * We need to grab some memory, and put together an RST,
+    * and then put it into the queue to be sent.
+    */
+   buff = (struct sk_buff *) prot->wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC);
+   if (buff == NULL) return;
+ 
+   DPRINTF((DBG_TCP, "TCP: reset: buff=0x%X\n", buff));
+   buff->len = sizeof(struct tcphdr);
+   buff->sk = NULL;
+   buff->dev = dev;
+ 
+   /* Put in the IP header and routing stuff. */
+   tmp = ip_build_header(buff, saddr, daddr, &dev,
+ 			IPPROTO_TCP, opt, tos, ttl, 0);
+   if (tmp < 0) {
+ 	prot->wfree(NULL, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	return;
+   }
+   buff->len += tmp;
+   t1 = (struct tcphdr *) ((char *)(buff + 1) + tmp);
+   memcpy(t1, th, sizeof(struct tcphdr));
+ 
+   /* Swap the send and the receive. */
+   t1->dest = th->source;
+   t1->source = th->dest;
+   t1->seq = th->ack_seq;
+   t1->ack_seq = htonl(ntohl(th->seq) + 1);
+   t1->rst = 1;
+   t1->window = 0;
+   t1->ack = 1;	/* several implementations of TCP need it */
+   t1->syn = 0;
+   t1->urg = 0;
+   t1->fin = 0;
+   t1->psh = 0;
+   t1->doff = sizeof(struct tcphdr) / sizeof(unsigned long);
+   tcp_send_check(t1, saddr, daddr, sizeof(struct tcphdr), NULL);
+   ip_queue_xmit(NULL, dev, buff, 1);
+ }
+ 
+ 
+ /*
+  * This routine handles a connection request.
+  * It should make sure we haven't already responded.
+  * Because of the way BSD works, we have to send a SYN/ACK now.
+  * This also means it will be harder to close a socket which is
+  * listening.
+  */
+ static void
+ tcp_conn_request(struct sock *sk, struct sk_buff *skb, struct ip *iph,
+ 		 unsigned long daddr, unsigned long saddr,
+ 		 struct options *opt, struct ifnet *dev, int syn_seq)
+ {
+   struct sk_buff *buff;
+   struct tcphdr *t1;
+   unsigned char *ptr;
+   struct sock *newsk;
+   struct tcphdr *th;
+   int tmp;
+ 
+   DPRINTF((DBG_TCP,
+ 	"TCP: conn_request: sk=0x%X skb=0x%X daddr=0x%X saddr=0x%X,\n"
+ 	"                   iph=0x%X opt=0x%X dev=0x%X)\n",
+ 	sk, skb, daddr, saddr, iph, opt, dev));
+   th = SKBTCP(skb);
+ 
+   /* If the socket is dead, don't accept the connection. */
+   if (! sk->dead) {
+ 	wake_up_interruptible(sk->sleep);
+   } else {
+ 	DPRINTF((DBG_TCP, "TCP: conn_request: dead socket\n"));
+ 	tcp_reset(daddr, saddr, th, sk->prot, opt, dev,
+ 		(iph == NULL) ? sk->tos : iph->ip_tos, sk->ttl);
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   /*
+    * Make sure we can accept more.  This will prevent a
+    * flurry of SYNs from eating up all our memory.
+    */
+   if (sk->ack_backlog >= sk->max_ack_backlog) {
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   /*
+    * We need to build a new sock struct.
+    * It is sort of bad to have a socket without an inode attached
+    * to it, but the wake_up's will just wake up the listening socket,
+    * and if the listening socket is destroyed before this is taken
+    * off of the queue, this will take care of it.
+    */
+   newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
+   if (newsk == NULL) {
+ 	/* Just ignore the SYN.  It will get retransmitted. */
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+ 
+   DPRINTF((DBG_TCP, "newsk = %X\n", newsk));
+   memcpy((char *) newsk, (char *) sk, sizeof(struct sock));
+   newsk->wback = NULL;
+   newsk->wfront = NULL;
+   newsk->rqueue = NULL;
+   newsk->send_head = NULL;
+   newsk->send_tail = NULL;
+   newsk->back_log = NULL;
+   newsk->rtt = TCP_CONNECT_TIME;
+   newsk->blog = 0;
+   newsk->intr = 0;
+   newsk->proc = 0;
+   newsk->done = 0;
+   newsk->send_tmp = NULL;
+   newsk->pair = NULL;
+   newsk->wmem_alloc = 0;
+   newsk->rmem_alloc = 0;
+   newsk->rcvbuf = sk->rcvbuf;
+   newsk->sndbuf = sk->sndbuf;
+   newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
+   newsk->err = 0;
+   newsk->shutdown = 0;
+   newsk->ack_backlog = 0;
+   newsk->delay_acks = tcp_delay_acks;
+   newsk->max_ack_backlog = MAX_ACK_BACKLOG;
+   newsk->acked_seq = ntohl(SKBTCP(skb)->seq) + 1;
+   newsk->fin_seq = ntohl(SKBTCP(skb)->seq);
+   newsk->syn_seq = ntohl(SKBTCP(skb)->seq);
+   newsk->state = TCP_SYN_RECV;
+   newsk->timeout = 0;
+ #if 0
+   newsk->send_seq = jiffies * SEQ_TICK - seq_offset;
+ #else
+   newsk->send_seq = syn_seq;
+ #endif
+   newsk->rcv_ack_seq = newsk->send_seq;
+   newsk->urg = 0;
+   newsk->retransmits = 0;
+   newsk->mdev = 0;
+   newsk->backoff = 0;
+   newsk->destroy = 0;
+   newsk->timer.data = (unsigned long)newsk;
+   newsk->timer.function = inet_timer;
+   newsk->dummy_th.source = SKBTCP(skb)->dest;
+   newsk->dummy_th.dest = SKBTCP(skb)->source;
+ 
+   /* Swap these two, they are from our point of view. */
+   newsk->daddr = saddr;
+   newsk->saddr = daddr;
+ 
+   put_sock(newsk->num, newsk);
+   newsk->dummy_th.res1 = 0;
+   newsk->dummy_th.doff = 6;
+   newsk->dummy_th.fin = 0;
+   newsk->dummy_th.syn = 0;
+   newsk->dummy_th.rst = 0;
+   newsk->dummy_th.psh = 0;
+   newsk->dummy_th.ack = 0;
+   newsk->dummy_th.urg = 0;
+   newsk->dummy_th.res2 = 0;
+   newsk->acked_seq = ntohl(SKBTCP(skb)->seq) + 1;
+   newsk->copied_seq = ntohl(SKBTCP(skb)->seq);
+   newsk->mss = (dev->if_mtu - HEADER_SIZE);
+ 
+   /* Grab the TTL and TOS values and use them. */
+   newsk->ttl = sk->ttl;
+   if (iph == NULL) {
+ 	printk("TCP: conn_rqst with IPH=NULL !\n");
+ 	newsk->tos = sk->tos;
+   } else {
+ 	newsk->tos = iph->ip_tos;
+   }
+ 
+   tcp_options(newsk, SKBTCP(skb));
+ 
+   buff = (struct sk_buff *) newsk->prot->wmalloc(newsk, MAX_SYN_SIZE,
+ 						 1, GFP_ATOMIC);
+   if (buff == NULL) {
+ 	sk->err = -ENOMEM;
+ 	newsk->dead = 1;
+ 	release_sock(newsk);
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+   buff->len = sizeof(struct tcphdr) + sizeof(unsigned long);
+   buff->sk = newsk;
+ 
+   /* Put in the IP header and routing stuff. */
+   tmp = ip_build_header(buff, newsk->saddr, newsk->daddr, &dev,
+ 			IPPROTO_TCP, sk->opt, sk->tos, sk->ttl, sk->dontroute);
+   if (tmp < 0) {
+ 	sk->err = tmp;
+ 	sk->prot->wfree(newsk, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	newsk->dead = 1;
+ 	release_sock(newsk);
+ 	skb->sk = sk;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return;
+   }
+   buff->len += tmp;
+ 
+   t1 = (struct tcphdr *) ((char *)(buff + 1) + tmp);
+   memcpy(t1, SKBTCP(skb), sizeof(struct tcphdr));
+ 
+   /* Swap the send and the receive. */
+   t1->dest = SKBTCP(skb)->source;
+   t1->source = newsk->dummy_th.source;
+   t1->seq = htonl(newsk->send_seq++);
+   buff->seq = newsk->send_seq;
+   t1->ack = 1;
+   newsk->window	= newsk->prot->rspace(newsk);
+   t1->window = htons(newsk->window);
+   t1->res1 = 0;
+   t1->res2 = 0;
+   t1->rst = 0;
+   t1->urg = 0;
+   t1->psh = 0;
+   t1->syn = 1;
+   t1->ack_seq = htonl(ntohl(SKBTCP(skb)->seq) + 1);
+   t1->doff = sizeof(struct tcphdr) / sizeof(unsigned long) + 1;
+ 
+   /* Fill in the TCP options. */
+   ptr = (unsigned char *) (t1 + 1);
+   ptr[0] = 2;
+   ptr[1] = 4;
+   ptr[2] = ((newsk->mss) >> 8) & 0xFF;
+   ptr[3] = (newsk->mss) & 0xFF;
+ 
+   tcp_send_check(t1, daddr, saddr, sizeof(struct tcphdr) +
+ 		     sizeof(unsigned long), newsk);
+   ip_queue_xmit(newsk, dev, buff, 0);
+ 
+   inet_reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME);
+   sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
+   skb->sk = newsk;
+ 
+   /* Charge the sock_buff to newsk. */
+   sk->rmem_alloc -= skb->mem_len;
+   newsk->rmem_alloc += skb->mem_len;
+ 
+   if (sk->rqueue == NULL) {
+ 	skb->qnext = skb;
+ 	skb->qprev = skb;
+ 	sk->rqueue = skb;
+   } else {
+ 	skb->qnext = sk->rqueue;
+ 	skb->qprev = sk->rqueue->qprev;
+ 	sk->rqueue->qprev = skb;
+ 	skb->qprev->qnext = skb;
+   }
+   sk->ack_backlog++;
+   release_sock(newsk);
+ }
+ 
+ 
+ static void
+ tcp_close(struct sock *sk, int timeout)
+ {
+   struct sk_buff *buff;
+   int need_reset = 0;
+   struct tcphdr *t1, *th;
+   struct proto *prot;
+   struct ifnet *dev = NULL;
+   int tmp, win;
+ 
+   /*
+    * We need to grab some memory, and put together a FIN,
+    * and then put it into the queue to be sent.
+    */
+   DPRINTF((DBG_TCP, "TCP: close: sk=0x%X timeout=%d\n",sk, timeout));
+   sk->inuse = 1;
+   sk->keepopen = 1;
+   sk->shutdown = SHUTDOWN_MASK;
+ 
+   if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+   /* We need to flush the recv. buffs. */
+   if (sk->rqueue != NULL) {
+ 	struct sk_buff *skb;
+ 	struct sk_buff *skb2;
+ 
+ 	skb = sk->rqueue;
+ 	do {
+ 		skb2 = skb->qnext;
+ 		/* If there is some real unread data, send a reset. */
+ 		if (skb->len > 0 &&
+ 		    after(ntohl(SKBTCP(skb)->seq) + skb->len + 1, sk->copied_seq))
+ 								need_reset = 1;
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		skb = skb2;
+ 	} while(skb != sk->rqueue);
+   }
+   sk->rqueue = NULL;
+ 
+   /* Get rid off any half-completed packets. */
+   if (sk->send_tmp) tcp_send_partial(sk);
+ 
+   switch(sk->state) {
+ 	case TCP_FIN_WAIT1:
+ 	case TCP_FIN_WAIT2:
+ 	case TCP_LAST_ACK:
+ 	case TCP_CLOSING:
+ 		/* Start a timer. */
+ 		inet_reset_timer(sk, TIME_CLOSE, 2 * sk->rtt);
+ 		release_sock(sk);
+ 		return;	/* break causes a double release - messy! */
+ 
+ 	case TCP_LISTEN:
+ 	case TCP_SYN_SENT:
+ 		sk->state = TCP_CLOSE;
+ 		release_sock(sk);
+ 		return;
+ 
+ 	case TCP_CLOSE:
+ 	case TCP_TIME_WAIT:
+ 		release_sock(sk);
+ 		return;
+ 
+ 	case TCP_CLOSE_WAIT:
+ 	case TCP_ESTABLISHED:
+ 	case TCP_SYN_RECV:
+ 		prot = sk->prot;
+ 		th = (struct tcphdr *) &sk->dummy_th;
+ 		buff = (struct sk_buff *) prot->wmalloc(sk, MAX_FIN_SIZE, 1,
+ 							GFP_ATOMIC);
+ 		if (buff == NULL) {
+ 			/*
+ 			 * This will force it to try again later.
+ 			 * Or it would have if someone released the
+ 			 * socket first.  Anyway, it might work now.
+ 			 */
+ 			release_sock(sk);
+ 			if (sk->state != TCP_CLOSE_WAIT)
+ 					sk->state = TCP_ESTABLISHED;
+ 			inet_reset_timer(sk, TIME_CLOSE, 100);
+ 			return;
+ 		}
+ 		buff->len = sizeof(struct tcphdr);
+ 		buff->sk = sk;
+ 
+ 		/* Put in the IP header and routing stuff. */
+ 		tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev,
+ 				      IPPROTO_TCP, sk->opt,
+ 				      sk->tos, sk->ttl, sk->dontroute);
+ 		if (tmp < 0) {
+ 			prot->wfree(sk, (unsigned char *) buff->mem_addr,
+ 				    buff->mem_len);
+ 			DPRINTF((DBG_TCP, "TCP: close: unable to build header for FIN.\n"));
+ 			release_sock(sk);
+ 			return;
+ 		}
+ 		buff->len += tmp;
+ 		buff->dev = dev;
+ 
+ 		t1 = (struct tcphdr *) ((char *)(buff + 1) + tmp);
+ 		memcpy(t1, th, sizeof(struct tcphdr));
+ 		t1->seq = ntohl(sk->send_seq);
+ 		sk->send_seq++;
+ 		buff->seq = sk->send_seq;
+ 		t1->ack = 1;
+ 
+ 		/* Ack everything immediately from now on. */
+ 		sk->delay_acks = 0;
+ 		t1->ack_seq = htonl(sk->acked_seq);
+ 		win = sk->prot->rspace(sk);
+ 		if (win > sk->window) sk->window = win;
+ 		t1->window = htons(sk->window);
+ 		t1->fin = 1;
+ 		t1->rst = need_reset;
+ 		t1->doff = sizeof(struct tcphdr) / sizeof(unsigned long);
+ 		tcp_send_check(t1, sk->saddr, sk->daddr,
+ 			       sizeof(struct tcphdr), sk);
+ 
+ 		if (sk->wfront == NULL) {
+ 			ip_queue_xmit(sk, dev, buff, 0);
+ 		} else {
+ 			inet_reset_timer(sk, TIME_WRITE,
+ 				    ip_backoff(sk->backoff) *
+ 				    (sk->mdev + sk->rtt));
+ 			buff->qnext = NULL;
+ 			buff->free = 0;
+ 			if (sk->wback == NULL) {
+ 				sk->wfront = buff;
+ 			} else {
+ 				sk->wback->qnext = buff;
+ 			}
+ 			sk->wback = buff;
+ 		}
+ 
+ 		if (sk->state == TCP_CLOSE_WAIT) sk->state = TCP_LAST_ACK;
+ 		  else sk->state = TCP_FIN_WAIT1;
+   }
+   release_sock(sk);
+ }
+ 
+ 
+ /*
+  * This routine takes stuff off of the write queue,
+  * and puts it in the xmit queue.
+  */
+ static void
+ tcp_write_xmit(struct sock *sk)
+ {
+   struct sk_buff *skb;
+ 
+   DPRINTF((DBG_TCP, "tcp_write_xmit(sk=%X)\n", sk));
+   while(sk->wfront != NULL &&
+         before(sk->wfront->seq, sk->window_seq) &&
+         sk->packets_out < sk->cong_window) {
+ 		skb = sk->wfront;
+ 		sk->wfront = skb->qnext;
+ 		if (sk->wfront == NULL) sk->wback = NULL;
+ 		skb->qnext = NULL;
+ 		DPRINTF((DBG_TCP, "Sending a packet.\n"));
+ 
+ 		/* See if we really need to send the packet. */
+ 		if (before(skb->seq, sk->rcv_ack_seq +1)) {
+ 			sk->retransmits = 0;
+ 			inet_kfree_skb(skb, FREE_WRITE);
+ 			if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 		} else {
+ 			ip_queue_xmit(sk, skb->dev, skb, skb->free);
+ 		}
+ 	}
+ }
+ 
+ 
+ /*
+  * This routine deals with incoming ACKs, but not outgoing ones.
+  * Result = 1 if ACK accepted, 0 if packet probably doesn't
+  * belong to this connection.
+  */
+ static int
+ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
+ {
+   unsigned long ack;
+   int flag = 0;
+ 
+   ack = ntohl(th->ack_seq);
+   DPRINTF((DBG_TCP, "TCP: ack: ack=%d, window=%d, "
+ 	  "sk->rcv_ack_seq=%d, sk->window_seq=%d\n",
+ 	  ack, ntohs(th->window), sk->rcv_ack_seq, sk->window_seq));
+ 
+   if (after(ack, sk->send_seq+1) || before(ack, sk->rcv_ack_seq-1)) {
+ 	if (after(ack, sk->send_seq) ||
+ 	   (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)) {
+ 		return(0);
+ 	}
+ 	if (sk->keepopen && !sk->timeout) {
+ 		inet_reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
+ 	}
+ 	return(1);
+   }
+ 
+   if (len != th->doff * sizeof(unsigned long)) flag |= 1;
+ 
+   /* See if our window has been shrunk. */
+   DPRINTF((DBG_TCP, "TCP: ack: check if shrunk wseq=%d ack+win=%d\n",
+ 				sk->window_seq, ack + ntohs(th->window)));
+   if (after(sk->window_seq, ack + ntohs(th->window))) {
+ 	/*
+ 	 * We may need to move packets from the send queue
+ 	 * to the write queue, if the window has been shrunk
+ 	 * on us. The RFC says you are not allowed to shrink
+ 	 * your window like this, but if the other end does,
+ 	 * you must be able to deal with it.
+ 	 */
+ 	struct sk_buff *skb;
+ 	struct sk_buff *skb2;
+ 	struct sk_buff *wskb = NULL;
+ 
+ 	DPRINTF((DBG_TCP, "TCP: ack: window shrunk!\n"));
+   
+ 	skb2 = sk->send_head;
+ 	sk->send_head = NULL;
+ 	sk->send_tail = NULL;
+ 
+ 	flag |= 4;
+ 
+ 	sk->window_seq = ack + ntohs(th->window);
+ 	if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+ 	cli();
+ 	while (skb2 != NULL) {
+ 		skb = skb2;
+ 		skb2 = skb->link3;
+ 		skb->link3 = NULL;
+ 		if (after(skb->seq, sk->window_seq)) {
+ 			if (sk->packets_out > 0) sk->packets_out--;
+ 
+ 			/*
+ 			 * The 'dev send list' is not done with the
+ 			 * 'skb->qnext' and 'skb->qprev' anymore.
+ 			 * So, the next call either succeeds or does
+ 			 * not do anything at all...
+ 			 */
+ 			dev_delque(skb);
+ 			if (skb->lock) {
+ 				/*
+ 				 * Still on 'dev' queue, add
+ 				 * back to write queue.
+ 				 */
+ 				skb->link3 = NULL;
+ 				if (sk->send_head) {
+ 					sk->send_tail->link3 = skb;
+ 					sk->send_tail = skb;
+ 				} else {
+ 					sk->send_head = skb;
+ 					sk->send_tail = skb;
+ 				}
+ 			} else {
+ 				skb->sk = sk;
+ 
+ 				/* Now add it to the write_queue. */
+ 				if (wskb == NULL) {
+ 					skb->qnext = sk->wfront;
+ 					sk->wfront = skb;
+ 				} else {
+ 					skb->qnext = wskb->qnext;
+ 					wskb->qnext = skb;
+ 				}
+ 				if (sk->wback == wskb) sk->wback = skb;
+ 				wskb = skb;
+ 			}
+ 		} else {
+ 			if (sk->send_head == NULL) {
+ 				sk->send_head = skb;
+ 				sk->send_tail = skb;
+ 			} else {
+ 				sk->send_tail->link3 = skb;
+ 				sk->send_tail = skb;
+ 			}
+ 			skb->link3 = NULL;
+ 		}
+ 	}
+ 	sti();
+   }
+ 
+   if (sk->send_tail == NULL || sk->send_head == NULL) {
+ 	DPRINTF((DBG_TCP, "TCP: ack: stail/shead NULL\n"));
+ 	sk->send_head = NULL;
+ 	sk->send_tail = NULL;
+ 	sk->packets_out = 0;
+   }
+ 
+   sk->window_seq = ack + ntohs(th->window);
+   if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+   /* We don't want too many packets out there. */
+   if (sk->cong_window < 2048 && ack != sk->rcv_ack_seq) {
+ 	if (sk->exp_growth) sk->cong_window *= 2;
+ 	  else sk->cong_window++;
+   }
+   DPRINTF((DBG_TCP, "TCP: ack: cong_window %d ack==rcv_ack_seq=%d\n",
+ 				sk->cong_window, ack == sk->rcv_ack_seq));
+   DPRINTF((DBG_TCP, "TCP: ack: updating rcv ack sequence.\n"));
+   sk->rcv_ack_seq = ack;
+ 
+   /* See if we can take anything off of the retransmit queue. */
+   while(sk->send_head != NULL) {
+ 	/* Check for a bug. */
+ 	if (sk->send_head->link3 &&
+ 	    after(sk->send_head->seq, sk->send_head->link3->seq)) {
+ 		printk("TCP: ack: *** bug send_list out of order.\n");
+ 		ip_sort_send(sk);
+ 	}
+         DPRINTF((DBG_TCP, "TCP: ack: check retran %d/%d\n",
+ 					sk->send_head->seq, ack+1));
+ 
+ 	if (before(sk->send_head->seq, ack+1)) {
+ 
+ 		struct sk_buff *oskb;
+ 
+ 		sk->retransmits = 0;
+ 
+ 		oskb = sk->send_head;
+ 
+ 		/* We have one less packet out there. */
+ 		if (sk->packets_out > 0) sk->packets_out --;
+ 		DPRINTF((DBG_TCP, "skb=%X skb->seq = %d acked ack=%d\n",
+ 				sk->send_head, sk->send_head->seq, ack));
+ 
+ 		/* Wake up the process, it can probably write more. */
+ 		if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+ 		/* Estimate the RTT. Ignore the ones right after a retransmit. */
+ 		if (sk->retransmits == 0 && ! (flag & 2)) {
+ 			long abserr, rtt = (jiffies - oskb->when) * 2;
+ 
+ 			if (sk->state == TCP_SYN_SENT ||
+ 			    sk->state == TCP_SYN_RECV)
+ 				/* First ACK, so nothing else to avg with. */
+ 				sk->rtt = rtt;
+ 			  else {
+ 				abserr = (rtt > sk->rtt) ? rtt - sk->rtt
+ 							 : sk->rtt - rtt;
+ 				sk->rtt = (7 * sk->rtt + rtt) >> 3;
+ 				if (abserr > sk->mdev) sk->mdev = abserr;
+ 				  else sk->mdev = (7 * sk->mdev + abserr) >> 3;
+ 			}
+ 			sk->backoff = 0;
+ 		}
+ 		flag |= (2 | 4);
+ 
+ 		/*
+ 		 * No point retransmitting faster than .1 sec;
+ 		 * 2 minutes is max legal RTT for Internet.
+ 		 */
+ 		if (sk->rtt < 10) sk->rtt = 10;
+ 		if (sk->rtt > 12000) sk->rtt = 12000;
+ 
+ 		cli();
+ 
+ 		/*
+ 		 * How can we know that (oskb == sk->send_head) still?
+ 		 * This might have changed by a timer interrupt- JS
+ 		 */
+ 		sk->send_head = oskb->link3;
+ 		if (sk->send_head == NULL) sk->send_tail = NULL;
+ 
+ 		/*
+ 		 * We may need to remove this from the dev send list.
+ 		 * The 'dev send list' is no longer done with the
+ 		 * 'skb->qnext' and 'skb->qprev' pointers.
+ 		 */
+ 		dev_delque(oskb);
+ 		sti();
+ 		inet_kfree_skb_sk(sk, oskb, FREE_WRITE);
+ 
+ 		/* Wake up this socket if it's not 0xdead. */
+ 		if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 	} else {
+ 		break;
+ 	}
+   }
+ 
+   /*
+    * Maybe we can take some stuff off of the write queue,
+    * and put it onto the xmit queue.
+    */
+   if (sk->wfront != NULL) {
+ 	DPRINTF((DBG_TCP, "TCP: ack: test wfront wseq=%d wfrseq=%d\n",
+ 					sk->window_seq, sk->wfront->seq));
+ 	if (after(sk->window_seq, sk->wfront->seq) &&
+ 		sk->packets_out < sk->cong_window) {
+ 		flag |= 1;
+ 		tcp_write_xmit(sk);
+ 	}
+   } else {
+ 	DPRINTF((DBG_TCP, "TCP: ack: test wfront NULL\n"));
+ 	if (sk->send_head == NULL && sk->ack_backlog == 0 &&
+ 	    sk->state != TCP_TIME_WAIT && !sk->keepopen) {
+ 		DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); 
+ 		if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+ 		inet_delete_timer(sk);
+ 	} else {
+ 		DPRINTF((DBG_TCP, "TCP: ack: setting timeout\n"));
+ 		if (sk->state != (unsigned char) sk->keepopen) {
+ 			DPRINTF((DBG_TCP, "TCP: ack: setting timeout %d (%d,%d)\n", ip_backoff(sk->backoff) * (sk->mdev + 2 * sk->rtt), sk->mdev, sk->rtt));
+ 			inet_reset_timer(sk, TIME_WRITE,
+ 				    ip_backoff(sk->backoff) *
+ 					       (sk->mdev + (2 * sk->rtt)));
+ 		}
+ 		if (sk->state == TCP_TIME_WAIT) {
+ 			DPRINTF((DBG_TCP, "TCP: ack: setting timeout tw %d\n",
+ 							TCP_TIMEWAIT_LEN));
+ 			inet_reset_timer(sk, TIME_DONE, TCP_TIMEWAIT_LEN);
+ 		}
+ 	}
+   }
+ 
+   /* FIXME: When should we send partial packets?? - JS */
+   if ((sk->packets_out < sk->cong_window) &&
+       (sk->send_tmp != NULL) && (sk->wfront == NULL)) {
+ 	DPRINTF((DBG_TCP, "TCP: ack: send partial\n"));
+ 	flag |= 1;
+ 	tcp_send_partial(sk);
+   }
+ 
+   /* See if we are done. */
+   if (sk->state == TCP_FIN_WAIT1) {
+ 	if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 	if (sk->rcv_ack_seq == sk->send_seq) {
+ 		flag |= 1;
+ 		sk->state = TCP_FIN_WAIT2;
+ 		sk->shutdown |= SEND_SHUTDOWN;
+ 		inet_reset_timer(sk, TIME_CLOSE, 4 * sk->rtt);
+ 	}
+   }
+ 
+   if (sk->state == TCP_LAST_ACK || sk->state == TCP_CLOSING) {
+ 	if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 	if (sk->rcv_ack_seq == sk->send_seq) {
+ 		if (sk->acked_seq != sk->fin_seq)
+ 			tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th,
+ 			   sk->daddr);
+ 		flag |= 1;
+ 		if (sk->state == TCP_LAST_ACK) sk->state = TCP_CLOSE;
+ 		  else tcp_time_wait(sk);
+ 		sk->shutdown = SHUTDOWN_MASK;
+ 	}
+   }
+ 
+   if (((! flag) || (flag & 4)) && sk->send_head != NULL &&
+ 	(sk->send_head->when + ip_backoff(sk->backoff) *
+ 			(sk->mdev + sk->rtt) < jiffies)) {
+ 	sk->exp_growth = 0;
+ 	ip_retransmit(sk, 0);
+ 	DPRINTF((DBG_TCP, "TCP: ack: ip_retransmit\n"));
+   }
+ 
+   DPRINTF((DBG_TCP, "TCP: ack: done\n"));
+   return(1);
+ }
+ 
+ 
+ /*
+  * This routine handles the data.  If there is room in the buffer,
+  * it will be have already been moved into it.  If there is no
+  * room, then we will just have to discard the packet.
+  */
+ static int
+ tcp_data(struct sk_buff *skb, struct sock *sk, 
+ 	 unsigned long saddr, unsigned short len)
+ {
+   struct sk_buff *skb1, *skb2;
+   struct tcphdr *th;
+   unsigned long old_acked_seq;
+   int new_window;
+ 
+   th = SKBTCP(skb);
+   print_th(th);
+   skb->len = len - (th->doff * sizeof(unsigned long));
+   DPRINTF((DBG_TCP, "tcp_data len = %d sk = %X:\n", skb->len, sk));
+ 
+   sk->bytes_rcv += skb->len;
+   if (skb->len == 0 && !th->fin && !th->urg && !th->psh) {
+ 	/* Don't want to keep passing ack's back and forth. */
+ 	if (! th->ack)
+ 		tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return(0);
+   }
+ 
+   if (skb->len && (sk->shutdown & RCV_SHUTDOWN)) {
+ 	old_acked_seq = sk->acked_seq;
+ 	sk->acked_seq = SKB_ENDSEQ(skb);
+ 	if ((int) (sk->acked_seq - old_acked_seq) > 0) {
+ 		new_window = sk->window - sk->acked_seq + old_acked_seq;
+ 		if (new_window < 0) new_window = 0;
+ 		sk->window = new_window;
+ 	}
+ 	tcp_reset(sk->saddr, sk->daddr, SKBTCP(skb), sk->prot,
+ 		  NULL, skb->dev, sk->tos, sk->ttl);
+ 	sk->state = TCP_CLOSE;
+ 	sk->err = EPIPE;
+ 	sk->shutdown = SHUTDOWN_MASK;
+ 	DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk));
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 	return(0);
+   }
+ 
+   /*
+    * Now we have to walk the chain, and figure out where this one
+    * goes into it.  This is set up so that the last packet we received
+    * will be the first one we look at, that way if everything comes
+    * in order, there will be no performance loss, and if they come
+    * out of order we will be able to fit things in nicely.
+    */
+ 
+   /* This should start at the last one, and then go around forwards. */
+   if (sk->rqueue == NULL) {
+ 	DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb));
+ 
+ 	sk->rqueue = skb;
+ 	skb->qnext = skb;
+ 	skb->qprev = skb;
+ 	skb1= NULL;
+   } else {
+ 	DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk));
+ 
+ 	for(skb1 = sk->rqueue; ; skb1 = skb1->qprev) {
+ 		DPRINTF((DBG_TCP, "skb1=%X\n", skb1));
+ 		DPRINTF((DBG_TCP, "skb1->seq = %d\n", ntohl(SKBTCP(skb1)->seq)));
+ 		if (after(ntohl(th->seq)+1, ntohl(SKBTCP(skb1)->seq))) {
+ 			skb->qprev = skb1;
+ 			skb->qnext = skb1->qnext;
+ 			skb->qnext->qprev = skb;
+ 			skb1->qnext = skb;
+ 			if (skb1 == sk->rqueue) sk->rqueue = skb;
+ 			break;
+ 		}
+ 		if (skb1->qprev == sk->rqueue) {
+ 			skb->qnext= skb1;
+ 			skb->qprev = skb1->qprev;
+ 			skb->qprev->qnext = skb;
+ 			skb1->qprev = skb;
+ 			skb1 = NULL; /* so we know we might be able
+ 					to ack stuff. */
+ 			break;
+ 		}
+ 	}
+ 	DPRINTF((DBG_TCP, "skb = %X:\n", skb));
+   }
+ 
+   if (before(sk->acked_seq, sk->copied_seq)) {
+ 	printk("*** INET: TCP: tcp_data bug acked %d < copied %d \n",
+ 					sk->acked_seq, sk->copied_seq);
+ 	old_acked_seq = sk->acked_seq;
+ 	sk->acked_seq = sk->copied_seq;
+ 	if ((int) (sk->acked_seq - old_acked_seq) > 0) {
+ 		new_window = sk->window - sk->acked_seq + old_acked_seq;
+ 		if (new_window < 0) new_window = 0;
+ 		sk->window = new_window;
+ 	}
+   }
+ 
+   /* Now figure out if we can ack anything. */
+   if (skb1 == NULL || skb1->acked || before(ntohl(th->seq), sk->acked_seq+1)) {
+       if (before(ntohl(th->seq), sk->acked_seq+1)) {
+ 		if (after(SKB_ENDSEQ(skb), sk->acked_seq)) {
+ 			old_acked_seq = sk->acked_seq;
+ 			sk->acked_seq = SKB_ENDSEQ(skb);
+ 			if ((int) (sk->acked_seq - old_acked_seq) > 0) {
+ 				new_window = sk->window - sk->acked_seq
+ 					+ old_acked_seq;
+ 				if (new_window < 0) new_window = 0;
+ 				sk->window = new_window;
+ 			}
+ 		}
+ 		skb->acked = 1;
+ 
+ 		/* When we ack the fin, we turn on the RCV_SHUTDOWN flag. */
+ 		if (SKBTCP(skb)->fin) {
+ 			if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 			sk->shutdown |= RCV_SHUTDOWN;
+ 		}
+ 	  
+ 		for(skb2 = skb->qnext;
+ 		    skb2 != sk->rqueue->qnext;
+ 		    skb2 = skb2->qnext) {
+ 			if (before(ntohl(SKBTCP(skb2)->seq), sk->acked_seq+1)) {
+ 				if (after(SKB_ENDSEQ(skb2), sk->acked_seq)) {
+ 					old_acked_seq = sk->acked_seq;
+ 					sk->acked_seq = SKB_ENDSEQ(skb2);
+ 					if ((int) (sk->acked_seq - old_acked_seq) > 0) {
+ 						new_window = sk->window - sk->acked_seq
+ 							+ old_acked_seq;
+ 						if (new_window < 0)
+ 							new_window = 0;
+ 						sk->window = new_window;
+ 						}
+ 				}
+ 				skb2->acked = 1;
+ 
+ 				/*
+ 				 * When we ack the fin, we turn on
+ 				 * the RCV_SHUTDOWN flag.
+ 				 */
+ 				if (SKBTCP(skb2)->fin) {
+ 					sk->shutdown |= RCV_SHUTDOWN;
+ 					if (! sk->dead)
+ 						wake_up_interruptible(sk->sleep);
+ 				}
+ 
+ 				/* Force an immediate ack. */
+ 				sk->ack_backlog = sk->max_ack_backlog;
+ 			} else {
+ 				break;
+ 			}
+ 		}
+ 
+ 		/*
+ 		 * This also takes care of updating the window.
+ 		 * This if statement needs to be simplified.
+ 		 */
+ 		if (! sk->delay_acks ||
+ 		    sk->ack_backlog >= sk->max_ack_backlog || 
+ 		    sk->bytes_rcv > sk->max_unacked || th->fin) {
+ 			tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+ 		} else {
+ 			sk->ack_backlog++;
+ 			inet_reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
+ 		}
+ 	}
+   }
+ 
+   /*
+    * If we've missed a packet, send an ack.
+    * Also start a timer to send another.
+    */
+   if (! skb->acked) {
+ 	/*
+ 	 * This is important.  If we don't have much room left,
+ 	 * we need to throw out a few packets so we have a good
+ 	 * window.
+ 	 */
+ 	while (sk->prot->rspace(sk) < sk->mss) {
+ 		skb1 = sk->rqueue;
+ 		if (skb1 == NULL) {
+ 			printk("INET: TCP: data: memory leak detected.\n");
+ 			break;
+ 		}
+ 
+ 		/* Don't throw out something that has been acked. */
+ 		if (skb1->acked) {
+ 			break;
+ 		}
+ 		if (skb1->qprev == skb1) {
+ 			sk->rqueue = NULL;
+ 		} else {
+ 			sk->rqueue = skb1->qprev;
+ 			skb1->qnext->qprev = skb1->qprev;
+ 			skb1->qprev->qnext = skb1->qnext;
+ 		}
+ 		inet_kfree_skb(skb1, FREE_READ);
+ 	}
+ 	tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+   }
+ 
+   /* Now tell the user we may have some data. */
+   if (! sk->dead) {
+ 	wake_up_interruptible(sk->sleep);
+   } else {
+ 	DPRINTF((DBG_TCP, "data received on dead socket.\n"));
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr)
+ {
+   extern int kill_pg(int pg, int sig, int priv);
+   extern int kill_proc(int pid, int sig, int priv);
+     
+   if (! sk->dead) wake_up_interruptible(sk->sleep);
+     
+   if (sk->urginline) {
+ 	th->urg = 0;
+ 	th->psh = 1;
+ 	return(0);
+   }
+ 
+   if (! sk->urg) {
+ 	/* So if we get more urgent data, we don't signal the user again. */
+ 	if (sk->proc != 0) {
+ 		if (sk->proc > 0) {
+ 			kill_proc(sk->proc, SIGURG, 1);
+ 		} else {
+ 			kill_pg(-sk->proc, SIGURG, 1);
+ 		}
+ 	}
+   }
+   sk->urg++;
+   return(0);
+ }
+ 
+ 
+ /* This deals with incoming fins. */
+ static int
+ tcp_fin(struct sock *sk, struct tcphdr *th, 
+ 	 unsigned long saddr, struct ifnet *dev)
+ {
+   DPRINTF((DBG_TCP, "TCP: fin: sk=0x%X th=0x%X saddr=0x%X dev=0x%X)\n",
+ 						sk, th, saddr, dev));
+   
+   if (! sk->dead) {
+ 	wake_up_interruptible(sk->sleep);
+   }
+ 
+   switch(sk->state) {
+ 	case TCP_SYN_RECV:
+ 	case TCP_SYN_SENT:
+ 	case TCP_ESTABLISHED:
+ 		/* Contains the one that needs to be acked */
+ 		sk->fin_seq = ntohl(th->seq)+1;
+ 		sk->state = TCP_CLOSE_WAIT;
+ 		if (th->rst) sk->shutdown = SHUTDOWN_MASK;
+ 		break;
+ 
+ 	case TCP_CLOSE_WAIT:
+ 	case TCP_CLOSING:
+ 	case TCP_LAST_ACK:
+ 	case TCP_CLOSE:
+ 		break; /* we got a retransmit of the fin. */
+ 
+ 	case TCP_TIME_WAIT:
+ 		tcp_time_wait(sk);
+ 		break;
+ 
+ 	case TCP_LISTEN:
+ 		break;	/* illegal -- ignore */
+ 
+ 	case TCP_FIN_WAIT1:
+ 		/* Contains the one that needs to be acked */
+ 		sk->fin_seq = ntohl(th->seq)+1;
+ 		sk->state = TCP_CLOSING;
+ 		break;
+ 
+ 	case TCP_FIN_WAIT2:
+ 		/* Contains the one that needs to be acked */
+ 		sk->fin_seq = ntohl(th->seq)+1;
+ 		tcp_time_wait(sk);
+ 		break;
+   }
+   sk->ack_backlog++;
+   return(0);
+ }
+ 
+ 
+ /* This will accept the next outstanding connection. */
+ static struct sock *
+ tcp_accept(struct sock *sk, int flags)
+ {
+   struct sock *newsk;
+   struct sk_buff *skb;
+   
+   DPRINTF((DBG_TCP, "TCP: accept: sk=0x%X flags=0x%X addr=%s\n",
+ 				sk, flags, in_ntoa(sk->saddr)));
+ 
+   /*
+    * We need to make sure that this socket is listening,
+    * and that it has something pending.
+    */
+   if (sk->state != TCP_LISTEN) {
+ 	sk->err = EINVAL;
+ 	return(NULL); 
+   }
+ 
+   /* Avoid the race. */
+   cli();
+   sk->inuse = 1;
+   while((skb = get_firstr(sk)) == NULL) {
+ 	if (flags & O_NONBLOCK) {
+ 		sti();
+ 		release_sock(sk);
+ 		sk->err = EAGAIN;
+ 		return(NULL);
+ 	}
+ 
+ 	release_sock(sk);
+ 	interruptible_sleep_on(sk->sleep);
+ 	if (current->signal & ~current->blocked) {
+ 		sti();
+ 		sk->err = ERESTARTSYS;
+ 		return(NULL);
+ 	}
+ 	sk->inuse = 1;
+   }
+   sti();
+ 
+   /* Now all we need to do is return skb->sk. */
+   newsk = skb->sk;
+ 
+   inet_kfree_skb(skb, FREE_READ);
+   sk->ack_backlog--;
+   release_sock(sk);
+   return(newsk);
+ }
+ 
+ 
+ /* This will initiate an outgoing connection. */
+ static int
+ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
+ {
+   struct sk_buff *buff;
+   struct sockaddr_in sin;
+   struct ifnet *dev=NULL;
+   unsigned char *ptr;
+   struct tcphdr *t1;
+   int tmp;
+ 
+   /* Only unconnected sockets can connect.  Really. */
+   if (sk->state != TCP_CLOSE) return(-EISCONN);
+ 
+   /* Verify and get the user address info. */
+   if (addr_len != sizeof(struct sockaddr_in)) return(-EINVAL);
+   tmp = verify_area(VERIFY_READ, usin, addr_len);
+   if (tmp < 0) return(tmp);
+   memcpy_fromfs(&sin, usin, min(sizeof(sin), addr_len));
+   if (sin.sin_family && sin.sin_family != AF_INET) return(-EAFNOSUPPORT);
+   DPRINTF((DBG_TCP, "TCP: connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr)));
+ 
+   /* Don't want a TCP connection going to a broadcast address. */
+   if (in_chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) { 
+ 	DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n"));
+ 	return(-ENETUNREACH);
+   }
+ 
+   sk->inuse = 1;
+   sk->daddr = sin.sin_addr.s_addr;
+ 
+   /* If the source address was not yet specified, set it now. */
+   if (sk->saddr == INADDR_ANY) {
+ 	sk->saddr = in_local_addr(sk->daddr);
+ 	if (sk->saddr == INADDR_ANY) return(-ENETUNREACH);
+   }
+ 
+   sk->send_seq	= jiffies * SEQ_TICK - seq_offset;
+   sk->rcv_ack_seq = sk->send_seq - 1;
+   sk->delay_acks = tcp_delay_acks;
+   sk->syn_seq = 0;
+   sk->max_ack_backlog = MAX_ACK_BACKLOG;
+   sk->err = 0;
+   sk->dummy_th.dest = sin.sin_port;
+   release_sock(sk);
+ 
+   buff = (struct sk_buff *) sk->prot->wmalloc(sk, MAX_SYN_SIZE,0, GFP_KERNEL);
+   if (buff == NULL) return(-ENOMEM);
+   sk->inuse = 1;
+   buff->len = 24;
+   buff->sk = sk;
+ 
+   /* Put in the IP header and routing stuff. */
+   /* We need to build the routing stuff fromt the things saved in skb. */
+   tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev, IPPROTO_TCP,
+ 			NULL, sk->tos, sk->ttl, sk->dontroute);
+   if (tmp < 0) {
+ 	sk->prot->wfree(sk, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	release_sock(sk);
+ 	return(-ENETUNREACH);
+   }
+   buff->len += tmp;
+   t1 = (struct tcphdr *) ((char *)(buff + 1) + tmp);
+ 
+   memcpy(t1, (char *)&(sk->dummy_th), sizeof(struct tcphdr));
+   t1->seq = htonl(sk->send_seq++);
+   buff->seq = sk->send_seq;
+   t1->ack = 0;
+   t1->window = 2;
+   t1->res1 = 0;
+   t1->res2 = 0;
+   t1->rst = 0;
+   t1->urg = 0;
+   t1->psh = 0;
+   t1->syn = 1;
+   t1->urg_ptr = 0;
+   t1->doff = 6;
+ 
+   /* Put in the TCP options to say MTU. */
+   ptr = (unsigned char *) (t1 + 1);
+   ptr[0] = 2;
+   ptr[1] = 4;
+   ptr[2] = (dev->if_mtu- HEADER_SIZE) >> 8;
+   ptr[3] = (dev->if_mtu- HEADER_SIZE) & 0xff;
+   sk->mss = dev->if_mtu - HEADER_SIZE;
+   tcp_send_check(t1, sk->saddr, sk->daddr,
+ 		  sizeof(struct tcphdr) + 4, sk);
+ 
+   /* This must go first otherwise a really quick response will get reset. */
+   sk->state = TCP_SYN_SENT;
+ 
+   ip_queue_xmit(sk, dev, buff, 0);
+   
+   sk->rtt = TCP_CONNECT_TIME;
+   inet_reset_timer(sk, TIME_WRITE /* -1 FIXME ??? */, TCP_CONNECT_TIME);
+   sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
+   release_sock(sk);
+   return(0);
+ }
+ 
+ 
+ /* This functions checks to see if the TCP header is actually acceptable. */
+ static int
+ tcp_sequence(struct sock *sk, struct tcphdr *th, short len,
+ 	     struct options *opt, unsigned long saddr, struct ifnet *dev)
+ {
+   long seq = ntohl(th->seq);
+ 
+   /*
+    * This isn't quite right.  sk->acked_seq could be more recent
+    * than sk->window.  This is however close enough.  We will accept
+    * slightly more packets than we should, but it should not cause
+    * problems unless someone is trying to forge packets.
+    */
+   DPRINTF((DBG_TCP, "TCP: sequence: sk=0x%X th=0x%X len=%d opt=%d saddr=0x%X\n",
+ 	  sk, th, len, opt, saddr));
+ 
+   if (between(seq, sk->acked_seq, sk->acked_seq + sk->window)||
+       between(seq + len-(th->doff*4), sk->acked_seq + 1,
+   	     sk->acked_seq + sk->window) ||
+      (before(seq, sk->acked_seq) &&
+        after(seq + len -(th->doff*4), sk->acked_seq + sk->window))) {
+        return(1);
+    }
+   DPRINTF((DBG_TCP, "TCP: sequence: rejecting packet.\n"));
+ 
+   /*
+    * Send a reset if we get something not ours and we are
+    * unsynchronized. Note: We don't do anything to our end. We
+    * are just killing the bogus remote connection then we will
+    * connect again and it will work (with luck).
+    */
+   if (sk->state == TCP_SYN_SENT || sk->state==TCP_SYN_RECV) {
+ 	tcp_reset(sk->saddr, sk->daddr, th, sk->prot, NULL,
+ 		  dev, sk->tos, sk->ttl);
+   	return(1);
+   }
+ 
+   /*
+    * If it's too far ahead, send an ack to let the
+    * other end know what we expect.
+    */
+   if (after(seq, sk->acked_seq + sk->window)) {
+ 	tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+ 	return(0);
+   }
+ 
+   /* In case it's just a late ack, let it through. */
+   if (th->ack && len == (th->doff * 4) &&
+       after(seq, sk->acked_seq - 32767) &&
+       !th->fin && !th->syn) return(1);
+ 
+   if (! th->rst) {
+ 	/* Try to resync things. */
+ 	tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+   }
+   return(0);
+ }
+ 
+ 
+ /*
+  * This deals with the tcp options, it is more general than it
+  * was before.  The previous version would attempt to process
+  * TCP options even if there were none, so data that just happened
+  * to look like an option would screw everything up.
+  */
+ static void
+ tcp_options(struct sock *sk, struct tcphdr *th)
+ {
+   unsigned char *ptr;
+   unsigned char *pend;
+ 
+   /* First determine if any options exist. */
+   if (th->doff <= sizeof(struct tcphdr) / sizeof(unsigned long)) return;
+ 
+   /* Scan options. */
+   ptr = (unsigned char *)(th + 1);
+   pend = ptr + (th->doff * sizeof(unsigned long) - sizeof(*th));
+ 
+   while ((ptr < pend) && (*ptr != TCPOPT_EOL)) {
+ 	short len = 1;
+ 
+ 	switch(*ptr) {
+ 		case TCPOPT_NOP:	/* skip NOP */
+ 			break;
+ 
+ 		case TCPOPT_MAXSEG:	/* MSS option is valid only in SYN packet */
+ 			len = ptr[1];
+ 			if (th->syn) {
+ 				int mss = 0;
+ 				short i;
+ 
+ 				for (i = 2; i < len; ++i)
+ 					mss = (mss << 8) | ptr[i];
+ 				if (mss > 0 && mss < sk->mss) sk->mss = mss;
+ 			}
+ 			break;
+ 
+ 		default:
+ 			/*
+ 			 * Unknown option aborts loop (we have no
+ 			 * idea as to the size of the option).
+ 			 */
+ 			len = (pend - ptr);
+ 			break;
+ 	}
+ 	ptr += len;
+   }
+ }
+ 
+ 
+ static int
+ tcp_recv(struct sk_buff *skb, struct ifnet *dev, struct options *opt,
+ 	 unsigned long daddr, unsigned short len,
+ 	 unsigned long saddr, int redo, struct ipprot * protocol)
+ {
+   struct sock *sk;
+   struct ip *iph;
+   struct tcphdr *th;
+   int syn_seq;
+ 
+   if (skb == NULL) {
+ 	DPRINTF((DBG_TCP, "TCP: recv: skb = NULL\n"));
+ 	return(0);
+   }
+ 
+   /*
+    * It is OK for these to be NULL.
+    *   if (protocol == NULL) {
+    * 	DPRINTF((DBG_TCP, "TCP: recv: protocol = NULL\n"));
+    * 	return(0);
+    *   }
+    * 
+    *   if (opt == NULL) {
+    * 	DPRINTF((DBG_TCP, "TCP: recv: opt = NULL\n"));
+    *   }
+    */
+ 
+   if (dev == NULL) {
+ 	DPRINTF((DBG_TCP, "TCP: recv: dev = NULL\n"));
+ 	return(0);
+   }
+   iph = NULL;
+   if (! redo) {
+ 	iph = SKBIP(skb);
+ 	skb->raw += (iph->ip_hl * sizeof(unsigned long));
+ 	len -= (iph->ip_hl * sizeof(unsigned long));
+   }
+   th = SKBTCP(skb);
+ 
+   /* Find the socket. */
+   syn_seq = ntohl(th->seq);
+   sk = tcp_get_sock(th->dest, saddr, th->source, daddr,
+ 		    th->syn && !th->ack, &syn_seq);
+   DPRINTF((DBG_TCP, "<<\n"));
+   DPRINTF((DBG_TCP, "len=%d redo=%d skb=0x%X\n", len, redo, skb));
+   DPRINTF((DBG_TCP, "sk=0x%X:\n", sk));
+ 
+   if (! redo) {
+ 	if (tcp_check(th, len, saddr, daddr )) {
+ 		DPRINTF((DBG_TCP,
+ 		    "Packet from %s with len %d dropped with bad checksum.\n",
+ 							in_ntoa(saddr), len));
+ 		skb->sk = NULL;
+ 		inet_kfree_skb(skb, FREE_READ);
+ 
+ 		/*
+ 		 * We don't release the socket because it was
+ 		 * never marked in use.
+ 		 */
+ 		return(0);
+ 	}
+ 
+ 	/* See if we know about the socket. */
+ 	if (sk == NULL) {
+ 		if (! th->rst)
+ 			tcp_reset(daddr, saddr, th, &tcp_prot, opt, dev,
+ 							0, ip_parms.defttl);
+ 		skb->sk = NULL;
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		return(0);
+ 	}
+ 
+ 	/*
+ 	 * Bump intcnt so supervisor level code knows
+ 	 * something may have changed.
+ 	 */
+ 	sk->intcnt++;
+ 
+ 	skb->len = len;
+ 	skb->sk = sk;
+ 	skb->acked = 0;
+ 	skb->used = 0;
+ 	skb->urg_used = 0;
+ 	skb->dev = dev;
+ 	skb->saddr = daddr;
+ 	skb->daddr = saddr;
+ 
+        /* We may need to add it to the backlog here. */
+        cli();
+        if (sk->inuse) {
+ 		if (sk->back_log == NULL) {
+ 			sk->back_log = skb;
+ 			skb->qnext = skb;
+ 			skb->qprev = skb;
+ 		} else {
+ 			skb->qnext = sk->back_log;
+ 			skb->qprev = sk->back_log->qprev;
+ 			skb->qprev->qnext = skb;
+ 			skb->qnext->qprev = skb;
+ 		}
+ 		sti();
+ 		return(0);
+ 	}
+ 	sk->inuse = 1;
+ 	sti();
+   } else {
+ 	if (sk == NULL) {
+ 		DPRINTF((DBG_TCP, "TCP: recv: bug sk=NULL redo = 1\n"));
+ 		return(0);
+ 	}
+   }
+ 
+   if (sk->prot == NULL) {
+ 	DPRINTF((DBG_TCP, "TCP: recv: sk->prot = NULL\n"));
+ 	return(0);
+   }
+ 
+   /* Charge the memory to the socket. */
+   if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
+ 	DPRINTF((DBG_TCP, "TCP: dropping packet due to lack of buffer space.\n"));
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	release_sock(sk);
+ 	return(0);
+   }
+   sk->rmem_alloc += skb->mem_len;
+ 
+   /* Now deal with it. */
+   DPRINTF((DBG_TCP, "TCP: about to do switch.\n"));
+   switch(sk->state) {
+ 	case TCP_SYN_RECV:
+ 	case TCP_ESTABLISHED:
+ 	case TCP_CLOSE_WAIT:
+ 	case TCP_FIN_WAIT1:
+ 	case TCP_FIN_WAIT2:
+ 	case TCP_CLOSING:
+ 	case TCP_LAST_ACK:
+ 	case TCP_TIME_WAIT:
+ 		if (! tcp_sequence(sk, th, len, opt, saddr, dev)) {
+ 			if (! th->rst) tcp_send_ack(sk->send_seq,
+ 						    sk->acked_seq, sk,
+ 						    th, saddr);
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		if (th->rst || th->syn) {
+ 			/* This means the thing should really be closed. */
+ 			/* Syn is illegal in this state. */
+ 			sk->err = ECONNRESET;
+ 			if (sk->state == TCP_CLOSE_WAIT) {
+ 				sk->err = EPIPE;
+ 			}
+ 
+ 			sk->state = TCP_CLOSE;
+ 			sk->shutdown = SHUTDOWN_MASK;
+ 			if (! sk->dead) {
+ 				wake_up_interruptible(sk->sleep);
+ 			}
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ #if 0
+ 		if (opt && (opt->security != 0 ||
+ 			    opt->compartment != 0 || th->syn)) {
+ 			sk->err = ECONNRESET;
+ 			sk->state = TCP_CLOSE;
+ 			sk->shutdown = SHUTDOWN_MASK;
+ 			tcp_reset(daddr, saddr,  th, sk->prot, opt,dev);
+ 			if (! sk->dead) {
+ 				wake_up_interruptible(sk->sleep);
+ 			}
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ #endif
+ 		if (th->ack) {
+ 			if (! tcp_ack(sk, th, saddr, len)) {
+ 				inet_kfree_skb(skb, FREE_READ);
+ 				release_sock(sk);
+ 				return(0);
+ 			}
+ 		} else {
+ 			/* error: invalid packet */
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 		if (sk->state == TCP_SYN_RECV) {
+ 			sk->state = TCP_ESTABLISHED;
+ 		}
+ 		if (th->urg) {
+ 			if (tcp_urg(sk, th, saddr)) {
+ 				inet_kfree_skb(skb, FREE_READ);
+ 				release_sock(sk);
+ 				return(0);
+ 			}
+ 		}
+ 
+ 		if (th->fin && tcp_fin(sk, th, saddr, dev)) {
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		if (tcp_data(skb, sk, saddr, len)) {
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		release_sock(sk);
+ 		return(0);
+ 
+ 	case TCP_CLOSE:
+ 		if (sk->dead || sk->daddr) {
+ 			DPRINTF((DBG_TCP, "packet received for closed,dead socket\n"));
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		if (! th->rst) {
+ 			if (! th->ack) th->ack_seq = 0;
+ 			tcp_reset(daddr, saddr, th, sk->prot, opt,
+ 				  dev, sk->tos, sk->ttl);
+ 		}
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		release_sock(sk);
+ 		return(0);
+ 
+ 	case TCP_LISTEN:
+ 		if (th->rst) {
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 		if (th->ack) {
+ 			tcp_reset(daddr, saddr, th, sk->prot, opt,
+ 				  dev, sk->tos, sk->ttl);
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		if (th->syn) {
+ #if 0
+ 			if (opt->security != 0 || opt->compartment != 0) {
+ 				tcp_reset(daddr, saddr, th, prot, opt,
+ 					  dev, sk->tos, sk->ttl);
+ 				release_sock(sk);
+ 				return(0);
+ 			}
+ #endif
+ 
+ 			/*
+ 			 * Now we just put the whole thing including
+ 			 * the header and saddr, and protocol pointer
+ 			 * into the buffer.  We can't respond until the
+ 			 * user tells us to accept the connection.
+ 			 */
+ 			tcp_conn_request(sk, skb, iph, daddr, saddr,
+ 					 opt, dev, syn_seq);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		release_sock(sk);
+ 		return(0);
+ 
+ 	case TCP_SYN_SENT:
+ 		if (th->ack) {
+ 			if (tcp_ack(sk, th, saddr, len)) {
+ 				if (th->rst) {
+ 					sk->err = ECONNREFUSED;
+ 					sk->state = TCP_CLOSE;
+ 					sk->shutdown = SHUTDOWN_MASK;
+ 					if (! sk->dead) {
+ 						wake_up_interruptible(sk->sleep);
+ 					}
+ 					inet_kfree_skb(skb, FREE_READ);
+ 					release_sock(sk);
+ 					return(0);
+ 				}
+ 			} else {
+ 				if (! th->rst) tcp_reset(daddr, saddr, th,
+ 							 sk->prot, opt, dev,
+ 							 sk->tos, sk->ttl);
+ 				inet_kfree_skb(skb, FREE_READ);
+ 				release_sock(sk);
+ 				return(0);
+ 			}
+ 		} else {
+ 			if (th->rst) {
+ 				inet_kfree_skb(skb, FREE_READ);
+ 				release_sock(sk);
+ 				return(0);
+ 			}
+ 		}
+ 
+ #if 0
+ 		if (opt->security != 0 || opt->compartment != 0) {
+ 			sk->err = ECONNRESET;
+ 			sk->state = TCP_CLOSE;
+ 			sk->shutdown = SHUTDOWN_MASK;
+ 			tcp_reset(daddr, saddr,  th, sk->prot, opt,
+ 				 dev, sk->tos, sk->ttl);
+ 			if (! sk->dead) {
+ 				wake_up_interruptible(sk->sleep);
+ 			}
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ #endif
+ 		if (! th->ack) {
+ 			if (th->syn) {
+ 				sk->state = TCP_SYN_RECV;
+ 			}
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		if (! tcp_ack(sk, th, saddr, len)) {
+ 			tcp_reset(daddr, saddr, th, sk->prot, opt,
+ 				  dev, sk->tos, sk->ttl);
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		/*
+ 		 * If the syn bit is also set, switch to
+ 		 * tcp_syn_recv, and then to established.
+ 		 */
+ 		if (! th->syn) {
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		/* Ack the SYN and fall through. */
+ 		sk->acked_seq = ntohl(th->seq)+1;
+ 		sk->fin_seq = ntohl(th->seq);
+ 		tcp_send_ack(sk->send_seq, ntohl(th->seq)+1,
+ 			     sk, th, sk->daddr);
+  
+ 		sk->state = TCP_ESTABLISHED;
+ 
+ 		/*
+ 		 * Now we need to finish filling out
+ 		 * some of the tcp header.
+ 		 */
+ 		/* We need to check for mtu info. */
+ 		tcp_options(sk, th);
+ 		sk->dummy_th.dest = th->source;
+ 		sk->copied_seq = sk->acked_seq-1;
+ 		if (! sk->dead) {
+ 			wake_up_interruptible(sk->sleep);
+ 		}
+ 
+ 		/*
+ 		 * Now process the rest like we were
+ 		 * already in the established state.
+ 		 */
+ 		if (th->urg) {
+ 			if (tcp_urg(sk, th, saddr)) { 
+ 				inet_kfree_skb(skb, FREE_READ);
+ 				release_sock(sk);
+ 				return(0);
+ 			}
+ 		}
+ 
+ 		if (th->fin && tcp_fin(sk, th, saddr, dev)) {
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		if (tcp_data(skb, sk, saddr, len)) {
+ 			inet_kfree_skb(skb, FREE_READ);
+ 			release_sock(sk);
+ 			return(0);
+ 		}
+ 
+ 		release_sock(sk);
+ 		return(0);
+ 
+ 	default:
+ 		printk("TCP: This should never be reached "
+ 			"- code to make compiler happy only\n");
+ 		release_sock(sk);
+ 		return(0);
+   }
+ }
+ 
+ 
+ /*
+  * This routine sends a packet with an out of date sequence
+  * number. It assumes the other end will try to ack it.
+  */
+ static void
+ tcp_write_wakeup(struct sock *sk)
+ {
+   struct sk_buff *buff;
+   struct tcphdr *t1;
+   struct ifnet *dev=NULL;
+   int tmp;
+   int win;
+ 
+   DPRINTF((DBG_TCP, "TCP: write_wakeup\n"));
+   if (sk->state != TCP_ESTABLISHED &&
+       sk->state != TCP_CLOSE_WAIT &&
+       sk->state != TCP_FIN_WAIT1 &&
+       sk->state != TCP_FIN_WAIT2) return;
+ 
+   buff = (struct sk_buff *) sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
+   if (buff == NULL) return;
+ 
+   buff->len = sizeof(struct tcphdr);
+   buff->sk = sk;
+ 
+   /* Put in the IP header and routing stuff. */
+   tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev, IPPROTO_TCP,
+ 			sk->opt, sk->tos, sk->ttl, sk->dontroute);
+   if (tmp < 0) {
+ 	sk->prot->wfree(sk, (unsigned char *) buff->mem_addr, buff->mem_len);
+ 	return;
+   }
+   buff->len += tmp;
+ 
+   t1 = (struct tcphdr *)((char *)(buff + 1) + tmp);
+   memcpy(t1, (char *) &sk->dummy_th, sizeof(struct tcphdr));
+ 
+   /*
+    * Use a previous sequence.
+    * This should cause the other end to send an ACK.
+    */
+   t1->seq = htonl(sk->send_seq-1);
+   t1->ack = 1; 
+   t1->res1 = 0;
+   t1->res2 = 0;
+   t1->rst = 0;
+   t1->urg = 0;
+   t1->psh = 0;
+   t1->fin = 0;
+   t1->syn = 0;
+   t1->ack_seq = htonl(sk->acked_seq);
+   win = sk->prot->rspace(sk);
+   if (win > sk->window) sk->window = win;
+   t1->window = htons(sk->window);
+   t1->doff = sizeof(struct tcphdr) / sizeof(unsigned long);
+   tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(struct tcphdr), sk);
+ 
+   /*
+    * Send and free it.
+    * This will prevent the timer from automatically being restarted.
+    */
+   ip_queue_xmit(sk, dev, buff, 1);
+ }
+ 
+ 
+ /* Socket option code for TCP. */
+ static int
+ tcp_setsockopt(struct sock *sk, int level, int optname,
+ 	       char *optval, int optlen)
+ {
+   int val, err;
+ 
+   if (level != SOL_TCP)
+ 	return(ip_setsockopt(sk, level, optname, optval, optlen));
+ 
+   if (optval == NULL) return(-EINVAL);
+ 
+   err = verify_area(VERIFY_READ, optval, sizeof(int));
+   if (err < 0) return(err);
+   val = get_fs_long((unsigned long *) optval);
+ 
+   switch(optname) {
+ 	case TCP_MAXSEG:
+ 		if (val < 200 || val > 2048) return(-EINVAL);
+ 		sk->mss = val;
+ 		return(0);
+ 
+ 	case TCP_NODELAY:
+ 		/*
+ 		 * Will be done later by Johannes.  This is supposed
+ 		 * to switch Nagle on and off, not ACK delaying.
+ 		 */
+ 		return(0);
+ 
+ 	case TCP_ACKDELAY:
+ 		if (! suser()) return(-EPERM);
+ 		tcp_delay_acks = (val) ? 1 : 0;
+ 		break;
+ 
+ 	default:
+ 		break;
+   }
+   return(-ENOPROTOOPT);
+ }
+ 
+ 
+ static int
+ tcp_getsockopt(struct sock *sk, int level, int optname,
+ 	       char *optval, int *optlen)
+ {
+   int val, err;
+ 
+   if (level != SOL_TCP)
+ 	return(ip_getsockopt(sk, level, optname, optval, optlen));
+ 			
+   switch(optname) {
+ 	case TCP_MAXSEG:
+ 		val = sk->mss;
+ 		break;
+ 
+ 	case TCP_NODELAY:
+ 		val = 0;	/* see above */
+ 		break;
+ 
+ 	case TCP_ACKDELAY:
+ 		val = tcp_delay_acks;
+ 		break;
+ 
+ 	default:
+ 		return(-ENOPROTOOPT);
+   }
+ 
+   err  =  verify_area(VERIFY_WRITE, optlen, sizeof(int));
+   if (err < 0) return(err);
+   put_fs_long(sizeof(int), (unsigned long *) optlen);
+ 
+   err = verify_area(VERIFY_WRITE, optval, sizeof(int));
+   if (err < 0) return(err);
+   put_fs_long(val, (unsigned long *)optval);
+ 
+   return(0);
+ }	
+ 
+ 
+ struct proto tcp_prot = {
+   sock_wmalloc,		sock_rmalloc,		sock_wfree,
+   sock_rfree,		sock_rspace,		sock_wspace,
+   tcp_close,		tcp_read,		tcp_write,
+   tcp_sendto,		tcp_recvfrom,		tcp_connect,
+   tcp_accept,		tcp_write_wakeup,	tcp_read_wakeup,
+   tcp_recv,		tcp_select,		tcp_ioctl,
+   NULL,			tcp_shutdown,		tcp_retransmit,
+   tcp_setsockopt,	tcp_getsockopt,		128,
+   0,			{NULL,},		"TCP"
+ };
+ 
+ 
+ static struct ipprot tcp_protocol = {
+   "TCP",
+   IPPROTO_TCP,		/* protocol ID		*/
+   0,			/* copy			*/
+   NULL,			/* data			*/
+   tcp_recv,		/* TCP handler		*/
+   tcp_err,		/* TCP error control	*/
+   NULL			/* next			*/
+ };
+ 
+ 
+ #if 0
+ int
+ tcp_devread(char *buf, int count)
+ {
+   int i;
+ 
+   DPRINTF((DBG_TCP, "TCP: devread(%d, 0x%lX, %d)\n", (long) buf, count));
+ 
+   /* Verify addresses. */
+   i = verify_area(VERIFY_WRITE, (void *) buf, count);
+   if (i < 0) return(i);
+ 
+   return(inet_getinfo(&tcp_prot, buf, count, 0));
+ }
+ #endif
+ 
+ 
+ void
+ tcp_proto_init(void)
+ {
+   int i;
+ 
+   /* Register this IP protocol. */
+   (void) inet_add_proto(&tcp_protocol);
+ 
+   /* Clear all TCP sockets. */
+   for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
+ 	tcp_prot.sock_array[i] = NULL;
+   }
+ 
+   seq_offset = CURRENT_TIME*250;
+ }
*** original/net/inet/timer.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/timer.c	Mon May  9 13:13:13 1994
***************
*** 0 ****
--- 1,203 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		TIMER - implementation of software timers.
+  *
+  * Version:	@(#)timer.c	1.1.10	02/03/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Corey Minyard <wf-rch!minyard@relay.EU.net>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *		Florian La Roche, <flla@stud.uni-sb.de>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "sock.h"
+ 
+ 
+ void
+ inet_delete_timer(struct sock *t)
+ {
+   unsigned long flags;
+ 
+   save_flags(flags);
+   cli();
+ 
+   t->timeout = 0;
+   del_timer(&t->timer);
+   restore_flags(flags);
+ }
+ 
+ 
+ void
+ inet_reset_timer(struct sock *t, int timeout, unsigned long len)
+ {
+   inet_delete_timer(t);
+   if (timeout != -1) t->timeout = timeout;
+   t->timer.expires = len;
+   add_timer(&t->timer);
+ }
+ 
+ 
+ /*
+  * Now we will only be called whenever we need to do
+  * something, but we must be sure to process all of the
+  * sockets that need it.
+  */
+ void
+ inet_timer(unsigned long data)
+ {
+   struct sock *sk = (struct sock*)data;
+   int why = sk->timeout;
+ 
+   /* timeout is overwritten by 'inet_delete_timer' and 'inet_reset_timer' */
+   if (sk->inuse) {
+ 	sk->timer.expires = 10;
+ 	add_timer(&sk->timer);
+ 	return;
+   }
+   sk->inuse = 1;
+ 
+   DPRINTF((DBG_TMR, "INET: timer: found sk=%X why=%d\n", sk, why));
+   if (sk->keepopen) inet_reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
+ 
+   /* Always see if we need to send an ACK. */
+   if (sk->ack_backlog) {
+ 	sk->prot->read_wakeup(sk);
+ 	if (! sk->dead) wake_up_interruptible(sk->sleep);
+   }
+ 
+   /* Now we need to figure out why the socket was on the timer. */
+   switch(why) {
+ 	case TIME_DONE:
+ 		if (! sk->dead ||
+ 		    (sk->state != TCP_TIME_WAIT && sk->state != TCP_CLOSE)) {
+ 			printk("INET: timer: non-dead socket in time_done !\n");
+ 			release_sock(sk);
+ 			break;
+ 		}
+ 		sk->state = TCP_CLOSE;
+ 		destroy_sock(sk);
+ 		break;
+ 
+ 	case TIME_DESTROY:
+ 		/*
+ 		 * We have waited for a while for all the memory associated
+ 		 * with the socket to be freed.  We need to print an error
+ 		 * message.
+  		 */
+ 		DPRINTF((DBG_TMR,
+ 			"INET: timer: possible memory leak.  sk = %X\n", sk));
+ 		sk->inuse = 0;
+ 		destroy_sock(sk);
+ 		break;
+ 
+ 	case TIME_CLOSE:
+ 		/* We have waited long enough, close the socket. */
+ 		sk->state = TCP_CLOSE;
+ 		inet_delete_timer(sk);
+ 		if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 		sk->shutdown = SHUTDOWN_MASK;
+ 		inet_reset_timer(sk, TIME_DESTROY, TCP_DONE_TIME);
+ 		release_sock(sk);
+ 		break;
+ 
+ 	case TIME_WRITE:	/* try to retransmit. */
+ 		/*
+ 		 * It could be we got here because we needed to send
+ 		 * an ACK.  So we need to check for that.
+  		 */
+ 		if (sk->send_head) {
+ 			if (jiffies < (sk->send_head->when +
+ 						ip_backoff(sk->backoff)
+ 				* (sk->mdev + sk->rtt))) {
+ 				inet_reset_timer(sk, TIME_WRITE,
+ 					    (sk->send_head->when
+ 					     + ip_backoff(sk->backoff) *
+ 					    (sk->mdev + sk->rtt)) - jiffies);
+ 				release_sock(sk);
+ 				break;
+ 			}
+ 			DPRINTF((DBG_TMR, "INET: timer: retransmitting.\n"));
+ 			sk->prot->retransmit(sk, 0);
+ 			if ((sk->state == TCP_ESTABLISHED &&
+ 			     sk->retransmits && !(sk->retransmits & 7))
+ 				|| (sk->state != TCP_ESTABLISHED &&
+ 				sk->retransmits > TCP_RETR1)) {
+ 				DPRINTF((DBG_TMR, "INET: timer TIME_WRITE time-out 1\n"));
+ 				ip_route_check(sk->daddr);
+ 			}
+ 			if (sk->state != TCP_ESTABLISHED &&
+ 			    sk->retransmits > TCP_RETR2) {
+ 				DPRINTF((DBG_TMR, "INET: timer TIME_WRITE time-out 2\n"));
+ 				sk->err = ETIMEDOUT;
+ 				if (sk->state == TCP_FIN_WAIT1 ||
+ 				    sk->state == TCP_FIN_WAIT2 ||
+ 				    sk->state == TCP_LAST_ACK) {
+ 					sk->state = TCP_TIME_WAIT;
+ 					inet_reset_timer(sk, TIME_DONE,
+ 						    TCP_TIMEWAIT_LEN);
+ 				} else {
+ 					sk->prot->close(sk, 1);
+ 					break;
+ 				}
+ 			}
+ 		} else {
+ 			DPRINTF((DBG_TMR,
+ 				"INET: timer: nothing to retransmit\n"));
+ 		}
+ 		release_sock(sk);
+ 		break;
+ 
+ 	case TIME_KEEPOPEN:
+ 		/* Send something to keep the connection open. */
+ 		if (sk->prot->write_wakeup) sk->prot->write_wakeup(sk);
+ 		sk->retransmits++;
+ 		if (sk->shutdown == SHUTDOWN_MASK) {
+ 			sk->prot->close(sk, 1);
+ 			sk->state = TCP_CLOSE;
+ 		}
+ 
+ 		if ((sk->state == TCP_ESTABLISHED && sk->retransmits &&
+ 		     !(sk->retransmits & 7)) || (sk->state != TCP_ESTABLISHED &&
+ 			sk->retransmits > TCP_RETR1)) {
+ 			DPRINTF((DBG_TMR, "INET: timer TIME_KEEPOPEN time-out 1\n"));
+ 			ip_route_check(sk->daddr);
+ 			release_sock(sk);
+ 			break;
+ 		}
+ 		if (sk->state != TCP_ESTABLISHED &&
+ 		    sk->retransmits > TCP_RETR2) {
+ 			DPRINTF((DBG_TMR, "INET: timer TIME_KEEPOPEN time-out 2\n"));
+ 			sk->err = ETIMEDOUT;
+ 			if (sk->state == TCP_FIN_WAIT1 ||
+ 			    sk->state == TCP_FIN_WAIT2) {
+ 				sk->state = TCP_TIME_WAIT;
+ 				if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 				release_sock(sk);
+ 			} else {
+ 				sk->prot->close(sk, 1);
+ 			}
+ 			break;
+ 		}
+ 		release_sock(sk);
+ 		break;
+ 
+ 	default:
+ 		printk("INET: timer: timer expired - reason unknown, sk=%08X\n",
+ 								(int)sk);
+ 		release_sock(sk);
+ 		break;
+   }
+ }
*** original/net/inet/udp.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/udp.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,711 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		The User Datagram Protocol (UDP) as per RFC-768.
+  *
+  * Version:	@(#)udp.c	1.1.30	03/31/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Steve Entwistle, <steve@rama.demon.co.uk>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "sock.h"
+ 
+ 
+ #define SKBUDP(x)	((struct udp *) ((x)->raw))
+ #define min(a,b)	((a) < (b) ? (a) : (b))
+ 
+ 
+ static void
+ print_udp(struct udp *uh)
+ {
+   if ((inet_debug & (1 << DBG_UDP)) == 0) return;
+ 
+   if (uh == NULL) {
+ 	printk("(NULL)\n");
+ 	return;
+   }
+   printk("UDP: sport=%d dport=%d\n",
+ 		ntohs(uh->uh_sport), ntohs(uh->uh_dport));
+   printk("     len=%d sum=%d\n",
+ 		ntohs(uh->uh_len), ntohs(uh->uh_sum));
+ }
+ 
+ 
+ static int
+ udp_select(struct sock *sk, int sel_type, select_table *wait)
+ {
+   select_wait(sk->sleep, wait);
+   switch(sel_type) {
+ 	case SEL_IN:
+ 		if (sk->rqueue != NULL) {
+ 			return(1);
+ 		}
+ 		return(0);
+ 
+ 	case SEL_OUT:
+ 		if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE) {
+ 			return(1);
+ 		}
+ 		return(0);
+ 	
+ 	case SEL_EX:
+ 		if (sk->err) return(1); /* can this ever happen? */
+ 		return(0);
+   }
+   return(0);
+ }
+ 
+ 
+ /*
+  * This routine is called by the ICMP module when it gets some
+  * sort of error condition.  If err < 0 then the socket should
+  * be closed and the error returned to the user.  If err > 0
+  * it's just the icmp type << 8 | icmp code.  
+  * header points to the first 8 bytes of the tcp header.  We need
+  * to find the appropriate port.
+  */
+ static void
+ udp_err(int type, int code, int err, int fatal,
+ 	struct ip *iph, struct ipprot *protocol)
+ {
+   struct sock *sk;
+   struct udp *uh;
+   unsigned char *ptr;
+ 
+   ptr = (unsigned char *) iph;
+   uh = (struct udp *) (ptr + (iph->ip_hl * sizeof(unsigned long)));
+ 
+   DPRINTF((DBG_UDP,
+ 	"UDP: err: type=%d code=%d err=%d fat=%d iph=0x%X saddr=%s:%d\n",
+ 	type, code, err, fatal, iph, in_ntoa(iph->ip_src.s_addr),
+ 	ntohs(uh->uh_sport)));
+   DPRINTF((DBG_UDP, " daddr=%s:%d protocol=%s\n",
+ 	in_ntoa(iph->ip_dst.s_addr), ntohs(uh->uh_dport), protocol->name));
+ 
+   /* See if the socket actually exists. */
+   sk = get_sock(&udp_prot, uh->uh_sport, iph->ip_dst.s_addr,
+ 		uh->uh_dport, iph->ip_src.s_addr);
+   if (sk == NULL) return;
+ 
+   /* Hmm.  How can this be a negative number? Alan? */
+   if (type < 0) {	/* as per the calling spec */
+   	sk->err = -type;
+   	wake_up_interruptible(sk->sleep);
+   	return;
+   }
+ 
+   /*
+    * Are we being told to slooooowwwww doooowwwwnnn! ?
+    * If so, this is only a notification, not an error.
+    */
+   if (type == ICMP_SOURCEQUENCH) {
+ 	if (sk->cong_window > 1) sk->cong_window = (sk->cong_window / 2);
+ 	return;
+   }
+   sk->err = err;
+ 
+   /*
+    * It's only fatal if we have connected to them.
+    * If this is the case, tell the user that the connection
+    * was refused.  This is the way BSD handles it...
+    */
+   if (fatal && (sk->state == TCP_ESTABLISHED)) sk->err = ECONNREFUSED;
+ 
+   wake_up_interruptible(sk->sleep);
+ }
+ 
+ 
+ static unsigned short
+ udp_check(struct udp *uh, int len, unsigned long saddr, unsigned long daddr)
+ {
+   unsigned long sum;
+ 
+   DPRINTF((DBG_UDP, "UDP: check: uh=0x%X len=%d saddr=%s ",
+ 			uh, len, in_ntoa(saddr)));
+   DPRINTF((DBG_UDP, "daddr=%s\n", in_ntoa(daddr)));
+ 
+   print_udp(uh);
+ 
+   __asm__("\t addl %%ecx,%%ebx\n"
+ 	  "\t adcl %%edx,%%ebx\n"
+ 	  "\t adcl $0, %%ebx\n"
+ 	  : "=b"(sum)
+ 	  : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256)
+ 	  : "cx","bx","dx" );
+ 
+   if (len > 3) {
+ 	__asm__("\tclc\n"
+ 		"1:\n"
+ 		"\t lodsl\n"
+ 		"\t adcl %%eax, %%ebx\n"
+ 		"\t loop 1b\n"
+ 		"\t adcl $0, %%ebx\n"
+ 		: "=b"(sum) , "=S"(uh)
+ 		: "0"(sum), "c"(len/4) ,"1"(uh)
+ 		: "ax", "cx", "bx", "si" );
+   }
+ 
+   /* Convert from 32 bits to 16 bits. */
+   __asm__("\t movl %%ebx, %%ecx\n"
+ 	  "\t shrl $16,%%ecx\n"
+ 	  "\t addw %%cx, %%bx\n"
+ 	  "\t adcw $0, %%bx\n"
+ 	  : "=b"(sum)
+ 	  : "0"(sum)
+ 	  : "bx", "cx");
+ 
+   /* Check for an extra word. */
+   if ((len & 2) != 0) {
+ 	__asm__("\t lodsw\n"
+ 		"\t addw %%ax,%%bx\n"
+ 		"\t adcw $0, %%bx\n"
+ 		: "=b"(sum), "=S"(uh)
+ 		: "0"(sum) ,"1"(uh)
+ 		: "si", "ax", "bx");
+   }
+ 
+   /* Now check for the extra byte. */
+   if ((len & 1) != 0) {
+ 	__asm__("\t lodsb\n"
+ 		"\t movb $0,%%ah\n"
+ 		"\t addw %%ax,%%bx\n"
+ 		"\t adcw $0, %%bx\n"
+ 		: "=b"(sum)
+ 		: "0"(sum) ,"S"(uh)
+ 		: "si", "ax", "bx");
+   }
+ 
+   /* We only want the bottom 16 bits, but we never cleared the top 16. */
+   return((~sum) & 0xFFFF);
+ }
+ 
+ 
+ static void
+ udp_send_check(struct udp *uh, unsigned long saddr, 
+ 	       unsigned long daddr, int len, struct sock *sk)
+ {
+   uh->uh_sum = 0;
+   if (sk && sk->no_check) return;
+   uh->uh_sum = udp_check(uh, len, saddr, daddr);
+   if (uh->uh_sum == 0) uh->uh_sum = 0xffff;
+ }
+ 
+ 
+ static int
+ udp_send(struct sock *sk, struct sockaddr_in *sin,
+ 	 unsigned char *from, int len, int flags)
+ {
+   struct sk_buff *skb;
+   struct ifnet *dev;
+   struct udp *uh;
+   unsigned char *buff;
+   unsigned long saddr;
+   int err, size, tmp;
+   int temp_flag;
+ 
+   DPRINTF((DBG_UDP, "UDP: send: dst=%s:%d buff=0x%X len=%d flags=%04X)\n",
+ 	in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port), from, len, flags));
+ 
+   /*
+    * If we are sending to a broadcast address, check that this
+    * socket has the 'broadcast' flag enabled.  This is assumed
+    * behaviour, to prevent 'normal' sockets from loading the
+    * network with (expensive) broadcasts.
+    */
+   if (in_chk_addr(sin->sin_addr.s_addr) == IS_BROADCAST) {
+ 	DPRINTF((DBG_UDP, "UDP: performing a broadcast\n"));
+ 	if (sk->broadcast == 0) {
+ 		DPRINTF((DBG_UDP, "UDP: send: broadcast not allowed !"));
+ 		return(-EACCES);
+ 	}
+   }
+ 
+   /* If the source address was not set, set it now. */
+   saddr = sk->saddr;
+   if (saddr == INADDR_ANY) {
+ 	saddr = in_local_addr(sin->sin_addr.s_addr);
+ 
+ 	/*
+ 	 * With UDP, it is possible that the address of an
+ 	 * interface is still set to 0.0.0.0, as BOOTP has
+ 	 * to set it to this address to get the interface
+ 	 * into a usable state.  So, only for UDP in non-
+ 	 * connected mode, we allow 0.0.0.0 source addresses.
+ 	 */
+ #if 0
+ 	if (saddr == INADDR_ANY) return(-ENETUNREACH);
+ #endif
+   }
+ 
+   err = verify_area(VERIFY_READ, from, len);
+   if (err < 0) return(err);
+ 
+   /* Allocate a copy of the packet. */
+   size = sk->prot->max_header + len;
+ 
+   /* Check the flags given. */
+   temp_flag = sk->dontroute;
+   if (flags & MSG_DONTROUTE) temp_flag = 1;
+ 
+   /* We don't understand about there... */
+   if (flags & MSG_OOB) return(-EINVAL);
+   if (flags & MSG_PEEK) return(-EINVAL);
+ 
+   skb = (struct sk_buff *) sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
+   if (skb == NULL) return(-ENOMEM);
+   skb->len = 0;
+   skb->dev = dev;
+   skb->sk = sk;
+ 
+   /* Now build the IP and MAC header. */
+   buff = (unsigned char *) (skb + 1);
+   dev = NULL;
+   DPRINTF((DBG_UDP, "UDP: send: 0x%X -> 0x%X dev=0x%X prot=0x%X len=%d\n",
+ 	saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
+   tmp = ip_build_header(skb, saddr, sin->sin_addr.s_addr, &dev,
+ 			IPPROTO_UDP, sk->opt, sk->tos, sk->ttl, temp_flag);
+   if (tmp < 0 ) {
+ 	sk->prot->wfree(sk, (unsigned char *) skb->mem_addr, skb->mem_len);
+ 	return(tmp);
+   }
+   skb->len += tmp;
+   buff += tmp;
+   skb->len += sizeof(struct udp) + len;	/* MAC + IP + UDP + len */
+   DPRINTF((DBG_UDP, "UDP: send: MAC+IP len=%d\n", tmp));
+ 
+   /* If there is a possibity of IP fragging this packet exists, give up. */
+   if (size > dev->if_mtu) {
+ 	sk->prot->wfree(sk, (unsigned char *) skb->mem_addr, skb->mem_len);
+ 	return(-EMSGSIZE);
+   }
+ 
+   /* Fill in the UDP header. */
+   uh = (struct udp *) buff;
+   uh->uh_len = htons(len + sizeof(struct udp));
+   uh->uh_sport = sk->dummy_th.source;
+   uh->uh_dport = sin->sin_port;
+ 
+   /* Copy the user data. */
+   buff = (unsigned char *) (uh + 1);
+   memcpy_fromfs(buff, from, len);
+ 
+   /* Set up the UDP checksum. */
+   udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
+ 
+   /* Send the datagram to the interface. */
+   ip_queue_xmit(sk, dev, skb, 1);
+ 
+   return(len);
+ }
+ 
+ 
+ static int
+ udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
+ 	   unsigned flags, struct sockaddr_in *usin, int addr_len)
+ {
+   struct sockaddr_in sin;
+   int tmp;
+ 
+   DPRINTF((DBG_UDP, "UDP: sendto: len=%d flags=0x%04X)\n", len, flags));
+   DPRINTF((DBG_UDP, "     sk=0x%X from=0x%X noblock=%d usin=0x%X alen=%d\n",
+ 			sk, from, noblock, usin, addr_len));
+ 
+   /* Check the parameters. */
+   if (len < 0) return(-EINVAL);
+   if (len == 0) return(0);
+ 
+   /* Get and verify the address. */
+   if (usin) {
+ 	if (addr_len < sizeof(sin)) return(-EINVAL);
+ 	tmp = verify_area(VERIFY_READ, usin, sizeof(sin));
+ 	if (tmp < 0) return(tmp);
+ 	memcpy_fromfs(&sin, usin, sizeof(sin));
+ 	if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL);
+ 	if (sin.sin_port == 0) return(-EINVAL);
+   } else {
+ 	if (sk->state != TCP_ESTABLISHED) return(-EINVAL);
+ 	sin.sin_family = AF_INET;
+ 	sin.sin_port = sk->dummy_th.dest;
+ 	sin.sin_addr.s_addr = sk->daddr;
+   }
+   sk->inuse = 1;
+ 
+   /* Send the packet. */
+   tmp = udp_send(sk, &sin, from, len, flags);
+ 
+   /* The datagram has been sent off. Release the socket. */
+   release_sock(sk);
+   return(tmp);
+ }
+ 
+ 
+ static int
+ udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
+ 	  unsigned flags)
+ {
+   return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
+ }
+ 
+ 
+ /*
+  * This should be easy, if there is something there we
+  * return it, otherwise we block.
+  */
+ static int
+ udp_recvfrom(struct sock *sk, unsigned char *to, int len, int noblock,
+ 	     unsigned flags, struct sockaddr_in *sin, int *addr_len)
+ {
+   struct sk_buff *skb;
+   int err, copied = 0;
+ 
+   if (len == 0) return(0);
+   if (len < 0) return(-EINVAL);
+ 
+   /* Check the buffer address. */
+   err = verify_area(VERIFY_WRITE, to, len);
+   if (err < 0) return(err);
+ 
+   /* Check the address info pointer early. */
+   if (sin) {
+ 	err = verify_area(VERIFY_WRITE, sin, sizeof(struct sockaddr_in));
+ 	if (err < 0) return(err);
+   }
+   if (addr_len) {
+ 	err = verify_area(VERIFY_WRITE, addr_len, sizeof(int));
+ 	if (err < 0) return(err);
+ 	put_fs_long(sizeof(struct sockaddr_in), (int *) addr_len);
+   }
+ 
+   /* Socket closing down? */
+   if (sk->shutdown & RCV_SHUTDOWN) return(0);
+ 
+ #if 0
+   /*
+    * This will pick up errors that occured while
+    * the program was doing something else.
+    */
+   if (sk->err) {
+ 	err = -sk->err;
+ 	sk->err = 0;
+ 	return(err);
+   }
+ #endif
+ 
+   sk->inuse = 1;
+   while(sk->rqueue == NULL) {
+ 	if (noblock) {
+ 		release_sock(sk);
+ 		return(-EAGAIN);
+ 	}
+ 	release_sock(sk);
+ 	cli();
+ 	if (sk->rqueue == NULL) {
+ 		interruptible_sleep_on(sk->sleep);
+ 		if (current->signal & ~current->blocked) {
+ 			sti();
+ 			return(-ERESTARTSYS);
+ 		}
+ 	}
+ 	sk->inuse = 1;
+ 	sti();
+   }
+   skb = sk->rqueue;
+ 
+   /* Are we only peeking into the packet queue? */
+   cli();
+   if (! (flags & MSG_PEEK)) {
+ 	if (skb->qnext == skb) {
+   		sk->rqueue = NULL;
+   	} else {
+ 		sk->rqueue = sk->rqueue->qnext;
+ 		skb->qprev->qnext = skb->qnext;
+ 		skb->qnext->qprev = skb->qprev;
+ 	}
+   }
+   sti();
+ 
+   /* Copy the current packet into user space. */
+   copied = min(len, skb->len);
+   memcpy_tofs(to, skb->raw + sizeof(struct udp), copied);
+ 
+   /* Copy the address info block to user space. */
+   if (sin) {
+ 	struct sockaddr_in addr;
+ 
+ 	addr.sin_family = AF_INET;
+ 	addr.sin_port = SKBUDP(skb)->uh_sport;
+ 	addr.sin_addr.s_addr = skb->daddr;
+ 	memcpy_tofs(sin, &addr, sizeof(struct sockaddr_in));
+   }
+ 
+   /* If we were not just peeking, free the packet. */
+   if (! (flags & MSG_PEEK)) {
+ 	inet_kfree_skb(skb, FREE_READ);
+   }
+   release_sock(sk);
+   return(copied);
+ }
+ 
+ 
+ static int
+ udp_read(struct sock *sk, unsigned char *buff, int len, int noblock,
+ 	 unsigned flags)
+ {
+   return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+ }
+ 
+ 
+ static int
+ udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
+ {
+   struct sockaddr_in sin;
+   int err;
+ 
+   if (addr_len < sizeof(sin)) return(-EINVAL);
+ 
+   err = verify_area(VERIFY_READ, usin, sizeof(struct sockaddr_in));
+   if (err < 0) return(err);
+   memcpy_fromfs(&sin, usin, sizeof(struct sockaddr_in));
+   if (sin.sin_family && sin.sin_family != AF_INET) return(-EAFNOSUPPORT);
+   sk->daddr = sin.sin_addr.s_addr;
+   sk->dummy_th.dest = sin.sin_port;
+ 
+   /* If the local address was not set, set it now. */
+   if (sk->saddr == INADDR_ANY) {
+ 	sk->saddr = in_local_addr(sk->daddr);
+ #if 0	/* FIXME: */
+ 	if (sk->saddr == INADDR_ANY) return(-ENETUNREACH);
+ #endif
+   }
+   sk->state = TCP_ESTABLISHED;
+   return(0);
+ }
+ 
+ 
+ static void
+ udp_close(struct sock *sk, int timeout)
+ {
+   sk->inuse = 1;
+   sk->state = TCP_CLOSE;
+   if (sk->dead) destroy_sock(sk);
+     else release_sock(sk);
+ }
+ 
+ 
+ /* All we need to do is get the socket, and then do a checksum. */
+ static int
+ udp_recv(struct sk_buff *skb, struct ifnet *dev, struct options *opt,
+ 	 unsigned long daddr, unsigned short len,
+ 	 unsigned long saddr, int redo, struct ipprot *protocol)
+ {
+   struct sock *sk;
+   struct udp *uh;
+   unsigned short uhlen;
+ 
+   if (! redo) {
+ 	struct ip *iph;
+ 
+ 	iph = SKBIP(skb);
+ 	skb->raw += (iph->ip_hl * sizeof(unsigned long));
+ 	len -= (iph->ip_hl * sizeof(unsigned long));
+   }
+   uh = SKBUDP(skb);
+   uhlen = ntohs(uh->uh_len);
+   sk = get_sock(&udp_prot, uh->uh_dport, saddr, uh->uh_sport, daddr);
+   if (sk == NULL) {
+ 	if (in_chk_addr(daddr) == IS_MYADDR) {
+ 		icmp_send(saddr, ICMP_UNREACH, ICMP_UNREACH_PORT,
+ 			  (struct ip *) ((unsigned char *)
+ 					(skb + 1) + dev->if_machlen));
+ 	}
+ 
+ 	/*
+ 	 * Hmm.  We got an UDP packet to a port to which we
+ 	 * don't wanna listen.  The only thing we can do now
+ 	 * is to ignore the packet... -FvK
+ 	 */
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	return(0);
+   }
+ 
+   if (! redo) {
+ 	if ((len < sizeof(struct udp)) || (uhlen > len) ||
+ 	    (uh->uh_sum && udp_check(uh, uhlen, saddr, daddr))) {
+ 		DPRINTF((DBG_UDP, "UDP: recv: bad checksum\n"));
+ 		skb->sk = NULL;
+ 		inet_kfree_skb(skb, FREE_READ);
+ 		return(0);
+ 	}
+ 	skb->sk = sk;
+ 	skb->dev = dev;
+ 	skb->len = uhlen;
+ 
+ 	/* These are supposed to be switched. */
+ 	skb->daddr = saddr;
+ 	skb->saddr = daddr;
+ 
+ 	/* If the socket is in use, queue the packet for later reading. */
+ 	cli();
+ 	if (sk->inuse) {
+ 		if (sk->back_log == NULL) {
+ 			sk->back_log = skb;
+ 			skb->qnext = skb;
+ 			skb->qprev = skb;
+ 		} else {
+ 			skb->qnext = sk->back_log;
+ 			skb->qprev = sk->back_log->qprev;
+ 			skb->qprev->qnext = skb;
+ 			skb->qnext->qprev = skb;
+ 		}
+ 		sti();
+ 		return(0);
+ 	}
+ 	sk->inuse = 1;
+ 	sti();
+   }
+ 
+   /* Charge it to the socket. */
+   if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
+ 	skb->sk = NULL;
+ 	inet_kfree_skb(skb, FREE_READ);
+ 	release_sock(sk);
+ 	return(0);
+   }
+   sk->rmem_alloc += skb->mem_len;
+ 
+   /* At this point we should print the thing out. */
+   DPRINTF((DBG_UDP, "<<\n"));
+   print_udp(uh);
+ 
+   /* Now add it to the data chain and wake things up. */
+   if (sk->rqueue == NULL) {
+ 	sk->rqueue = skb;
+ 	skb->qnext = skb;
+ 	skb->qprev = skb;
+   } else {
+ 	skb->qnext = sk->rqueue;
+ 	skb->qprev = sk->rqueue->qprev;
+ 	skb->qprev->qnext = skb;
+ 	skb->qnext->qprev = skb;
+   }
+   skb->len = uhlen - sizeof(struct udp);
+ 
+   if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 
+   release_sock(sk);
+   return(0);
+ }
+ 
+ 
+ int
+ udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+ {
+   struct sk_buff *skb;
+   unsigned long amount;
+   int err;
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		return(inet_dbg_ioctl(arg, DBG_UDP));
+ 
+ 	case TIOCOUTQ:
+ 		if (sk->state == TCP_LISTEN) return(-EINVAL);
+ 		amount = sk->prot->wspace(sk) / 2;
+ 		err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		put_fs_long(amount, (int *) arg);
+ 		return(0);
+ 
+ 	case TIOCINQ:	/* == FIONREAD */
+ 		if (sk->state == TCP_LISTEN) return(-EINVAL);
+ 		amount = 0;
+ 		skb = sk->rqueue;
+ 		if (skb != NULL) {
+ 			/*
+ 			 * We will only return the amount
+ 			 * of this packet since that is all
+ 			 * that will be read.
+ 			 */
+ 			amount = skb->len;
+ 		}
+ 		err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		put_fs_long(amount, (int *) arg);
+ 		return(0);
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+   return(0);
+ }
+ 
+ 
+ struct proto udp_prot = {
+   sock_wmalloc,		sock_rmalloc,		sock_wfree,
+   sock_rfree,		sock_rspace,		sock_wspace,
+   udp_close,		udp_read,		udp_write,
+   udp_sendto,		udp_recvfrom,		udp_connect,
+   NULL,			NULL,			NULL,
+   udp_recv,		udp_select,		udp_ioctl,
+   NULL,			NULL,			NULL,
+   ip_setsockopt,	ip_getsockopt,		128,
+   0,			{NULL,},		"UDP"
+ };
+ 
+ 
+ static struct ipprot udp_protocol = {
+   "UDP",
+   IPPROTO_UDP,		/* protocol ID		*/
+   0,			/* copy			*/
+   NULL,			/* data			*/
+   udp_recv,		/* UDP handler		*/
+   udp_err,		/* UDP error control	*/
+   NULL			/* next			*/
+ };
+ 
+ 
+ #if 0
+ int
+ udp_devread(char *buf, int count)
+ {
+   int i;
+ 
+   DPRINTF((DBG_UDP, "UDP: devread(%d, 0x%lX, %d)\n", (long) buf, count));
+ 
+   /* Verify addresses. */
+   i = verify_area(VERIFY_WRITE, (void *) buf, count);
+   if (i < 0) return(i);
+ 
+   return(inet_getinfo(&udp_prot, buf, count, 1));
+ }
+ #endif
+ 
+ 
+ void
+ udp_proto_init(void)
+ {
+   int i;
+ 
+   /* Register this protocol with IP. */
+   (void) inet_add_proto(&udp_protocol);
+ 
+   /* Clear all UDP sockets. */
+   for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
+ 	udp_prot.sock_array[i] = NULL;
+   }
+ }
*** original/net/inet/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/inet/utils.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,524 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Various kernel-resident INET utility functions; mainly
+  *		for format conversion and debugging output.
+  *
+  * Version:	@(#)utils.c	1.1.19	02/03/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include "global.h"
+ #include "sock.h"
+ #include "route.h"
+ #include <stdarg.h>
+ 
+ 
+ /* This *should* be in <linux/in.h> -FvK */
+ #ifndef IN_CLASSD_NET
+ #   define IN_CLASSD_NET	IN_CLASSC_NET
+ #endif
+ 
+ 
+ int inet_debug = 0;			/* INET domain debug flag	*/
+ 
+ 
+ #ifdef	INET_DEBUG
+ void
+ inet_dprintf(int level, char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if ((inet_debug & (1 << level)) == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* INET_DEBUG */
+ 
+ 
+ /* Display a DARPA Internet address in readable format. Needed by ARP. */
+ char *
+ in_print(unsigned char *ptr)
+ {
+   static char buff[18];
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (ptr == NULL) return("[NONE]");
+   sprintf(buff, "%d.%d.%d.%d",
+ 	(ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255), (ptr[3] & 255));
+   return(buff);
+ }
+ 
+ 
+ /* Display a DARPA Internet address. Needed by ARP. */
+ char *
+ in_sprint(struct sockaddr *sap)
+ {
+   static char buff[24];
+   unsigned char *ptr;
+   extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+   if (sap == NULL) return("[NONE]");
+   ptr = (unsigned char *) &SA_ADDR(*sap);
+   sprintf(buff, "%d:%d.%d.%d.%d", SA_FAMILY(*sap),
+ 	(ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255), (ptr[3] & 255));
+   return(buff);
+ }
+ 
+ 
+ /* Display an IP address in readable format. */
+ char *
+ in_ntoa(unsigned long in)
+ {
+   return(in_print((unsigned char *) &in));
+ }
+ 
+ 
+ /* Determine a default network mask. */
+ unsigned long
+ in_getmask(unsigned long dst, struct ifnet *dev, struct ifaddr *ifa)
+ {
+   unsigned long dflt_mask;
+   unsigned long dev_addr;
+   unsigned long dev_mask;
+ 
+   /* Check for the default route address first. */
+   if (dst == INADDR_ANY) {
+ 	dflt_mask = 0;
+ 	return(dflt_mask);
+   }
+ 
+   /* Convert to host order. */
+   dst = ntohl(dst);
+ 
+   /* Determine the standard Internet network mask. */
+   if (IN_CLASSD(dst)) dflt_mask = IN_CLASSD_NET;
+     else if (IN_CLASSC(dst)) dflt_mask = IN_CLASSC_NET;
+     else if (IN_CLASSB(dst)) dflt_mask = IN_CLASSB_NET;
+     else if (IN_CLASSA(dst)) dflt_mask = IN_CLASSA_NET;
+     else dflt_mask = 0L;
+   DPRINTF((DBG_INET, "INET: getmask: dst %s", in_ntoa(htonl(dst))));
+   DPRINTF((DBG_INET, " default %s ", in_ntoa(htonl(dflt_mask))));
+ 
+   /* Can we check on subnets? */
+   if (dev == NULL && ifa == NULL) {
+ 	DPRINTF((DBG_INET, "--> dev=NULL\n"));
+ 	return(htonl(dflt_mask));
+   }
+ 
+   /* We'd better have an INET address here! */
+   if (ifa == NULL) ifa = dev_get_addr(dev, AF_INET);
+   dev_addr = ntohl(IN_IFADDR(ifa));
+   DPRINTF((DBG_INET, "dev_addr %s", in_ntoa(htonl(dev_addr))));
+   dev_mask = ntohl(IN_IFMASK(ifa));
+   DPRINTF((DBG_INET, " dev_mask %s ", in_ntoa(htonl(dev_mask))));
+ 
+   /* Is the destination a subnet of the local net ? */
+   if (dst & ~dflt_mask) {	/* subnetwork addr */
+ 	if ((dst & dflt_mask) == (dev_addr & dflt_mask)) {
+ 		DPRINTF((DBG_INET, "--> %s\n", in_ntoa(htonl(dev_mask))));
+ 		return(htonl(dev_mask));	/* inside our own network */
+ 	} else {
+ 		DPRINTF((DBG_INET, "--> illegal, assuming %s\n",
+ 						in_ntoa(htonl(dflt_mask))));
+ 		return(htonl(dflt_mask));
+ 		/* Illegal, as we can't derive the proper netmask. */
+ 	}
+   }
+ 
+   DPRINTF((DBG_RT, "--> %s\n", in_ntoa(htonl(dflt_mask))));
+   return(htonl(dflt_mask));
+ }
+ 
+ 
+ /* Check the address for our address, broadcasts, etc. */
+ int
+ in_chk_addr_ifa(unsigned long addr, struct ifnet *dev, struct ifaddr *ifa)
+ {
+   unsigned long dst;
+   unsigned long mask;
+ 
+   if (dev == NULL || dev->if_name == NULL || ifa == NULL) {
+ 	DPRINTF((DBG_INET, "INET: chk_addr_ifa(NULL)\n"));
+ 	return(0);
+   }
+   DPRINTF((DBG_INET, "INET: chk_addr_dev(%s, \"%s\") --> ",
+ 				in_ntoa(addr), dev->if_name));
+ 
+   /* Accept both `all ones' and `all zeros' as BROADCAST. */
+   if ((addr == INADDR_ANY) || (addr == INADDR_BROADCAST)) {
+ 	DPRINTF((DBG_INET, "STDBROADCAST\n"));
+ 	return(IS_BROADCAST);
+   }
+ 
+   /* Is it the exact IP address? */
+   if (IN_IFADDR(ifa) == addr) {
+ 	DPRINTF((DBG_INET, "MYADDR(%s)\n", dev->if_name));
+ 	return(IS_MYADDR);
+   }
+ 
+   /* Is it the set BROADCAST address? */
+   if ((dev->if_flags & IFF_BROADCAST) && (IN_IFBRDADDR(ifa) == addr)) {
+ 	DPRINTF((DBG_INET, "BROADCAST(%s)\n", dev->if_name));
+ 	return(IS_BROADCAST);
+   }
+ 
+   /* Accept all of the `loopback' class A net, only from a loopback device. */
+   dst = ntohl(addr);
+   if ((dev->if_flags & IFF_LOOPBACK) && (dst & IN_CLASSA_NET) == 0x7F000000L) {
+ 	DPRINTF((DBG_INET, "LOOPBACK\n"));
+         return(IS_MYADDR);
+   }
+  
+   /* Check for simple network broadcasts. */
+   mask = in_getmask(addr, NULL, ifa); 
+   if ((addr & ~mask) == (INADDR_BROADCAST & ~mask)) {
+ 	DPRINTF((DBG_INET, "NETBROADCAST\n"));
+ 	return(IS_BROADCAST);
+   }
+ 
+   /* Nope. Check for a subnetwork broadcast. */
+   if ((addr & IN_IFMASK(ifa)) == (IN_IFADDR(ifa) & IN_IFMASK(ifa))) {
+ 	if ((addr & ~IN_IFMASK(ifa)) == 0L) {
+ 		DPRINTF((DBG_INET, "SUBBROADCAST-0\n"));
+ 		return(IS_BROADCAST);
+ 	}
+ 	if (((addr & ~IN_IFMASK(ifa)) | IN_IFMASK(ifa)) == INADDR_BROADCAST) {
+ 		DPRINTF((DBG_INET, "SUBBROADCAST-1\n"));
+ 		return(IS_BROADCAST);
+ 	}
+   }
+ 
+   if (mask != IN_IFMASK(ifa)) {
+ 	if ((addr & mask) == (IN_IFADDR(ifa) & mask)) {
+ 		if ((addr & ~mask) == 0L) {
+ 			DPRINTF((DBG_INET, "SUBBROADCAST-0\n"));
+ 			return(IS_BROADCAST);
+ 		}
+ 		if (((addr & ~mask) | mask) == INADDR_BROADCAST) {
+ 			DPRINTF((DBG_INET, "SUBBROADCAST-1\n"));
+ 			return(IS_BROADCAST);
+ 		} 
+ 	}
+   }
+ 
+   /*
+    * If the interface address is set to all zeroes accept anything on
+    * this interface, as it this allows the BOOTP client to work...
+    */
+   if (IN_IFADDR(ifa) == INADDR_ANY) {
+ 	DPRINTF((DBG_INET, "INADDR_ANY\n"));
+ 	return(IS_MYADDR);
+   }
+ 
+   /* No match at all. */
+   DPRINTF((DBG_INET, "NONE\n"));
+   return(0);
+ }
+ 
+ 
+ int
+ in_chk_addr(unsigned long addr)
+ {
+   struct ifnet *dev;
+   struct ifaddr *ifa;
+   int res;
+ 
+   DPRINTF((DBG_INET, "INET: chk_addr(%s)\n", in_ntoa(addr)));
+ 
+   /* Check the interface addresses. */
+   for (dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & IFF_UP) == 0) continue;
+ 	if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+ 	res = in_chk_addr_ifa(addr, dev, ifa);
+ 	if (res) return(res);
+   }
+ 
+   /* No match at all. */
+   return(0);
+ }
+ 
+ 
+ /*
+  * Return the "primary" interface, i.e. the first one
+  * we find to be UP and not LOOPBACK.  This is needed
+  * by the "metric 0" routing hack.
+  */
+ struct ifnet *
+ in_dev_primary(void)
+ {
+   struct ifnet *dev;
+ 
+   /* Check the interface addresses. */
+   for (dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & IFF_UP) == 0) continue;
+ 	if (dev->if_flags & IFF_LOOPBACK) continue;
+ 	return(dev);
+   }
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * IP needs a source address for the given target.
+  * This means that we have to ask the router which interface
+  * it thinks we should use for the given target address. If
+  * the interface returned is the LOOPBACK device, we have to
+  * do an additional check for the real address of the inter-
+  * face to which the address belongs.
+  */
+ unsigned long
+ in_local_addr(unsigned long dst)
+ {
+   struct ifnet *dev;
+   struct ifaddr *ifa;
+   struct rtable *rt;
+ 
+   DPRINTF((DBG_INET, "INET: in_local_addr(%s)\n", in_ntoa(dst)));
+ 
+   /* First, ask the IP router which interface we should use. */
+   rt = inet_route(dst, NULL);
+   if (rt == NULL) {
+ 	DPRINTF((DBG_INET, "INET: No route for %s !\n", in_ntoa(dst)));
+ 	return(INADDR_ANY);
+   }
+ 
+   /* Check for the LOOPBACK device. */
+   if (rt->rt_ifp->if_flags & IFF_LOOPBACK) {
+ 	/*
+ 	 * Loopback device is used, so we are talking to one of
+ 	 * our own addresses.  However, we cannot blindly use the
+ 	 * address of the interface the router is giving us, as
+ 	 * this is always LOCALHOST.  We need to find out which
+ 	 * interface we seem to be talking to, and use that address
+ 	 * instead.  Usually, this is the same as the 'dst' argument
+ 	 * we are checking on.
+ 	 */
+ 	for (dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 		if ((dev->if_flags & IFF_UP) == 0) continue;
+ 		if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+ 
+ 		/* Check if this address is on the correct network. */
+ 		if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+         	if ((IN_IFADDR(ifa) & IN_IFMASK(ifa)) ==
+ 		    (dst & IN_IFMASK(ifa))) return(IN_IFADDR(ifa));
+ 	}
+ 	printk("INET: Huh?  Route, but no iface for %s??\n", in_ntoa(dst));
+ 	return(INADDR_ANY);
+   }
+ 
+   /* The normal case- we are talking to any other address. */
+   ifa = dev_get_addr(rt->rt_ifp, AF_INET);
+   if (ifa == NULL) {
+ 	printk("INET: Huh?  Route to non-INET iface ??\n");
+ 	return(INADDR_ANY);
+   }
+   return(IN_IFADDR(ifa));
+ }
+ 
+ 
+ /*
+  * Find an interface that can handle addresses for a certain
+  * address.  The lookups are done in a specific order:
+  *
+  * - First, check if the target is an exact match of an interface
+  *   "pointopoint" address.  This is an important rule, as many
+  *   people have an ethernet at home, with an occasional SLIP link
+  *   to the outside world.  The local end of the SLIP link is often
+  *   an address on the local net (i.e. an address also possible on
+  *   the Ethernet), so we have to check any point-to-point lines
+  *   first.
+  *
+  * - Second, we check if the target is an exact match of an inter-
+  *   face address.  This also has precedence over any netmasked
+  *   matches.
+  *
+  * - Third, try to match the target with the interface pointopoint
+  *   address, using the network mask of that interface.
+  *
+  * - Finally, match the target with the netmasked interface address.
+  */
+ struct ifnet *
+ in_dev_check(unsigned long addr)
+ {
+   struct ifnet *dev;
+   struct ifaddr *ifa;
+ 
+   /* Rule 1: check pointopoint addresses. */
+   for(dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & (IFF_UP | IFF_POINTOPOINT)) !=
+ 				(IFF_UP | IFF_POINTOPOINT)) continue;
+ 	if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+ 
+ 	if (addr == IN_IFDSTADDR(ifa)) return(dev);
+   }
+ 
+   /* Rule 2: check interface addresses. */
+   for(dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & IFF_UP) == 0) continue;
+ 	if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+ 
+ 	if (addr == IN_IFADDR(ifa)) return(dev);
+   }
+ 
+   /* Rule 3: check netmasked p-p addresses. */
+   for(dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & (IFF_UP | IFF_POINTOPOINT)) !=
+ 				(IFF_UP | IFF_POINTOPOINT)) continue;
+ 	if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+ 
+ 	if ((IN_IFDSTADDR(ifa) & IN_IFMASK(ifa)) ==
+ 	    (addr & IN_IFMASK(ifa))) return(dev);
+   }
+ 
+   /* Rule 4: check netmasked iface addresses. */
+   for(dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & IFF_UP) == 0) continue;
+ 	if ((ifa = dev_get_addr(dev, AF_INET)) == NULL) continue;
+ 
+ 	if ((IN_IFADDR(ifa) & IN_IFMASK(ifa)) ==
+ 	    (addr & IN_IFMASK(ifa))) return(dev);
+   }
+ 
+   /* Alas, no interface can handle this address... */
+   return(NULL);
+ }
+ 
+ 
+ /* Check a DARPA Internet address if it's one of our own. Needed by ARP. */
+ int
+ in_check(struct sockaddr *sap)
+ {
+   if (sap == NULL) return(0);
+   return(in_chk_addr(SA_ADDR(*sap)));
+ }
+ 
+ 
+ /* Compare the two DARPA Internet addresses.  Needed by ARP. */
+ int
+ in_compare(struct sockaddr *one, struct sockaddr *two)
+ {
+   if ((SA_FAMILY(*one) == SA_FAMILY(*two)) &&
+       (SA_ADDR(*one)   == SA_ADDR(*two))) return(1);
+ 
+   return(0);
+ }
+ 
+ 
+ /* Make a socket address.  Needed by ARP. */
+ void
+ in_mksock(struct sockaddr *sap, unsigned char *ptr)
+ {
+   SA_FAMILY(*sap) = AF_INET;
+   SA_ADDR(*sap) = *((unsigned long *) ptr);
+ }
+ 
+ 
+ /* Get the bare address from a socket address.  Needed by ARP. */
+ unsigned char *
+ in_unsock(struct sockaddr *sap)
+ {
+   return((unsigned char *) &SA_ADDR(*sap));
+ }
+ 
+ 
+ int
+ inet_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   if (!suser()) return(-EPERM);
+   val = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+   if (val < 0) return(val);
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		inet_debug = 0;
+ 		break;
+ 
+ 	default:
+ 		if (val > 0 && val < 33) {
+ 			inet_debug |= (1 << val);
+ 			return(0);
+ 		}
+ 		if (val >= 128) {
+ 			inet_debug &= ~(1 << (val - 128));
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ void
+ inet_kfree_skb_sk(struct sock *sk, struct sk_buff *skb, int rw)
+ {
+   if (skb == NULL) {
+ 	printk("INET: kfree_skb_sk(NULL)\n");
+ 	return;
+   }
+ 
+   /* If this packet was not attached to a socket, just nuke it. */
+   if (sk == NULL) {
+ 	skb_release(skb);
+ 	return;
+   }
+ 
+   cli();
+   switch(rw) {
+ 	case FREE_READ:
+ 		sk->rmem_alloc -= skb->mem_len;
+ 		skb_release(skb);
+ 		break;
+ 
+ 	case FREE_WRITE:
+ 		sk->wmem_alloc -= skb->mem_len;
+ 		if (skb->lock) skb->free = 1;
+ 		  else skb_free(skb);
+ 		if (! sk->dead) wake_up_interruptible(sk->sleep);
+ 		break;
+ 
+ 	default:
+ 		printk("INET: weird opcode %d in kfree_skb_sk(0x%X)\n",
+ 							rw, (int) skb);
+ 		sk->rmem_alloc -= skb->mem_len;
+ 		skb_release(skb);
+ 		break;
+   }
+ 
+   if (sk->destroy && sk->rmem_alloc == 00 && sk->wmem_alloc == 0) {
+ 	inet_delete_timer(sk);
+ 	kfree_s(sk, sizeof(struct sock));
+   }
+ 
+   sti();
+ }
+ 
+ 
+ void
+ inet_kfree_skb(struct sk_buff *skb, int rw)
+ {
+   if (skb == NULL) {
+ 	printk("INET: kfree_skb(NULL) !\n");
+ 	return;
+   }
+ 
+   inet_kfree_skb_sk(skb->sk, skb, rw);
+ }
*** original/net/route/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/route/Makefile	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,38 ----
+ #
+ # Makefile for the Linux ROUTE module.
+ #
+ # 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 definition is now in the main makefile...
+ 
+ .c.o:
+ 	$(CC) $(CFLAGS) -c -o $*.o $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ .c.s:
+ 	$(CC) $(CFLAGS) -S -o $*.s $<
+ 
+ 
+ OBJS	= main.o family.o utils.o ioctl.o table.o
+ 
+ 
+ route.o:	$(OBJS)
+ 		$(LD) -r -o route.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.s *.a tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.s *.a tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/route/family.c	Thu Jan  1 01:00:00 1970
--- linux/net/route/family.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,61 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		ROUTE address family handling functions.
+  *
+  * Version:	@(#)family.c	1.2.3	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/route.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "route.h"
+ 
+ 
+ static struct route_aftype *route_aftypes = NULL;
+ 
+ 
+ /* Find an addess (protocol) family type. */
+ struct route_aftype *
+ route_aftype(int af)
+ {
+   struct route_aftype *ap;
+ 
+   for (ap = route_aftypes; ap != NULL; ap = ap->next) {
+ 	if (ap->af == af) return(ap);
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Add a new Address Family handler to the list. */
+ int
+ route_add_af(struct route_aftype *af)
+ {
+   cli();
+   af->next = route_aftypes;
+   route_aftypes = af;
+   sti();
+   return(0);
+ }
*** original/net/route/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/route/ioctl.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,111 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		ROUTE I/O control handling functions.
+  *
+  * Version:	@(#)ioctl.c	1.2.4	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/route.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "route.h"
+ 
+ 
+ static int
+ route_add(struct rtentry *rt)
+ {
+   struct route_aftype *ap;
+ 
+   DPRINTF(("ROUTE: add() called.\n"));
+ 
+   /* See if we know about this address family... */
+   if ((ap = route_aftype(rt->rt_dst.sa_family)) == NULL) {
+ 	DPRINTF(("ROUTE: add: unsupported address family %d\n",
+ 						rt->rt_dst.sa_family));
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Call the address family handler for the real work. */
+   return(ap->add(rt));
+ }
+ 
+ 
+ static int
+ route_del(struct rtentry *rt)
+ {
+   struct route_aftype *ap;
+ 
+   DPRINTF(("ROUTE: del() called.\n"));
+ 
+   /* See if we know about this address family... */
+   if ((ap = route_aftype(rt->rt_dst.sa_family)) == NULL) {
+ 	DPRINTF(("ROUTE: del: unsupported address family %d\n",
+ 						rt->rt_dst.sa_family));
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Call the address family handler for the real work. */
+   return(ap->del(rt));
+ }
+ 
+ 
+ /* Handle a ROUTE layer I/O control request. */
+ int
+ route_ioctl(struct inode *inode, struct file *file,
+ 	    unsigned int cmd, unsigned long arg)
+ {
+   struct rtentry rt;
+   int err;
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (!suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(route_dbg_ioctl(arg, 1));
+ 
+ 	case SIOCADDRT:
+ 		if (!suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ,(char *) arg,
+ 				  sizeof(struct rtentry));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&rt, (char *) arg, sizeof(struct rtentry));
+ 		return(route_add(&rt));
+ 
+ 	case SIOCDELRT:
+ 		if (!suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (char *) arg,
+ 				  sizeof(struct rtentry));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(&rt, (char *) arg, sizeof(struct rtentry));
+ 		return(route_del(&rt));
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+ 
+   /*NOTREACHED*/
+   return(0);
+ }
*** original/net/route/main.c	Thu Jan  1 01:00:00 1970
--- linux/net/route/main.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,53 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		ROUTE main functions.
+  *
+  * Version:	@(#)main.c	1.2.3	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/route.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include "route.h"
+ 
+ 
+ /* Initialize the ROUTE module. */
+ int
+ route_init(struct ddi *ddi)
+ {
+ #ifdef NETDBG
+   printk("%s: device %d/%d: ", ddi->name, ddi->major, ddi->minor);
+ #endif
+ 
+   /* Tell SOCKET about our minor device. */
+   if (ddi->minor) {
+ 	(void) net_add_minor(ddi->minor, route_ioctl);;
+   }
+ 
+ #ifdef NETDBG
+   printk("OK\n");
+ #endif
+   return(0);
+ }
*** original/net/route/route.h	Thu Jan  1 01:00:00 1970
--- linux/net/route/route.h	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,44 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		ROUTE various definitions.
+  *
+  * Version:	@(#)route.h	1.2.1	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _ROUTE_H
+ #define _ROUTE_H
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	ROUTE_DEBUG
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  ROUTE_DEBUG
+ #   define ROUTE_DEBUG
+ #endif
+ 
+ #ifdef	ROUTE_DEBUG
+ #   define DPRINTF(x)	route_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ extern int		route_init(struct ddi *ddi);
+ 
+ #endif	/* _ROUTE_H */
*** original/net/route/table.c	Thu Jan  1 01:00:00 1970
--- linux/net/route/table.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,113 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		ROUTE routing table handling functions.
+  *
+  * Version:	@(#)table.c	1.2.8	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Florian La Roche, <flla@stud.uni-sb.de>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/route.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <stdarg.h>
+ #include "route.h"
+ 
+ 
+ struct rtable *route_base = NULL;		/* the routing table	*/
+ 
+ 
+ /* Remove all routing table entries for a device. */
+ void
+ route_flush(struct ifnet *dev)
+ {
+   struct rtable *x, **r = &route_base;
+ 
+   DPRINTF(("NET: route_flush: flushing for dev 0x%X (%s)\n",
+ 					(long)dev, dev->if_name));
+   cli();
+   while((x = *r) != NULL) {
+ 	if (x->rt_ifp == dev) {
+ 		*r = x->rt_next;
+ 		kfree_s(x, sizeof(struct rtable));
+ 	} else {
+ 		r = &x->rt_next;
+ 	}
+   }
+   sti();
+ }
+ 
+ 
+ /* Called from the PROCfs module. */
+ struct pagelist *
+ route_get_info(void)
+ {
+   struct rtable *r;
+   char *pos;
+   struct pagelist *fp, *pp;
+ 
+   fp = (struct pagelist *) __get_free_page(GFP_KERNEL);
+   if (fp == NULL) return(NULL);
+   pp = fp;
+   pp->page = (char*) __get_free_page(GFP_KERNEL);
+   if (pp->page == NULL) {
+ 	free_page((unsigned long) pp);
+ 	return(NULL);
+   }
+   pp->next = NULL;
+   pp->prev = NULL;
+   pos = pp->page;
+   for (r = route_base; r != NULL; r = r->rt_next) {
+ 	if (pos >= (pp->page + (PAGE_SIZE - 90))) {
+ 		pp->length = (long)pos - (long)pp->page;
+ 		pp->next = (struct pagelist *) __get_free_page(GFP_KERNEL);
+ 		if (pp->next == NULL) {
+ 			DPRINTF(("NET: route_get_info: out of memory !\n"));
+ 			return(fp);
+ 		}
+ 		pp->next->prev = pp;
+ 		pp = pp->next;
+ 		pp->next = NULL;
+ 		pp->page = (char *) __get_free_page(GFP_KERNEL);
+ 		if (pp->page == NULL) {
+ 			DPRINTF(("NET: route_get_info: out of memory !\n"));
+ 			pp->prev->next = NULL;
+ 			free_page((unsigned long) pp);
+ 			return(fp);
+ 		}
+ 		pos = pp->page;
+ 	}
+         pos += sprintf(pos, "%8.8s ", r->rt_ifp->if_name);
+ 	pos += sprintf(pos, "%s ", route_sprint(&r->rt_dst));
+ 	pos += sprintf(pos, "%s ", route_sprint(&r->rt_gateway));
+ 	pos += sprintf(pos, "%s ", route_sprint(&r->rt_genmask));
+ 	pos += sprintf(pos, "%02X %3d %8d %5d\n",
+ 		r->rt_flags, r->rt_metric, r->rt_refcnt, r->rt_use);
+   }
+   pp->length = (long)pos - (long)pp->page;
+   return(fp);
+ }
*** original/net/route/table.h	Thu Jan  1 01:00:00 1970
--- linux/net/route/table.h	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,49 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		Definitions for the IP router.
+  *
+  * Version:	@(#)table.h	1.2.1	12/17/93
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		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.
+  */
+ #ifndef _INET_ROUTE_TABLE_H
+ #define _INET_ROUTE_TABLE_H
+ 
+ 
+ #include <linux/route.h>
+ 
+ 
+ /* This is an entry in the IP routing table. */
+ struct rtable {
+   struct rtable		*rt_next;
+   unsigned long		rt_dst;
+   unsigned long		rt_gateway;
+   u_char		rt_flags;
+   u_char		rt_metric;
+   short			rt_refcnt;
+   u_long		rt_use;
+   struct ifnet		*rt_ifp;
+ };
+ 
+ 
+ extern void		rt_flush(struct ifnet *dev);
+ extern void		rt_add(short flags, unsigned long addr,
+ 			       unsigned long gw, struct ifnet *dev);
+ extern struct rtable	*rt_route(unsigned long daddr, struct options *opt);
+ extern int		rt_get_info(char * buffer);
+ extern int		rt_ioctl(unsigned int cmd, unsigned long arg);
+ 
+ #endif	/* _INET_ROUTE_TABLE_H */
*** original/net/route/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/route/utils.c	Mon May  9 13:13:14 1994
***************
*** 0 ****
--- 1,98 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module implements the top-level part of the routing
+  *		table.  All actual work is done by the protocol-dependent
+  *		routing functions...
+  *
+  *		ROUTE various utility functions.
+  *
+  * Version:	@(#)utils.c	1.2.2	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/types.h>
+ #include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include <linux/route.h>
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <stdarg.h>
+ #include "route.h"
+ 
+ 
+ extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+ 
+ static int route_debug = 0;			/* ROUTE debug flag	*/
+ 
+ 
+ char *
+ route_sprint(struct sockaddr *sap)
+ {
+   static char buff[64];
+   char *bufp = buff;
+   int i;
+ 
+   if (sap == NULL) return("[NULL]");
+   for (i = 0; i < sizeof(struct sockaddr); i++)
+ 	bufp += sprintf(bufp, "%02X", ((unsigned char *) sap)[i] & 0377);
+   return(buff);
+ }
+ 
+ 
+ #ifdef ROUTE_DEBUG
+ void
+ route_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+ 
+   if (route_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* ROUTE_DEBUG */
+ 
+ 
+ int
+ route_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		route_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON */
+ 		route_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			route_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/socket/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/socket/Makefile	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,39 ----
+ #
+ # Makefile for the Linux NET-3 networking layer.
+ #
+ # 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 definition is now in the main makefile...
+ 
+ OBJS	= sock.o family.o skbuff.o ddi.o dev.o utils.o \
+ 	  ioctl.o proto.o eth.o packet.o
+ 
+ 
+ .c.o:
+ 	$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ .c.s:
+ 	$(CC) $(CFLAGS) -S $<
+ 
+ 
+ socket.o:	$(OBJS)
+ 		$(LD) -r -o socket.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.a *.s tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.a *.s tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/socket/ddi.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/ddi.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,61 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		Implement the Device Driver Interface (DDI) routines.
+  *		Currently, this is only used by the NET layer of LINUX,
+  *		but it eventually might move to an upper directory of
+  *		the system.
+  *
+  * Version:	@(#)ddi.c	1.1.0	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include "socket.h"
+ 
+ 
+ extern struct ddi	protocols[];	/* network protocols		*/
+ extern struct ddi	devices[];	/* device driver map		*/
+ 
+ 
+ /*
+  * This is the function that is called by a kernel routine during
+  * system startup.  Its purpose is to walk trough the "devices"
+  * table (defined above), and to call all moduled defined in it.
+  */
+ void
+ ddi_init(void)
+ {
+   struct ddi *ddi;
+ 
+   /* First off, kick all configured protocols. */
+   ddi = protocols;
+   while (ddi->name != NULL) {
+ 	(void) (*ddi->init)(ddi);
+ 	ddi++;
+   }
+   
+   /* Done.  Now kick all configured device drivers. */
+   ddi = devices;
+   while (ddi->name != NULL) {
+ 	(void) (*ddi->init)(ddi);
+ 	ddi++;
+   }
+ 
+   /* We're all done... */
+ }
*** original/net/socket/dev.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/dev.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,242 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module handles the generic part of the NET interface
+  *		management functions and data.  This means creating new
+  *		interfaces, looking them up by their name, and more of
+  *		that crap.
+  *
+  * Version:	@(#)dev.c	1.2.5	03/30/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>	
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/route.h>
+ #include "socket.h"
+ 
+ 
+ struct ifnet *dev_base = NULL;
+ 
+ 
+ /*
+  * Create a new interface descriptor, and fill in its base
+  * fields with sane values.  This function is called by device
+  * drivers, once for every unit they wish to install.
+  */
+ struct ifnet *
+ dev_create(char *name)
+ {
+   struct ifnet *dev;
+   struct ifnet *ptr;
+ 
+   dev = (struct ifnet *) kmalloc(sizeof(struct ifnet), GFP_KERNEL);
+   if (dev == NULL) {
+ 	DPRINTF((DBG_DEV, "NET: dev_create: no memory for new interface !\n"));
+ 	return(NULL);
+   }
+   memset(dev, 0, sizeof(struct ifnet));
+ 
+   /* Allocate buffers for the "name" and "stats" fields. */
+   dev->if_name = (char *) kmalloc(IFNAMSIZ + 1, GFP_KERNEL);
+   if (dev->if_name == NULL) {
+ 	DPRINTF((DBG_DEV,
+ 		"NET: dev_create: no memory for interface name !\n"));
+ 	kfree_s(dev, sizeof(struct ifnet));
+ 	return(NULL);
+   }
+   if (name != NULL) strncpy(dev->if_name, name, IFNAMSIZ);
+     else memset(dev->if_name, 0, IFNAMSIZ + 1);
+ 
+   dev->if_stats =
+ 	(struct dev_stats *) kmalloc(sizeof(struct dev_stats), GFP_KERNEL);
+   if (dev->if_stats == NULL) {
+ 	DPRINTF((DBG_DEV,
+ 		"NET: dev_create: no memory for interface statistics !\n"));
+ 	if (dev->if_name != NULL)
+ 		kfree_s(dev->if_name, strlen(dev->if_name) + 1);
+ 	kfree_s(dev, sizeof(struct ifnet));
+ 	return(NULL);
+   }
+   memset(dev->if_stats, 0, sizeof(struct dev_stats));
+ 
+   /* Allocate and clear the "MAC address" block. */
+   dev->if_macaddr = (struct ifaddr *)kmalloc(sizeof(struct ifaddr), GFP_KERNEL);
+   if (dev->if_macaddr == NULL) return(NULL);
+   memset(dev->if_macaddr, 0, sizeof(struct ifaddr));
+ 
+   /* Stuff into the linked list. */
+   cli();
+   if (dev_base != NULL) {
+ 	ptr = dev_base;
+ 	while (ptr->if_next != NULL) ptr = ptr->if_next;
+ 	ptr->if_next = dev;
+   } else dev_base = dev;
+   dev->if_next = NULL;
+   sti();
+ 
+   return(dev);
+ }
+ 
+ 
+ /* Free a previously allocated interface. */
+ int
+ dev_free(struct ifnet *dev)
+ {
+   struct ifnet *prev, *ptr;
+ 
+   if (dev == NULL) return(0);
+ 
+   /* Free any allocated memory. */
+   if (dev->if_name != NULL) kfree(dev->if_name);
+   if (dev->if_stats != NULL) kfree(dev->if_stats);
+ 
+   /* Remove from linked list. */
+   cli();
+   prev = NULL;
+   ptr = dev_base;
+   while (ptr != NULL) {
+ 	if (ptr == dev) {
+ 		if (prev != NULL) prev->if_next = ptr->if_next;
+ 		  else dev_base = ptr->if_next;
+ 		sti();
+ 		kfree(dev);
+ 		return(0);
+ 	}
+ 	prev = ptr;
+ 	ptr = ptr->if_next;
+   }
+   sti();
+ 
+   /* Not found... */
+   return(-ESRCH);
+ }
+ 
+ 
+ /* Find an address in the interface address list. */
+ struct ifaddr *
+ dev_get_addr(struct ifnet *dev, int af)
+ {
+   struct ifaddr *ifa;
+ 
+   DPRINTF((DBG_DEV, "NET: dev_get_addr: dev=0x%X af=%d ifa=0x%X\n",
+ 					dev, af, dev->if_addr));
+   if (dev == NULL || dev->if_addr == NULL) return(NULL);
+ 
+   DPRINTF((DBG_DEV, "     lookup start: "));
+   ifa = dev->if_addr;
+   while (ifa != NULL) {
+ 	if (ifa->ifa_addr.sa_family == af) {
+ 		DPRINTF((DBG_DEV, "found 0x%X\n", ifa));
+ 		return(ifa);
+ 	}
+ 	ifa = ifa->ifa_next;
+   }
+   DPRINTF((DBG_DEV, "NONE\n"));
+   return(NULL);
+ }
+ 
+ 
+ /* Add a protocol address to an interface. */
+ struct ifaddr *
+ dev_add_addr(struct ifnet *dev, int af)
+ {
+   struct ifaddr *ifa;
+ 
+   DPRINTF((DBG_DEV, "NET: dev_add_addr: dev=0x%X af=%d\n", dev, af));
+   ifa = dev_get_addr(dev, af);
+   DPRINTF((DBG_DEV, "NET: dev_add_addr: ifa=0x%X\n", ifa));
+   if (ifa == NULL) {
+ 	ifa = (struct ifaddr *) kmalloc(sizeof(struct ifaddr), GFP_KERNEL);
+ 	if (ifa == NULL) {
+ 		DPRINTF((DBG_DEV, "NET: dev_add_addr: no mem "));
+ 		DPRINTF((DBG_DEV, "for new address for %s !\n", dev->if_name));
+ 		return(NULL);
+ 	}
+ 	memset(ifa, 0, sizeof(struct ifaddr));
+ 	DPRINTF((DBG_DEV, "               ifa (new) at 0x%X\n", ifa));
+ 	cli();
+ 	ifa->ifa_next = dev->if_addr;
+ 	dev->if_addr = ifa;
+ 	sti();
+   }
+   DPRINTF((DBG_DEV, "               ifa at 0x%X\n", ifa));
+   return(ifa);
+ }
+ 
+ 
+ /* Find an interface in the list. */
+ struct ifnet *
+ dev_get_name(char *name)
+ {
+   struct ifnet *dev;
+ 
+   for (dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if (dev->if_name != NULL) {
+ 		DPRINTF((DBG_DEV, "NET: dev_get_name: \"%s\" -> \"%s\"\n",
+ 					dev->if_name, name));
+ 		if (strcmp(dev->if_name, name) == 0) return(dev);
+ 	}
+   }
+   return(NULL);
+ }
+ 
+ 
+ /* Prepare an interface for use. */
+ int
+ dev_open(struct ifnet *dev)
+ {
+   int ret = 0;
+ 
+   if (dev->open != NULL) ret = dev->open(dev);
+   if (ret == 0) dev->if_flags |= (IFF_UP | IFF_RUNNING);
+   dev->if_tbusy = 0;
+ 
+   return(ret);
+ }
+ 
+ 
+ /* Completely shutdown an interface. */
+ int
+ dev_close(struct ifnet *dev)
+ {
+   dev->if_tbusy = 0;
+   if (dev->if_flags & IFF_UP) {
+ 	dev->if_flags &= ~IFF_UP;
+ 	if (dev->close != NULL) dev->close(dev);
+ 
+ 	route_flush(dev);
+   }
+   return(0);
+ }
+ 
+ 
+ void
+ dev_init(void)
+ {
+   /* Setup the "bottom half" function for NET. */
+   bh_base[NET_BH].routine = net_bh;
+ 
+   /* Initialize the ETH module. */
+   eth_init();
+ }
*** original/net/socket/eth.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/eth.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,272 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module handles all generic Ethernet stuff, and can
+  *		be used by Ethernet-class device drivers to do most of
+  *		the high-level Ethernet data processing.
+  *
+  * Version:	@(#)eth.c	1.1.7	03/31/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include <linux/in.h>			/* for ntohs() et al		*/
+ #include "socket.h"
+ 
+ 
+ static int eth_nr_cards = 0;		/* number of installed cards	*/
+ 
+ 
+ /* Display an Ethernet address in readable format. */
+ static char *
+ eth_print(unsigned char *ptr)
+ {
+   static char buff[20];
+ 
+   if (ptr == NULL) return("[NONE]");
+   sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X",
+ 	(ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255),
+ 	(ptr[3] & 255), (ptr[4] & 255), (ptr[5] & 255)
+   );
+   return(buff);
+ }
+ 
+ 
+ static char *
+ eth_sprint(struct sockaddr *sap)
+ {
+   return(eth_print(sap->sa_data));
+ }
+ 
+ 
+ /* Display the contents of the Ethernet MAC header. */
+ static void
+ eth_dump(struct ifnet *dev, struct ethhdr *eth, int len)
+ {
+   unsigned char buff[32];
+   unsigned char *ptr;
+   int addr, i;
+ 
+   /* Dump the Ethernet header. */
+   printk("     %s->", eth_print(eth->h_source));
+   printk("%s TYPE=0x%04X LEN=%d\n",
+ 	eth_print(eth->h_dest), ntohs(eth->h_proto), len);
+ 
+   /* Dump the data. */
+   ptr = (unsigned char *)(eth + 1);	/* point to frame data */
+   len -= dev->if_machlen;			/* minus MAC header */
+   addr = 0;
+   while (len > 0) {
+ 	printk("     %04X: ", addr);
+ 	for(i = 0; i < 16; i++) {
+ 		if (len > 0) {
+ 			printk("%02X ", (*ptr & 0xFF));
+ 			buff[i] = *ptr++;
+ 			if (buff[i] < 32 || buff[i] > 126) buff[i] = '.';
+ 		} else {
+ 			printk("   ");
+ 			buff[i] = ' ';
+ 		}
+ 		addr++;
+ 		len--;
+ 	};
+ 	buff[i] = '\0';
+ 	printk("  \"%s\"\n", buff);
+   }
+   printk("     ----\n");
+ }
+ 
+ 
+ /* Create the Ethernet MAC header. */
+ static int
+ eth_mac1(struct ifnet *dev, struct sk_buff *skb, int type,
+ 	 struct sockaddr *src, struct sockaddr *dst)
+ {
+   struct ethhdr *eth;
+ 
+   /* Fill in the basic Ethernet MAC header. */
+   eth = (struct ethhdr *) (skb + 1);
+   memcpy(eth->h_source, src->sa_data, dev->if_macalen);
+   if (dst == NULL) memcpy(eth->h_dest,
+ 			  dev->if_macaddr->ifa_broadaddr.sa_data,
+ 			  dev->if_macalen);
+     else memcpy(eth->h_dest, dst->sa_data, dev->if_macalen);
+   eth->h_proto = (unsigned short) htons(type);
+ 
+   DPRINTF((DBG_ETH, "ETH: mac1(%s, 0x%X, %s, ",
+ 		dev->if_name, skb, eth_print(eth->h_source)));
+   DPRINTF((DBG_ETH, "%s, 0x%X)\n", eth_print(eth->h_dest), type));
+ 
+   /* Return the header length. */
+   return(dev->if_machlen);
+ }
+ 
+ 
+ /* Rebuild the Ethernet MAC header. */
+ static void
+ eth_mac2(struct ifnet *dev, struct sk_buff *skb, struct sockaddr *dst)
+ {
+   struct ethhdr *eth;
+ 
+   eth = (struct ethhdr *) (skb + 1);
+   DPRINTF((DBG_ETH, "ETH: mac2(0x%X, %s, %s)\n",
+ 		skb, dev->if_name, eth_sprint(dst)));
+   memcpy(eth->h_dest, dst->sa_data, dev->if_macalen);
+ }
+ 
+ 
+ /* Determine the packet's protocol ID. */
+ static int
+ eth_recv(struct ifnet *dev, struct sk_buff *skb, int len)
+ {
+   struct ethhdr *eth;
+ 
+   eth = (struct ethhdr *) (skb + 1);
+   DPRINTF((DBG_ETH, "\nETH: %s recv:\n", dev->if_name));
+ #ifdef NET_DEBUG
+   if (net_debug & (1 << DBG_ETH)) eth_dump(dev, eth, len);
+ #endif
+ 
+   /* Important check.  See if the source address is valid. */
+   if (!memcmp(eth->h_source,
+ 	      dev->if_macaddr->ifa_broadaddr.sa_data,
+ 	      dev->if_macalen)) {
+ 	printk("ETH: %s: Bogus source address detected (ignored)\n",
+ 							dev->if_name);
+ 	eth_dump(dev, eth, len);
+ 	return(0);
+   }
+ 	
+   /* Make sure we get the pointers right! */
+   skb->raw = (unsigned char *) (eth + 1);
+   skb->len -= dev->if_machlen;
+ 
+   return((int) ntohs(eth->h_proto));
+ }
+ 
+ 
+ /* Send out an Ethernet frame. */
+ static void
+ eth_send(struct ifnet *dev, struct sk_buff *skb,
+ 	 struct sockaddr *target, int priority)
+ {
+   struct ethhdr *eth;
+ 
+   /* Add the MAC header length to the `data' length. */
+   eth = (struct ethhdr *) (skb + 1);
+   skb->raw = (unsigned char *) eth;
+ #if 0
+   skb->len += dev->if_machlen;
+ #endif
+ 
+   DPRINTF((DBG_ETH, "\nETH: %s sent:\n", dev->if_name));
+ #ifdef NET_DEBUG
+   if (net_debug & (1 << DBG_ETH)) eth_dump(dev, eth, skb->len);
+ #endif
+ 
+   /* Call the DEV interface for the actual work. */
+   dev_queue(dev, skb, target, priority);
+ }
+ 
+ 
+ /* Setup an Ethernet MAC address. */
+ static int
+ eth_setmacaddr(struct ifnet *dev, struct sockaddr *sap)
+ {
+   if (sap != NULL) {
+ 	memcpy(&(dev->if_macaddr->ifa_addr), sap, sizeof(struct sockaddr));
+   }
+   dev->if_macaddr->ifa_addr.sa_family = ARPHRD_ETHER;
+   memcpy(&(dev->if_macaddr->ifa_broadaddr.sa_data), ETH_A_BCAST, ETH_ALEN);
+   dev->if_macaddr->ifa_broadaddr.sa_family = ARPHRD_ETHER;
+   return(0);
+ }
+ 
+ 
+ /* Retrieve an Ethernet MAC address. */
+ static struct sockaddr *
+ eth_getmacaddr(struct ifnet *dev)
+ {
+   return(&(dev->if_macaddr->ifa_addr));
+ }
+ 
+ 
+ /* Retrieve an Ethernet MAC broadcast address. */
+ static struct sockaddr *
+ eth_getmacbroadaddr(struct ifnet *dev)
+ {
+   return(&(dev->if_macaddr->ifa_broadaddr));
+ }
+ 
+ 
+ static struct arp_hwtype eth_hw_type_1 = {
+   ARPHRD_ETHER,		"10 Mbps Ethernet", 	ETH_ALEN,
+   eth_sprint,	NULL
+ };
+ static struct arp_hwtype eth_hw_type_2 = {
+   ARPHRD_EETHER,	"3 Mbps Ethernet",	ETH_ALEN,
+   eth_sprint,	NULL
+ };
+ static struct arp_hwtype eth_hw_type_6 = {
+   ARPHRD_IEEE802,	"IEEE 802.2 Ethernet",	ETH_ALEN,
+   eth_sprint,	NULL
+ };
+ 
+ 
+ /* Setup an interface for an Ethernet-class device. */
+ int
+ eth_setup(struct ifnet *dev)
+ {
+   /* Standardized fields. */
+   dev->if_mtu = ETH_MTU;
+ 
+   dev->recv = eth_recv;
+   dev->send = eth_send;
+   dev->mac1 = eth_mac1;
+   dev->mac2 = eth_mac2;
+   dev->mac_aget = eth_getmacaddr;
+   dev->mac_aset = eth_setmacaddr;
+   dev->mac_bget = eth_getmacbroadaddr;
+   dev->mac_bset = NULL;
+   dev->mac_print = eth_print;
+   dev->mac_sprint = eth_sprint;
+ 
+   /* Initialize the MAC address structure. */
+   dev->if_machlen = ETH_HLEN;
+   dev->if_macalen = ETH_ALEN;
+ 
+   /* Set up the generic Ethernet interface name. */
+   sprintf(dev->if_name, ETH_NAME, eth_nr_cards++);
+ 
+   return(0);
+ }
+ 
+ 
+ void
+ eth_init(void)
+ {
+   /* Tell ARP about the hardware types we support. */
+   (void) arp_add_hw(&eth_hw_type_1);
+   (void) arp_add_hw(&eth_hw_type_2);
+   (void) arp_add_hw(&eth_hw_type_6);
+ }
*** original/net/socket/family.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/family.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,78 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This module handles the registration of the various address
+  *		families.  This basically means: maintaining a list of data
+  *		structures, sent to us by the protocol modules when they
+  *		start up.
+  *
+  * Version:	@(#)family.c	1.2.1	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <linux/signal.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/stat.h>
+ #include <linux/socket.h>
+ #include <linux/ddi.h>
+ #include <linux/if.h>
+ #include "socket.h"
+ 
+ 
+ static struct proto_ops *net_pops[NFAMILY];
+ 
+ 
+ /* See if we can handle a certain address family. */
+ struct proto_ops *
+ net_af_get(int af)
+ {
+   int i;
+ 
+   for(i = 0; i < NFAMILY; i++) {
+ 	if (net_pops[i] == NULL) continue;
+ 	if (net_pops[i]->family == af) return(net_pops[i]);
+   }
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * This function is called by a protocol handler that wants to
+  * advertise its address family, and have it linked into the
+  * SOCKET module.
+  */
+ int
+ net_af_reg(int family, struct proto_ops *ops)
+ {
+   int i;
+ 
+   cli();
+   for(i = 0; i < NFAMILY; i++) {
+ 	if (net_pops[i] != NULL) continue;
+ 	net_pops[i] = ops;
+ 	net_pops[i]->family = family;
+ 	sti();
+ 	return(0);
+   }
+   sti();
+   return(-ENOMEM);
+ }
+ 
+ 
+ void
+ net_af_init(void)
+ {
+   int i;
+ 
+   for (i = 0; i < NFAMILY; i++) net_pops[i] = NULL;
+ }
*** original/net/socket/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/ioctl.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,395 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This file handles all NET I/O control requests. All of the
+  *		generic requests are handled here, while the interface- or
+  *		protocol-specific requests get forwarded to the responsible
+  *		modules.
+  *
+  * Version:	@(#)ioctl.c	1.2.10	03/28/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include "socket.h"
+ 
+ 
+ /* Check if the new FLAGS settings are OK. */
+ static int
+ net_check_flags(struct ifnet *dev, int new_flags)
+ {
+   int old_flags;
+   int ret = 0;
+ 
+   /* Save the old flags. */
+   old_flags = dev->if_flags;
+ 
+   /* Check for a change of the IFF_UP flag. */
+   if ((old_flags & IFF_UP) != (new_flags & IFF_UP)) {
+ 	if (new_flags & IFF_UP)
+ 		ret = dev_open(dev);
+ 	  else
+ 		ret = dev_close(dev);
+ 	if (ret < 0) return(ret);
+   }
+ 
+   /* Check for a change of the IFF_PROMISC flag. */
+   if ((old_flags & IFF_PROMISC) != (new_flags & IFF_PROMISC)) {
+ 	if (dev->set_multi == NULL) return(-EINVAL);
+ 	if (new_flags & IFF_PROMISC)
+ 		ret = dev->set_multi(dev, -1, NULL);
+ 	  else
+ 		ret = dev->set_multi(dev, 0, NULL);
+ 	if (ret < 0) return(ret);
+   }
+ 
+   /* Check for a change of the IFF_ALLMULTI flag. */
+   if ((old_flags & IFF_ALLMULTI) != (new_flags & IFF_ALLMULTI)) {
+ 	if (dev->set_multi == NULL) return(-EINVAL);
+ 	if (new_flags & IFF_ALLMULTI) {
+ #if 1
+ 		printk("%s: no multicast mode available yet !\n", dev->if_name);
+ 		ret = -EINVAL;
+ #else
+ 		ret = dev->set_multi(dev, 1, NULL);
+ #endif
+ 	} else {
+ 		ret = dev->set_multi(dev, 0, NULL);
+ 	}
+ 	if (ret < 0) return(ret);
+   }
+ 
+   /* All seemed well. */
+   dev->if_flags = new_flags;
+ 
+   return(ret);
+ }
+ 
+ 
+ /* Fetch the caller's info block. */
+ static int
+ net_getioreq(unsigned long arg, int rw, struct ifreq *ptr, struct ifnet **dev)
+ {
+   int err;
+ 
+   if (rw == 1) {
+ 	err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct ifreq));
+ 	if (err < 0) return(err);
+ 	memcpy_tofs((char *) arg, ptr, sizeof(struct ifreq));
+ 	return(0);
+   }
+ 
+   /* We are reading a request from the caller. */
+   err = verify_area(VERIFY_READ, (char *) arg, sizeof(struct ifreq));
+   if (err < 0) return(err);
+   memcpy_fromfs(ptr, (char *) arg, sizeof(struct ifreq));
+ 
+   /* See which interface the caller is talking about. */
+   if ((*dev = dev_get_name(ptr->ifr_name)) == NULL) {
+ 	DPRINTF((DBG_IOC,
+ 		"NET: SIOC: unknown interface \"%s\" requested.\n",
+ 							ptr->ifr_name));
+ 	return(-EINVAL);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /* Perform a SIOCGIFCONF call. */
+ static int
+ net_ifconf(unsigned long arg)
+ {
+   struct ifconf ifc;
+   struct ifreq ifr;
+   struct ifnet *dev;
+   unsigned char *pos;
+   int err, len;
+ 
+   /* Fetch the caller's info block. */
+   err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct ifconf));
+   if (err < 0) return(err);
+   memcpy_fromfs(&ifc, (char *) arg, sizeof(struct ifconf));
+   len = ifc.ifc_len;
+   pos = (unsigned char *) ifc.ifc_buf;
+   if ((err = verify_area(VERIFY_WRITE, pos, len)) < 0) return(err);
+ 
+   /* Loop over the interfaces, and write an info block for each. */
+   for (dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	memset(&ifr, 0, sizeof(struct ifreq));
+ 	if (dev->if_name != NULL) strcpy(ifr.ifr_name, dev->if_name);
+ 	if (dev->if_addr != NULL) memcpy(&ifr.ifr_addr,
+ 					 &dev->if_addr->ifa_addr,
+ 					 sizeof(struct sockaddr));
+ 
+ 	/* Write this block to the caller's space. */
+ 	memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
+ 	pos += sizeof(struct ifreq);
+ 	len -= sizeof(struct ifreq);
+ 	if (len < sizeof(struct ifreq)) break;
+   }
+ 
+   /* All done.  Write the updated control block back to the caller. */
+   ifc.ifc_len = (pos - (unsigned char *)ifc.ifc_buf);
+   ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
+   memcpy_tofs((char *) arg, &ifc, sizeof(struct ifconf));
+ 
+   return(0);
+ }
+ 
+ 
+ /* Perform the SIOCxIFxxx calls. */
+ static int
+ net_ifsioc(struct socket *sock, unsigned int cmd, unsigned long arg)
+ {
+   struct ifreq ifr;
+   struct ifnet *dev;
+   struct ifaddr *ifa;
+   struct proto_ops *pro;
+   int err;
+ 
+   /* Now check which request to perform. */
+   switch(cmd) {
+ 	case SIOCGIFFLAGS:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		ifr.ifr_flags = dev->if_flags;
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFFLAGS:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		return(net_check_flags(dev, ifr.ifr_flags));
+ 
+ 	case SIOCGIFADDR:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		if (dev->if_addr == NULL)
+ 			memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr));
+ 		  else memcpy(&ifr.ifr_addr, &dev->if_addr->ifa_addr,
+ 					sizeof(struct sockaddr));
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFADDR:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		pro = net_af_get(ifr.ifr_addr.sa_family);
+ 		if (pro == NULL) return(-EPFNOSUPPORT);
+ 		ifa = dev_add_addr(dev, ifr.ifr_addr.sa_family);
+ 		if (ifa != NULL) ifa->ifa_addr = ifr.ifr_addr;
+ 		return(pro->ioctl(NULL, cmd, arg));
+ 
+ 	case SIOCGIFBRDADDR:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		if (dev->if_addr == NULL)
+ 			memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr));
+ 		  else memcpy(&ifr.ifr_addr, &dev->if_addr->ifa_broadaddr,
+ 					sizeof(struct sockaddr));
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFBRDADDR:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		pro = net_af_get(ifr.ifr_addr.sa_family);
+ 		if (pro == NULL) return(-EPFNOSUPPORT);
+ 		ifa = dev_add_addr(dev, ifr.ifr_addr.sa_family);
+ 		if (ifa != NULL) ifa->ifa_broadaddr = ifr.ifr_addr;
+ 		return(pro->ioctl(NULL, cmd, arg));
+ 
+ 	case SIOCGIFDSTADDR:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		if (dev->if_addr == NULL)
+ 			memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr));
+ 		  else memcpy(&ifr.ifr_addr, &dev->if_addr->ifa_dstaddr,
+ 					sizeof(struct sockaddr));
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFDSTADDR:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		pro = net_af_get(ifr.ifr_addr.sa_family);
+ 		if (pro == NULL) return(-EPFNOSUPPORT);
+ 		ifa = dev_add_addr(dev, ifr.ifr_addr.sa_family);
+ 		if (ifa != NULL) ifa->ifa_dstaddr = ifr.ifr_addr;
+ 		return(pro->ioctl(NULL, cmd, arg));
+ 
+ 	case SIOCGIFNETMASK:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		if (dev->if_addr == NULL)
+ 			memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr));
+ 		  else memcpy(&ifr.ifr_addr, &dev->if_addr->ifa_mask,
+ 					sizeof(struct sockaddr));
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFNETMASK:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		pro = net_af_get(ifr.ifr_addr.sa_family);
+ 		if (pro == NULL) return(-EPFNOSUPPORT);
+ 		ifa = dev_add_addr(dev, ifr.ifr_addr.sa_family);
+ 		if (ifa != NULL) ifa->ifa_mask = ifr.ifr_addr;
+ 		return(pro->ioctl(NULL, cmd, arg));
+ 
+ 	case SIOCGIFMETRIC:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		ifr.ifr_metric = dev->if_metric;
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFMETRIC:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		dev->if_metric = ifr.ifr_metric;
+ 		return(0);
+ 
+ 	case SIOCGIFMTU:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		ifr.ifr_mtu = dev->if_mtu;
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFMTU:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		return(dev->ioctl((struct inode *) dev, NULL,
+ 				  cmd, (unsigned long) ifr.ifr_mtu));
+ 
+ 	case SIOCGIFMEM:
+ 		return(-EINVAL);
+ 
+ 	case SIOCSIFMEM:
+ 		if (! suser()) return(-EPERM);
+ 		return(-EINVAL);
+ 
+ 	case SIOCGIFSTATS:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		err = verify_area(VERIFY_WRITE, (void *) ifr.ifr_data,
+ 				  sizeof(struct dev_stats));
+ 		if (err < 0) return(err);
+ 		memcpy_tofs((char *) ifr.ifr_data, dev->if_stats,
+ 				sizeof(struct dev_stats));
+ 		return(0);
+ 
+ 	case SIOCSIFSTATS:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		err = verify_area(VERIFY_READ, (void *) ifr.ifr_data,
+ 				  sizeof(struct dev_stats));
+ 		if (err < 0) return(err);
+ 		memcpy_fromfs(dev->if_stats, (char *) ifr.ifr_data,
+ 					sizeof(struct dev_stats));
+ 		return(0);
+ 
+ 	case SIOCGIFENCAP:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		return(dev->ioctl((struct inode *) dev, NULL, cmd, arg));
+ 
+ 	case SIOCSIFENCAP:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		return(dev->ioctl((struct inode *) dev, NULL, cmd, arg));
+ 
+ 	case SIOCGIFHWADDR:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		memcpy(&ifr.ifr_addr, &dev->if_macaddr->ifa_addr,
+ 						sizeof(struct sockaddr));
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFHWADDR:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		if (dev->mac_aset == NULL) return(-EINVAL);
+ 		if ((err = dev->mac_aset(dev, &ifr.ifr_addr)) < 0) return(err);
+ 		return(dev->ioctl((struct inode *) dev, NULL, cmd, arg));
+ 
+ 	case SIOCGIFHWBRADDR:
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		memcpy(&ifr.ifr_addr, &dev->if_macaddr->ifa_broadaddr,
+ 						sizeof(struct sockaddr));
+ 		return(net_getioreq(arg, 1, &ifr, NULL));
+ 
+ 	case SIOCSIFHWBRADDR:
+ 		if (! suser()) return(-EPERM);
+ 		if ((err = net_getioreq(arg, 0, &ifr, &dev)) < 0) return(err);
+ 		if (dev->mac_bset == NULL) return(-EINVAL);
+ 		if ((err = dev->mac_bset(dev, &ifr.ifr_addr)) < 0) return(err);
+ 		return(dev->ioctl((struct inode *) dev, NULL, cmd, arg));
+ 
+ 	default:
+ 		if (sock != NULL) return(sock->ops->ioctl(sock, cmd, arg));
+   }
+   return(-EINVAL);
+ }
+ 
+ 
+ /* This function handles all "interface"-type I/O control requests. */
+ int
+ net_ioctl(struct inode *inode, struct file *file,
+ 	  unsigned int cmd, unsigned long arg)
+ {
+   struct socket *sock;
+   int err;
+ 
+   DPRINTF((DBG_IOC, "NET: ioctl: inode=0x%X file=0x%X cmd=0x%X arg=0x%X\n",
+ 						inode, file, cmd, arg));
+   sock = NULL;
+   if (inode != NULL) {
+ 	if ((sock = socki_lookup(inode)) == NULL) {
+ 		printk("NET: ioctl: can't find socket for inode!\n");
+ 		return(-EBADF);
+ 	}
+   }
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (! suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(net_dbg_ioctl(arg, 1));
+ 
+ #if 1	/* Will go with official release. */
+ 	case 0x2401:	/* IP_SET_DEV */
+ 		printk("NET: Warning: old-style ioctl(IP_SET_DEV) called!\n");
+ 		printk("     This is probably caused by an old \"config\"\n");
+ 		printk("     program on your disk: \"%s\"\n", current->comm);
+ 		return(-EINVAL);
+ #endif
+ 
+ 	case SIOCDARP:
+ 	case SIOCGARP:
+ 	case SIOCSARP:
+ 		if (net_io_vec[ARP_MINOR] != NULL)
+ 			return((*net_io_vec[ARP_MINOR])(NULL, NULL, cmd, arg));
+ 		  else return(-EINVAL);
+ 
+ 	case SIOCADDRT:
+ 	case SIOCDELRT:
+ 		if (net_io_vec[ROUTE_MINOR] != NULL)
+ 			return((*net_io_vec[ROUTE_MINOR])(NULL, NULL, cmd, arg));
+ 		  else return(-EINVAL);
+ 
+ 	case SIOCGIFCONF:
+ 		return(net_ifconf(arg));
+ 
+ 	default:
+ 		return(net_ifsioc(sock, cmd, arg));
+   }
+ 
+   /*NOTREACHED*/
+   return(-EINVAL);
+ }
*** original/net/socket/packet.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/packet.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,470 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This file handles the top-level part of all network packet
+  *		handling, including the low-level ("bottom half") processing
+  *		of device driver requests.
+  *
+  * Version:	@(#)packet.c	1.2.13	03/18/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Matt Dillon, <dillon@apollo.west.oit.com>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/malloc.h>
+ #include <linux/errno.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include "socket.h"
+ 
+ 
+ #define	DEV_MAX_RETRIES	10	/* maybe this should be device dependent? */
+ 
+ 
+ static struct sk_buff *volatile net_backlog = NULL;
+ 
+ 
+ /* This routine causes all interfaces to try to send some data. */
+ static void
+ net_kick(void)
+ {
+   struct ifnet *dev;
+ 
+   for (dev = dev_base; dev != NULL; dev = dev->if_next) {
+ 	if ((dev->if_flags & (IFF_UP | IFF_RUNNING)) == 0) continue;
+ 	DPRINTF((DBG_PKT,
+ 	    "NET: kick interface 0x%X (flags 0x%X, busy=%d qlen=%d)\n",
+ 			dev, dev->if_flags, dev->if_tbusy, dev->if_qlen));
+ 	if (dev->if_tbusy == 0) dev_tint(dev);
+ 	if (dev->if_qlen) mark_bh(NET_BH);
+   }
+ }
+ 
+ 
+ /*
+  * Send (or queue for sending) a packet.
+  * This function is called by the higher layers to
+  * pass a packet to the DEV (device driver) layer.
+  */
+ void
+ dev_queue(struct ifnet *dev, struct sk_buff *skb,
+ 	  struct sockaddr *target, int pri)
+ {
+   struct ifqueue *q, *p;
+ 
+   DPRINTF((DBG_PKT, "NET: dev_queue(skb=%X, dev=%X, pri = %d)\n",
+ 							skb, dev, pri));
+ 
+   if (skb == NULL) {
+ 	printk("NET: dev_queue: skb = NULL\n");
+ 	return;
+   }
+   if (dev == NULL) {
+ 	printk("NET: dev_queue: dev = NULL\n");
+ 	cli();
+ 	skb->lock++;
+ 	sti();
+ 	skb_release(skb);
+ 	return;
+   }
+ 
+   /* Make sure we have not missed an interrupt. */
+   skb->dev = dev;
+ 
+   /* We have to be UP to get any work done, eh? */
+   if ((dev->if_flags & IFF_UP) == 0) {
+ 	DPRINTF((DBG_PKT, "NET: dev_queue: sending to \"%s\", not UP\n",
+ 							dev->if_name));
+ 	cli();
+ 	skb->lock++;
+ 	sti();
+ 	skb_release(skb);
+ 	return;
+   }
+ 
+   /* Make sure we won't trash the device driver. */
+   if (skb->len > dev->if_mtu) {
+ 	printk("NET: dev_queue: program %s tried sending too large a packet\n",
+ 						current->comm);
+ 	printk("                on interface %s. Size is %d, MTU is %d\n",
+ 					dev->if_name, skb->len, dev->if_mtu);
+ 	printk("                Dumping packet....\n");
+ 	cli();
+ 	skb->lock++;
+ 	sti();
+ 	skb_release(skb);
+ 	return;
+   }
+ 
+   if (skb->lock && skb->arp != 2) {
+ 	/* Already queued, do not queue it twice! */
+ 	dev->xmit(dev, NULL);
+ 	return;
+   }
+   skb->lock = 1;
+ 
+   /*
+    * Check if we have to pass this packet to ARP for resolving
+    * the target address.  If it hasn't been resolved yet, we
+    * can't send it out, right?
+    */
+   if (skb->arp == 0) {
+ 	DPRINTF((DBG_PKT, "NET: dev_queue: sent to ARP.\n"));
+ 	arp_queue(skb, target);
+ 	return;
+   }
+   skb->arp = 1;
+ 
+   /* See if the device driver is ready for this. */
+   if (dev->xmit(dev, skb) == 0) {
+ 	skb_release(skb);
+   	return;
+   }
+ 
+   /*
+    * A retransmit is indicated by a negative priority. Note that
+    * this will go on forever, which probably is not such a good
+    * idea...
+    */
+   if (pri < 0 || pri >= IFF_MAX_PRIO) {
+ 	printk("NET: dev_queue: invalid priority %d requested on \"%s\" !\n",
+ 							pri, dev->if_name);
+ 	skb_release(skb);
+ 	return;
+   }
+ 
+   /* Allocate an output-queue entry. */
+   q = (struct ifqueue *) kmalloc(sizeof(struct ifqueue), GFP_ATOMIC);
+   if (q == NULL) {
+ 	printk("NET: dev_queue: no memory for queue on dev \"%s\" !\n",
+ 						dev->if_name);
+ 	skb_release(skb);
+ 	return;
+   }
+   q->dev = dev;
+   q->skb = skb;
+   q->len = skb->len;
+   q->retries = DEV_MAX_RETRIES;
+   q->next = NULL;
+ 
+   /* Add this packet to the interface output queue. */
+   cli();
+   if (dev->if_qhead[pri] != NULL) {
+ 	p = dev->if_qhead[pri];
+ 	while (p->next != NULL) p = p->next;
+ 	p->next = q;
+ 	q->next = NULL;
+   } else {
+ 	dev->if_qhead[pri] = q;
+   }
+   dev->if_qlen++;
+   sti();
+ 
+   DPRINTF((DBG_PKT,
+ 	"NET: dev_queue: queued for dev \"%s\", pri=%d, backlog=%d\n",
+ 					dev->if_name, pri, dev->if_qlen));
+ }
+ 
+ 
+ /*
+  * Send (or queue for sending) a packet.
+  * This function is called only from dev_tint() for
+  * sending retries, so the packet is already ARPed
+  * and priority is valid.  Returns '1' for 'packet
+  * sent or dropped, continue' or '0' for 'packet
+  * requeued, stop now'.
+  */
+ static int
+ dev_requeue(struct ifnet *dev, struct sk_buff *skb, int pri, int retries)
+ {
+   struct ifqueue *q;
+ 
+   DPRINTF((DBG_PKT, "NET: dev_requeue(skb=%X, dev=%X, pri = %d)\n",
+ 							skb, dev, pri));
+   if (dev == NULL) {
+ 	printk("NET: dev_requeue: dev = NULL\n");
+ 	skb_release(skb);
+ 	return(1);
+   }
+ 
+   /* We have to be UP to get any work done, eh? */
+   if ((dev->if_flags & IFF_UP) == 0) {
+ 	DPRINTF((DBG_PKT, "NET: dev_requeue: sending to \"%s\", not UP\n",
+ 							dev->if_name));
+ 	skb_release(skb);
+ 	return(1);
+   }
+ 
+   /* See if the device driver is ready for this. */
+   if (dev->xmit(dev, skb) == 0) {
+ 	skb_release(skb);
+   	return(1);
+   }
+ 
+   if (++pri >= IFF_MAX_PRIO) pri = (IFF_MAX_PRIO - 1);
+ 
+   if (--retries <= 0) {
+ 	skb_release(skb);
+ 	return(1);
+   }
+ 
+   /* Allocate an output-queue entry. */
+   q = (struct ifqueue *) kmalloc(sizeof(struct ifqueue), GFP_ATOMIC);
+   if (q == NULL) {
+ 	printk("NET: dev_requeue: no memory for queue on dev \"%s\" !\n",
+ 						dev->if_name);
+ 	skb_release(skb);
+ 	return(1);
+   }
+   q->dev = dev;
+   q->skb = skb;
+   q->len = skb->len;
+   q->retries = retries;
+   q->next = NULL;
+ 
+   /* Add this packet to the interface output queue. */
+   cli();
+   if (dev->if_qhead[pri] != NULL) {
+ 	q->next = dev->if_qhead[pri];
+ 	dev->if_qhead[pri] = q;
+   } else {
+ 	dev->if_qhead[pri] = q;
+   }
+   dev->if_qlen++;
+   sti();
+ 
+   DPRINTF((DBG_PKT,
+ 	"NET: dev_requeue: queued for dev \"%s\", pri=%d, backlog=%d\n",
+ 					dev->if_name, pri, dev->if_qlen));
+   return(0);
+ }
+ 
+ 
+ /*
+  * Fetch a packet from a device driver.
+  * This function is the base level entry point for all drivers that
+  * want to send a packet to the upper (protocol) levels.  It takes
+  * the (already formatted) `skb' pointer, and queues it (after some
+  * sanity checks- even a device driver can goof up..) for later pro-
+  * cessing by the "bottom half" part of this function, "net_bh".
+  *
+  * Return values:	1 <- exit I can't do any more
+  *			0 <- feed me more (i.e. "done", "OK"). 
+  */
+ int
+ dev_rint(struct ifnet *dev, struct sk_buff *skb, int len)
+ {
+   static int dropping = 0;
+ 
+   if (dev == NULL || skb == NULL || len <= 0) return(1);
+ 
+   cli();
+   skb->lock++;
+   sti();
+ 
+   if (dropping && net_backlog != NULL) {
+ 	skb_release(skb);
+ 	return(1);
+   }
+   if (dropping) printk("NET: dev_rint: no longer dropping packets.\n");
+   dropping = 0;
+ 
+   skb->dev = dev;
+   skb->sk = NULL;
+   skb->len = len;
+   DPRINTF((DBG_PKT, "NET: dev_rint: packet at 0x%X, len=%d\n", skb, skb->len));
+ 
+   /* Now add it to the backlog. */
+   cli();
+   if (net_backlog == NULL) {
+ 	skb->next = NULL;
+ 	net_backlog = skb;
+   } else {
+ 	skb->next = (struct sk_buff *) net_backlog;
+ 	net_backlog = skb;
+   }
+   sti();
+    
+   /* If any packet arrived, mark it for processing. */
+   if (net_backlog != NULL) mark_bh(NET_BH);
+ 
+   /* OK, all done. */
+   return(0);
+ }
+ 
+ 
+ /* A device driver is ready to transmit a packet. */
+ void
+ dev_tint(struct ifnet *dev)
+ {
+   struct ifqueue *q;
+   struct sk_buff *skb;
+   int i;
+   int retries;
+ 
+   /* Try to send as much packets as possible... */
+   for(i = 0; i < IFF_MAX_PRIO; i++) {
+ 	cli();
+ 	while ((q = dev->if_qhead[i]) != NULL) {
+ 		dev->if_qhead[i] = q->next;
+ 		skb = q->skb;
+ 		retries = q->retries;
+ 		kfree_s(q, sizeof(struct ifqueue));
+ 		dev->if_qlen--;
+ 		sti();
+ 
+ 		/* This will send it through the process again. */
+ 		if (!dev_requeue(dev, skb, i, retries) || dev->if_tbusy) {
+ 			sti();
+ 			return;
+ 		}
+ 		cli();
+ 	}
+ 	sti();
+   }
+ }
+ 
+ 
+ /*
+  * This function gets called periodically, to see if we can
+  * process any data that came in from some interface.
+  */
+ void
+ net_bh(void *tmp)
+ {
+   struct sk_buff *skb;
+   unsigned short int type;
+   unsigned char flag = 0;
+   static volatile int in_bh = 0;
+ 
+   /* Check && mark our BUSY state. */
+   cli();
+   if (in_bh != 0) {
+ 	sti();
+ 	return;
+   }
+   in_bh = 1;
+   sti();
+ 
+   /* Can we send anything now? */
+   net_kick();
+   
+   /* Any data left to process? */
+   cli();
+   while (net_backlog != NULL) {
+ 	skb = (struct sk_buff *) net_backlog;
+ 	net_backlog = skb->next;
+ 	sti();
+ 	skb->next = NULL;
+ 
+ 	/*
+ 	 * Do any hardware-dependent processing in this packet, and set
+ 	 * up the pointers so that the `raw' field point to the beginning
+ 	 * of the non-hardware dependent data.  This function usually does
+ 	 * some amount of tracing, too.  It returns the protocol ID of the
+ 	 * packet, as defined by IEEE for their Ethernet specification.
+ 	 */
+ 	type = skb->dev->recv(skb->dev, skb, skb->len);
+ 	DPRINTF((DBG_PKT, "NET: net_bh: skb=0x%X, raw=0x%X, len=%d, dev=0x%X: ",
+ 					skb, skb->raw, skb->len, skb->dev));
+ 	DPRINTF((DBG_PKT, "type=0x%04X\n", type));
+ 	if (type == 0) {
+ 		skb_release(skb);
+ 		cli();
+ 		continue;
+ 	}
+ 
+ 	/*
+ 	 * We got a packet ID.  Now loop over the "known protocols"
+ 	 * table (which is actually a linked list, but this will
+ 	 * change soon if I get my way- FvK), and forward the packet
+ 	 * to anyone who wants it.
+ 	 */
+ 	flag = net_proto_send(type, skb);
+ 	DPRINTF((DBG_PKT, "NET: net_bh: sent to protocols (%d)\n", flag));
+ 
+ 	/*
+ 	 * That's odd.  We got an unknown packet.  Who's using
+ 	 * stuff like Novell or Amoeba on this network??
+ 	 */
+ 	if (!flag) {
+ 		DPRINTF((DBG_PKT, "NET: net_bh: unknown type 0x%04X (ignored)\n",
+ 									type));
+ 		skb_release(skb);
+ 	}
+ 
+ 	/* Again, see if we can transmit anything now. */
+ 	net_kick();
+ 	cli();
+   }
+   in_bh = 0;
+   sti();
+ }
+ 
+ 
+ /*
+  * Try to remove this skb from any queue.
+  * This does not necessarily succeed (even if we find it),
+  * as it may just be being sent by the driver layers....
+  */
+ void
+ dev_delque(struct sk_buff *skb)
+ {
+   struct ifqueue *q, **p;
+   unsigned long flags;
+   int i;
+ 
+   /* Some sanity checks... */
+   if (skb == NULL) {
+ 	DPRINTF((DBG_PKT, "NET: dev_delque: skb = NULL !\n"));
+ 	return;
+   }
+   if (skb->dev == NULL) {
+ 	DPRINTF((DBG_PKT, "NET: dev_delque: skb->dev = NULL !\n"));
+ 	return;
+   }
+   DPRINTF((DBG_PKT, "NET: dev_delque: GO\n"));
+ 
+   /* Remove packet from any interface queues. */
+   save_flags(flags);
+   cli();
+   for(i = 0; i < IFF_MAX_PRIO; i++) {
+ 
+ 	/* Walk through the queue of this device. */
+ 	p = &skb->dev->if_qhead[i];
+ 	while ((q = *p)) {
+ 	    if (q->skb == skb) {
+ 	        *p = q->next;
+ 	        skb_release(skb);
+ 	        kfree_s(q, sizeof(struct ifqueue));
+ 		skb->dev->if_qlen--;
+ 	    } else {
+ 	        p = &q->next;
+ 	    }
+ 	}
+   }
+   restore_flags(flags);
+   DPRINTF((DBG_PKT, "NET: dev_delque: GO 2\n"));
+ 
+   /* Remove from the ARP queue. */
+   arp_delque(skb);
+   DPRINTF((DBG_PKT, "NET: dev_delque: DONE\n"));
+ }
*** original/net/socket/proto.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/proto.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,119 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This file handles the upcalls to the various protocol layers
+  *		when a packet arrives.  It also handles the registration of
+  *		any new protocols.
+  *
+  * Version:	@(#)proto.c	1.2.3	03/20/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/socket.h>
+ #include <linux/sockios.h>
+ #include <linux/string.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include "socket.h"
+ 
+ 
+ static struct packet_type *packet_type_base = NULL;
+ 
+ 
+ /* Add a packet ID to the list. */
+ int
+ net_add_packet(struct packet_type *pt)
+ {
+   struct packet_type *p1;
+ 
+   cli();
+   pt->next = packet_type_base;
+ 
+   /* See if we need to copy it. */
+   for (p1 = packet_type_base; p1 != NULL; p1 = p1->next) {
+ 	if (p1->type == pt->type) {
+ 		pt->copy = 1;
+ 		break;
+ 	}
+   }
+   packet_type_base = pt;
+   sti();
+   DPRINTF((DBG_PRO, "NET: add_packet: added protocol 0x%04X\n", pt->type));
+   return(0);
+ }
+ 
+ 
+ /* Remove a packet ID from the list. */
+ void
+ net_del_packet(struct packet_type *pt)
+ {
+   struct packet_type *lpt, *pt1;
+ 
+   if (pt == packet_type_base) {
+ 	cli();
+ 	packet_type_base = pt->next;
+ 	sti();
+ 	DPRINTF((DBG_PRO, "NET: del_packet: deleted protocol 0x%04X\n",
+ 								pt->type));
+ 	return;
+   }
+ 
+   lpt = NULL;
+   for (pt1 = packet_type_base; pt1->next != NULL; pt1 = pt1->next) {
+ 	if (pt1->next == pt ) {
+ 		cli();
+ 		if (!pt->copy && lpt) lpt->copy = 0;
+ 		pt1->next = pt->next;
+ 		sti();
+ 		DPRINTF((DBG_PRO, "NET: del_packet: deleted protocol 0x%04X\n",
+ 								pt->type));
+ 		return;
+ 	}
+ 	if (pt1->next -> type == pt ->type) lpt = pt1->next;
+   }
+ }
+ 
+ 
+ /* Pass an incoming packet to all parties interested in it. */
+ int
+ net_proto_send(unsigned short type, struct sk_buff *skb)
+ {
+   struct packet_type *pp;
+   int found = 0;
+ 
+   for (pp = packet_type_base; pp != NULL; pp = pp->next) {
+ 	DPRINTF((DBG_PRO, "NET: proto_send: trying proto \"%s\": ", pp->name));
+ 	if (pp->type == type || pp->type == ETH_P_802_3) {
+ 		struct sk_buff *skb2;
+ 
+ 		DPRINTF((DBG_PRO, "[YES]\n"));
+ 		if (pp->copy) {
+ 			skb2 = skb_duplicate(skb);
+ 		} else {
+ 			skb2 = skb;
+ 		}
+ 		found = 1;
+ 
+ 		/* Kick the protocol handler. */
+ 		pp->handler(skb2->dev, skb2, pp);
+ 		continue;
+ 	}
+ 	DPRINTF((DBG_PRO, "[NO]\n"));
+   }
+   return(found);
+ }
*** original/net/socket/skbuff.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/skbuff.c	Mon May  9 13:13:15 1994
***************
*** 0 ****
--- 1,356 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		A re-implementation of the NET buffer handling functions
+  *		and data.  The structure format is described in the NET
+  *		<linux/skbuff.h> include file.
+  *
+  *		The bigbuff system is a horrible hack that allocates a
+  *		certain amount of static kernel memory to buffers for large
+  *		packets. There are SKB_BIG_NUM buffers of size SKB_BIG_SIZE.
+  *		Packets for which allocation with kmalloc is likely to fail
+  *		(pre-fragment large UDP packets from NFS for example) are
+  *		allocated preferentially into bigbuffs, and if there aren't
+  *		any as a last ditch attempt vmalloc() is used. The latter is
+  *		dangerous as it is not thread clean - i.e. when the packet is
+  *		being freed it may be done from a background thread.
+  *
+  * Version:	@(#)skbuff.c	1.2.11	04/05/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Johannes Stille, <johannes@titan.westfalen.de>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *		Alex Bligh, <alex@cconcepts.co.uk>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *		Portions Copyright 1993 Matthew Dillon
+  *		Portions Copyright 1994 Alex Bligh
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/if.h>
+ #include <linux/skbuff.h>
+ #include "socket.h"
+ 
+ 
+ #define SKB_PAGE_SIZE	4096		/* should be PAGE_SIZE		*/
+ #define SKB_BIG_NUM	12
+ #define SKB_BIG_SIZE	9000
+ 
+ 
+ #define skb_is_big(len) ((len) > (SKB_PAGE_SIZE - sizeof(struct sk_buff)))
+ 
+ 
+ /* Tracking stuff. */
+ typedef struct SkNode {
+   struct SkNode		*sn_Next;
+   struct sk_buff	*sn_Skb;
+ } SkNode;
+ 
+ 
+ static SkNode *SkTrackBase = NULL;
+ static SkNode *SkFreeBase = NULL;
+ static char SkBigBuff[SKB_BIG_NUM][SKB_BIG_SIZE];
+ static struct sk_buff *SkBigBufp[SKB_BIG_NUM] = { NULL };
+  
+ 
+ /* Find a free sk_bigbuff, and return NULL if fails. */
+ static struct sk_buff *
+ skb_big_alloc(unsigned long size)
+ {
+   unsigned long flags;
+   int i;
+ 
+   if (size > SKB_BIG_SIZE) {
+ 	printk("NET: skbuff: Warning - big buffers too small, increase SKB_BIG_SIZE\n");
+ 	return(NULL);
+   }
+ 
+   save_flags(flags);
+   cli();
+   for (i = 0; i < SKB_BIG_NUM; i++) {
+ 	if (SkBigBufp[i] == NULL ) {
+ 		SkBigBufp[i] = (struct sk_buff *) SkBigBuff[i];
+ 		restore_flags(flags);
+ 
+ 		DPRINTF((DBG_SKB, "NET: big_alloc: claiming buff %d\n", i));
+ 		return(SkBigBufp[i]);
+ 	}
+   }
+   restore_flags(flags);
+   printk("NET: skbuff: Warning - out of big buffers, increase SKB_BIG_NUM\n");
+   return(NULL);
+ }
+ 
+ 
+ /*
+  * Free a big buffer - there's no need to protect this one with cli()
+  * as noone will be freeing the same buffer at the same time. But I've
+  * done it anyway.
+  */
+ static int
+ skb_big_free(struct sk_buff *skb)
+ {
+   unsigned long flags;
+   int i;
+ 
+   save_flags(flags);
+   cli();
+   for (i = 0; i < SKB_BIG_NUM; i++ ) {
+ 	if (SkBigBufp[i] == skb) {
+ 		SkBigBufp[i] = NULL;
+ 		restore_flags(flags);
+ 		DPRINTF((DBG_SKB, "NET: big_free: freeing buff %d\n", i));
+ 		return(0);
+ 	}
+   }
+   restore_flags(flags);
+   return(1);
+ }
+ 
+ 
+ /*
+  * Tracking commands:
+  *	-1	test and delete sk_buff from tracking list
+  *	0	test only
+  *	1	add new sk_buff to tracking list (returns 0)
+  *
+  * Returns the associated SkNode if the operation succeeded, NULL otherwise.
+  * On delete (cmd=-1), returns SkNode (now on the free list), expecting that
+  * you will not really do anything with it.
+  */
+ static SkNode *
+ skb_track(struct sk_buff *skb, short cmd)
+ {
+   unsigned long flags;
+   SkNode *sn;
+ 
+   save_flags(flags);
+   cli();
+ 
+   if (cmd > 0) {
+ 	/*
+ 	 * Allocate tracking node, not embedded in skb for
+ 	 * better protection of the tracking list in case
+ 	 * of memory corruption.
+          */
+ 	if ((sn = SkFreeBase) == NULL) {
+ 		sn = kmalloc(sizeof(SkNode), GFP_ATOMIC);
+ 	} else {
+ 		SkFreeBase = sn->sn_Next;
+ 	}
+ 	if (sn) {
+ 		sn->sn_Next = SkTrackBase;
+ 		sn->sn_Skb = skb;
+ 		SkTrackBase = sn;
+ 	}
+   } else {
+ 	SkNode **snp;
+ 
+ 	for (snp = &SkTrackBase; (sn = *snp); snp = &sn->sn_Next) {
+ 		if (sn->sn_Skb == skb) { /* sk_buff found! */
+ 			if (cmd < 0) {
+ 				*snp = sn->sn_Next; /* unlink from SkTrackBase */
+ 				sn->sn_Next = SkFreeBase; /* link into SkFreeBase */
+ 				SkFreeBase = sn;
+ 			}
+ 			break;
+ 		}
+ 	}
+   }
+ 
+   restore_flags(flags);
+   return(sn);
+ }
+ 
+ 
+ /* Kill an skb. */
+ static void
+ skb_kill(struct sk_buff *skb)
+ {
+   if (skb == NULL) return;
+ 
+   /* If the size indicates "big", try the BigFree first. */
+   if (skb_is_big(skb->mem_len)) {
+ 	DPRINTF((DBG_SKB, "NET: skbuff_kill: trying big_free\n"));
+ 	if (skb_big_free(skb) != 0) {
+ 		DPRINTF((DBG_SKB, "NET: skbuff_kill: using vfree\n"));
+ 		vfree((void *) skb->mem_addr);
+ 	}
+   } else {
+ 	DPRINTF((DBG_SKB, "NET: skbuff_kill: using kfree\n"));
+ 	kfree_s((void *)skb->mem_addr, skb->mem_len);
+   }
+ }
+ 
+ 
+ /* Unconditionally free an skb buffer.  Usually called via skb_release(). */
+ void
+ skb_free(struct sk_buff *skb)
+ {
+   DPRINTF((DBG_SKB, "NET: skbuff_free: skb=0x%lX\n", (long) skb));
+ 
+   if (skb_track(skb, -1) == NULL) {
+ 	printk("NET: skb_free: skb %08lX already freed\n", (long) skb);
+ 	return;
+   }
+   if (skb->mem_addr != skb) {
+ 	printk("NET: skb_free: skb->mem_addr != skb %08lX:%08lX\n",
+ 		(long) skb->mem_addr, (long) skb);
+ 	return;
+   }
+ 
+   /*
+    * Delayed free.  Do NOT depend on this action!  This exists solely to
+    * (attempt to) prevent use-after-free bugs from causing a total crash 
+    * until we get the networking debugged.
+    */
+   {
+ 	static struct sk_buff *SkDelFree = NULL;
+ 	struct sk_buff *sktmp;
+ 	unsigned long flags;
+ 
+ 	save_flags(flags);
+ 	cli();
+ 	sktmp = SkDelFree;
+ 	SkDelFree = skb;
+ 	skb = sktmp;
+ 	restore_flags(flags);
+   }
+ 
+   /* Nix the (delayed) skb. */
+   skb_kill(skb);
+ }
+ 
+ 
+ /*
+  * This buffer is not used by the DEV layer anymore, so we can
+  * clear the lock and, if needed, free any allocated memory.
+  */
+ void
+ skb_release(struct sk_buff *skb)
+ {
+   DPRINTF((DBG_SKB, "NET: skbuff_release: skb=0x%lX\n", (int) skb));
+ 
+   if (skb_track(skb, 0) == NULL) {
+ 	printk("NET: skb_release: skb %08lX already freed\n", (long) skb);
+ 	return;
+   }
+   if (skb->mem_addr == (void *)0xF8F8F8F8) {
+ 	printk("NET: release: dead memory at skb=0x%0lX !\n", (long) skb);
+ 	return;
+   }
+   cli();
+   if (skb->lock) skb->lock--;
+   if (skb->free && !skb->lock) {
+ 	sti();
+ 	skb_free(skb);
+   }
+   sti();
+ }
+ 
+ 
+ struct sk_buff *
+ skb_pcreate(int len, int priority)
+ {
+   struct sk_buff *skb;
+ 
+   DPRINTF((DBG_SKB, "NET: skb_pcreate: size=%u prio=%d\n", len, priority));
+ 
+   if (intr_count && priority != GFP_ATOMIC) {
+ 	static int count = 0;
+ 
+ 	if (++count < 5) {
+ 		printk("NET: skb_pcreate: called nonatomically from interrupt %08lx\n",
+ 					((unsigned long *)&len)[-1]);
+ 		priority = GFP_ATOMIC;
+ 	}
+   }
+ 
+   if (len <= 0) {
+ 	printk("NET: skb_pcreate: impossible size %d requested!\n", len);
+ 	return(NULL);
+   }
+ 
+   len += sizeof(struct sk_buff);
+   if (skb_is_big(len)) {
+ 	DPRINTF((DBG_SKB, "NET: skb_pcreate: allocating large buffer\n"));
+ 	if ((skb = skb_big_alloc(len)) == NULL) {
+ 		DPRINTF((DBG_SKB, "NET: skb_pcreate: using vmalloc\n"));
+ 		skb = (struct sk_buff *) vmalloc(len);
+ 	}
+   } else {
+ 	DPRINTF((DBG_SKB, "NET: skb_pcreate: using kmalloc\n"));
+ 	skb = (struct sk_buff *) kmalloc(len, priority);
+   }
+ 
+   if (skb == NULL) return(NULL);
+   if (skb_track(skb, 1) == NULL) {
+ 	skb_kill(skb);
+ 	return(NULL);
+   }
+ 
+   memset(skb, 0, sizeof(struct sk_buff));
+   skb->mem_addr = skb;
+   skb->mem_len = len;
+   skb->len = len - sizeof(struct sk_buff);
+   skb->raw = (unsigned char *) (skb + 1);
+   skb->free = 1;
+   skb->lock = 0;
+   skb->arp = 0;
+ 
+   return(skb);
+ }
+ 
+ 
+ struct sk_buff *
+ skb_create(int len)
+ {
+   return(skb_pcreate(len, GFP_ATOMIC));
+ }
+ 
+ 
+ struct sk_buff *
+ skb_duplicate(struct sk_buff *skb)
+ {
+   struct sk_buff *new;
+ 
+   new = skb_pcreate((skb->mem_len - sizeof(struct sk_buff)), GFP_ATOMIC);
+   if (new == NULL) return(NULL);
+ 
+   if (skb_track(new, 1) == NULL) {
+ 	skb_kill(new);
+ 	new = NULL;
+   } else {
+ 	memcpy(new, skb, skb->mem_len);
+ 	new->mem_addr = new;
+ 	new->mem_len = skb->mem_len;
+ 	new->raw = (char *)new + ((char *)skb->raw - (char *)skb);
+   }
+   return(new);
+ }
+ 
+ 
+ void
+ skb_print(struct sk_buff *skb)
+ {
+   if (skb == NULL) {
+ 	printk("NET: skb_print: NULL !\n");
+ 	return;
+   }
+   printk("  mem_addr=0x%lX mem_len=%d len=%d next=0x%lX\n",
+ 	(long) skb->mem_addr, skb->mem_len, skb->len, (long) skb->next);
+   printk("  used=%d free=%d arp=%d\n", skb->used, skb->free, skb->arp);
+   printk("  sk=0x%lX link3=0x%lX\n", (long) skb->sk, (long) skb->link3);
+ }
*** original/net/socket/sock.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/sock.c	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,1008 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		This file handles the basic socket operations at the system-
+  *		call (i.e. "upper") level.  For most operations, it checks
+  *		the sanity of some arguments, and then passes the request
+  *		to the address-family handler.
+  *
+  * Version:	@(#)sock.c	1.2.10	05/05/94
+  *
+  * Authors:	Orest Zborowski, <obz@Kodak.COM>
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Alan Cox, <iiitac@pyramid.swansea.ac.uk>
+  *
+  *		Copyright 1991 Orest Zborowski
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <linux/signal.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/stat.h>
+ #include <linux/socket.h>
+ #include <linux/fcntl.h>
+ #include <linux/major.h>
+ #include <linux/termios.h>
+ #include <linux/timer.h>
+ #include <linux/ddi.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <linux/if_arp.h>
+ #include <linux/if_ether.h>
+ #include "socket.h"
+ 
+ 
+ #define VERSION	"2E BETA-6"
+ 
+ 
+ static int	sock_lseek(struct inode *inode, struct file *file,
+ 			   off_t offset, int whence);
+ static int	sock_read(struct inode *inode, struct file *file,
+ 			  char *buf, int size);
+ static int	sock_write(struct inode *inode, struct file *file,
+ 			   char *buf, int size);
+ static int	sock_readdir(struct inode *inode, struct file *file,
+ 			     struct dirent *dirent, int count);
+ static void	sock_close(struct inode *inode, struct file *file);
+ static int	sock_select(struct inode *inode, struct file *file,
+ 			    int which, select_table *seltable);
+ 
+ 
+ static struct file_operations socket_file_ops = {
+   sock_lseek,
+   sock_read,
+   sock_write,
+   sock_readdir,
+   sock_select,
+   net_ioctl,
+   NULL,			/* mmap */
+   NULL,			/* no special open code... */
+   sock_close
+ };
+ 
+ static struct socket	sockets[NSOCKETS];
+ static struct wait_queue *socket_wait_free = NULL;
+ int			(*net_io_vec[256])(struct inode *, struct file *,
+ 					  unsigned int, unsigned long);
+ 
+ 
+ #define last_socket	(sockets + NSOCKETS - 1)
+ 
+ 
+ /* Obtains the first available file descriptor and sets it up for use. */
+ static int
+ get_fd(struct inode *inode)
+ {
+   int fd;
+   struct file *file;
+ 
+   /* Find a file descriptor suitable for return to the user. */
+   file = get_empty_filp();
+   if (!file) return(-1);
+   for (fd = 0; fd < NR_OPEN; ++fd)
+ 	if (!current->files->fd[fd]) break;
+   if (fd == NR_OPEN) {
+ 	file->f_count = 0;
+ 	return(-1);
+   }
+   FD_CLR(fd, &current->files->close_on_exec);
+   current->files->fd[fd] = file;
+   file->f_op = &socket_file_ops;
+   file->f_mode = 3;
+   file->f_flags = 0;
+   file->f_count = 1;
+   file->f_inode = inode;
+   if (inode) inode->i_count++;
+   file->f_pos = 0;
+   return(fd);
+ }
+ 
+ 
+ /*
+  * Reverses the action of get_fd() by releasing the file. it closes
+  * the descriptor, but makes sure it does nothing more. Called when
+  * an incomplete socket must be closed, along with sock_release().
+  */
+ static inline void
+ toss_fd(int fd)
+ {
+   sys_close(fd);		/* the count protects us from iput */
+ }
+ 
+ 
+ struct socket *
+ socki_lookup(struct inode *inode)
+ {
+   struct socket *sock;
+ 
+   if ((sock = inode->i_socket) != NULL) {
+ 	if (sock->state != SS_FREE && SOCK_INODE(sock) == inode) return(sock);
+ 	printk("NET: uhhuh. Found stale inode->i_socket pointer !\n");
+   }
+   for (sock = sockets; sock <= last_socket; ++sock) {
+ 	if (sock->state != SS_FREE && SOCK_INODE(sock) == inode) {
+ 		printk("NET: uhhuh. Found socket despite no "
+ 					" inode->i_socket pointer\n");
+ 		return(sock);
+ 	}
+   }
+   return(NULL);
+ }
+ 
+ 
+ static inline struct socket *
+ sockfd_lookup(int fd, struct file **pfile)
+ {
+   struct file *file;
+ 
+   if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) return(NULL);
+   if (pfile) *pfile = file;
+   return(socki_lookup(file->f_inode));
+ }
+ 
+ 
+ static struct socket *
+ sock_alloc(int wait)
+ {
+   struct socket *sock;
+ 
+   while (1) {
+ 	cli();
+ 	for (sock = sockets; sock <= last_socket; ++sock) {
+ 		if (sock->state == SS_FREE) {
+ 			sock->state = SS_UNCONNECTED;
+ 			sti();
+ 			sock->flags = 0;
+ 			sock->ops = NULL;
+ 			sock->data = NULL;
+ 			sock->conn = NULL;
+ 			sock->iconn = NULL;
+ 
+ 			/*
+ 			 * This really shouldn't be necessary, but everything
+ 			 * else depends on inodes, so we grab it.
+ 			 * Sleeps are also done on the i_wait member of this
+ 			 * inode.  The close system call will iput this inode
+ 			 * for us.
+ 			 */
+ 			if (!(SOCK_INODE(sock) = get_empty_inode())) {
+ 				printk("NET: alloc: no more inodes\n");
+ 				sock->state = SS_FREE;
+ 				return(NULL);
+ 			}
+ 			SOCK_INODE(sock)->i_mode = S_IFSOCK;
+ 			SOCK_INODE(sock)->i_uid = current->euid;
+ 			SOCK_INODE(sock)->i_gid = current->egid;
+ 			SOCK_INODE(sock)->i_socket = sock;
+ 
+ 			sock->wait = &SOCK_INODE(sock)->i_wait;
+ 			DPRINTF((DBG_NET, "NET: sock_alloc: sk 0x%x, ino 0x%x\n",
+ 			       			sock, SOCK_INODE(sock)));
+ 			return(sock);
+ 		}
+ 	}
+ 	sti();
+ 	if (!wait) return(NULL);
+ 	DPRINTF((DBG_NET, "NET: sock_alloc: no free sockets, sleeping...\n"));
+ 	interruptible_sleep_on(&socket_wait_free);
+ 	if (current->signal & ~current->blocked) {
+ 		DPRINTF((DBG_NET, "NET: sock_alloc: sleep was interrupted\n"));
+ 		return(NULL);
+ 	}
+ 	DPRINTF((DBG_NET, "NET: sock_alloc: wakeup... trying again...\n"));
+   }
+ }
+ 
+ 
+ static inline void
+ sock_release_peer(struct socket *peer)
+ {
+   peer->state = SS_DISCONNECTING;
+   wake_up_interruptible(peer->wait);
+ }
+ 
+ 
+ static void
+ sock_release(struct socket *sock)
+ {
+   int oldstate;
+   struct inode *inode;
+   struct socket *peersock, *nextsock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_release: socket 0x%x, inode 0x%x\n",
+ 						sock, SOCK_INODE(sock)));
+   if ((oldstate = sock->state) != SS_UNCONNECTED)
+ 			sock->state = SS_DISCONNECTING;
+ 
+   /* Wake up anyone waiting for connections. */
+   for (peersock = sock->iconn; peersock; peersock = nextsock) {
+ 	nextsock = peersock->next;
+ 	sock_release_peer(peersock);
+   }
+ 
+   /*
+    * Wake up anyone we're connected to. First, we release the
+    * protocol, to give it a chance to flush data, etc.
+    */
+   peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL;
+   if (sock->ops) sock->ops->release(sock, peersock);
+   if (peersock) sock_release_peer(peersock);
+   inode = SOCK_INODE(sock);
+   sock->state = SS_FREE;		/* this really releases us */
+   wake_up_interruptible(&socket_wait_free);
+ 
+   /* We need to do this. If sock alloc was called we already have an inode. */
+   iput(inode);
+ }
+ 
+ 
+ static int
+ sock_lseek(struct inode *inode, struct file *file, off_t offset, int whence)
+ {
+   DPRINTF((DBG_NET, "NET: sock_lseek: huh?\n"));
+   return(-ESPIPE);
+ }
+ 
+ 
+ static int
+ sock_read(struct inode *inode, struct file *file, char *ubuf, int size)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_read: buf=0x%x, size=%d\n", ubuf, size));
+   if (!(sock = socki_lookup(inode))) {
+ 	printk("NET: read: can't find socket for inode!\n");
+ 	return(-EBADF);
+   }
+   if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+   return(sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)));
+ }
+ 
+ 
+ static int
+ sock_write(struct inode *inode, struct file *file, char *ubuf, int size)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_write: buf=0x%x, size=%d\n", ubuf, size));
+   if (!(sock = socki_lookup(inode))) {
+ 	printk("NET: write: can't find socket for inode!\n");
+ 	return(-EBADF);
+   }
+   if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+   return(sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)));
+ }
+ 
+ 
+ static int
+ sock_readdir(struct inode *inode, struct file *file, struct dirent *dirent,
+ 	     int count)
+ {
+   DPRINTF((DBG_NET, "NET: sock_readdir: huh?\n"));
+   return(-EBADF);
+ }
+ 
+ 
+ static int
+ sock_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_select: inode = 0x%x, kind = %s\n", inode,
+        (sel_type == SEL_IN) ? "in" :
+        (sel_type == SEL_OUT) ? "out" : "ex"));
+   if (!(sock = socki_lookup(inode))) {
+ 	printk("NET: select: can't find socket for inode!\n");
+ 	return(0);
+   }
+ 
+   /* We can't return errors to select, so its either yes or no. */
+   if (sock->ops && sock->ops->select)
+ 	return(sock->ops->select(sock, sel_type, wait));
+   return(0);
+ }
+ 
+ 
+ void
+ sock_close(struct inode *inode, struct file *file)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_close: inode=0x%x (cnt=%d)\n", inode, inode->i_count));
+ 
+   /* It's possible the inode is NULL if we're closing an unfinished socket. */
+   if (!inode) return;
+   if (!(sock = socki_lookup(inode))) {
+ 	printk("NET: close: can't find socket for inode!\n");
+ 	return;
+   }
+   sock_release(sock);
+ }
+ 
+ 
+ int
+ sock_awaitconn(struct socket *mysock, struct socket *servsock)
+ {
+   struct socket *last;
+ 
+   DPRINTF((DBG_NET, "NET: sock_awaitconn: trying to connect socket 0x%x to 0x%x\n",
+ 							mysock, servsock));
+   if (!(servsock->flags & SO_ACCEPTCON)) {
+ 	DPRINTF((DBG_NET, "NET: sock_awaitconn: server not accepting connections\n"));
+ 	return(-EINVAL);
+   }
+ 
+   /* Put ourselves on the server's incomplete connection queue. */
+   mysock->next = NULL;
+   cli();
+   if (!(last = servsock->iconn)) servsock->iconn = mysock;
+     else {
+ 	while (last->next) last = last->next;
+ 	last->next = mysock;
+   }
+   mysock->state = SS_CONNECTING;
+   mysock->conn = servsock;
+   sti();
+ 
+   /*
+    * Wake up server, then await connection. server will set state to
+    * SS_CONNECTED if we're connected.
+    */
+   wake_up_interruptible(servsock->wait);
+   if (mysock->state != SS_CONNECTED) {
+ 	interruptible_sleep_on(mysock->wait);
+ 	if (mysock->state != SS_CONNECTED &&
+ 	    mysock->state != SS_DISCONNECTING) {
+ 		/*
+ 		 * if we're not connected we could have been
+ 		 * 1) interrupted, so we need to remove ourselves
+ 		 *    from the server list
+ 		 * 2) rejected (mysock->conn == NULL), and have
+ 		 *    already been removed from the list
+ 		 */
+ 		if (mysock->conn == servsock) {
+ 			cli();
+ 			if ((last = servsock->iconn) == mysock)
+ 					servsock->iconn = mysock->next;
+ 			else {
+ 				while (last->next != mysock) last = last->next;
+ 				last->next = mysock->next;
+ 			}
+ 			sti();
+ 		}
+ 		return(mysock->conn ? -EINTR : -EACCES);
+ 	}
+   }
+   return(0);
+ }
+ 
+ 
+ /*
+  * Perform the socket system call. we locate the appropriate
+  * family, then create a fresh socket.
+  */
+ static int
+ sock_socket(int family, int type, int protocol)
+ {
+   int i, fd;
+   struct socket *sock;
+   struct proto_ops *ops;
+ 
+   DPRINTF((DBG_NET, "NET: sock_socket: family = %d, type = %d, protocol = %d\n",
+ 						family, type, protocol));
+ 
+   /* Locate the correct protocol family. */
+   ops = net_af_get(family);
+   if (ops == NULL) {
+ 	DPRINTF((DBG_NET, "NET: sock_socket: family %d not found\n", family));
+ 	return(-EAFNOSUPPORT);
+   }
+ 
+   /*
+    * Check that this is a type that we know how to manipulate and
+    * the protocol makes sense here. The family can still reject the
+    * protocol later.
+    *
+    * NOTE: PACKET and SEQPACKET modes not supported at the moment.
+    *       -FvK
+    */
+   if (((type != SOCK_STREAM) &&
+        (type != SOCK_DGRAM) &&
+        (type != SOCK_RAW)) ||
+       (protocol < 0)) return(-ESOCKTNOSUPPORT);
+ 
+   /*
+    * allocate the socket and allow the family to set things up. if
+    * the protocol is 0, the family is instructed to select an appropriate
+    * default.
+    */
+   if (! (sock = sock_alloc(1))) {
+ 	printk("NET: socket: no more sockets\n");
+ 	return(-EAGAIN);	/* FIXME: BSD: ENOBUFS -FvK */
+   }
+   sock->type = type;
+   sock->ops = ops;
+   if ((i = sock->ops->create(sock, protocol)) < 0) {
+ 	sock_release(sock);
+ 	return(i);
+   }
+ 
+   if ((fd = get_fd(SOCK_INODE(sock))) < 0) {
+ 	sock_release(sock);
+ 	return(-EMFILE);
+   }
+ 
+   return(fd);
+ }
+ 
+ 
+ static int
+ sock_socketpair(int family, int type, int protocol, unsigned long usockvec[2])
+ {
+   struct socket *sock1, *sock2;
+   int err, fd1, fd2, i;
+ 
+   DPRINTF((DBG_NET, "NET: sock_socketpair: family = %d, type = %d, protocol = %d\n",
+ 						family, type, protocol));
+ 
+   /*
+    * Obtain the first socket and check if the underlying protocol
+    * supports the socketpair call.
+    */
+   if ((fd1 = sock_socket(family, type, protocol)) < 0) return(fd1);
+   sock1 = sockfd_lookup(fd1, NULL);
+   if (!sock1->ops->socketpair) {
+ 	sys_close(fd1);
+ 	return(-EINVAL);
+   }
+ 
+   /* Now grab another socket and try to connect the two together. */
+   if ((fd2 = sock_socket(family, type, protocol)) < 0) {
+ 	sys_close(fd1);
+ 	return(-EINVAL);
+   }
+   sock2 = sockfd_lookup(fd2, NULL);
+   if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) {
+ 	sys_close(fd1);
+ 	sys_close(fd2);
+ 	return(i);
+   }
+   sock1->conn = sock2;
+   sock2->conn = sock1;
+   sock1->state = SS_CONNECTED;
+   sock2->state = SS_CONNECTED;
+ 
+   err = verify_area(VERIFY_WRITE, usockvec, 2 * sizeof(int));
+   if (err < 0) return(err);
+   put_fs_long(fd1, &usockvec[0]);
+   put_fs_long(fd2, &usockvec[1]);
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Bind a name to a socket. Nothing much to do here since its
+  * the protocol's responsibility to handle the local address.
+  */
+ static int
+ sock_bind(int fd, struct sockaddr *umyaddr, int addrlen)
+ {
+   struct socket *sock;
+   int i;
+ 
+   DPRINTF((DBG_NET, "NET: sock_bind: fd = %d\n", fd));
+   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+   if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) {
+ 	DPRINTF((DBG_NET, "NET: sock_bind: bind failed\n"));
+ 	return(i);
+   }
+   return(0);
+ }
+ 
+ 
+ /*
+  * Perform a listen. Basically, we allow the protocol to do anything
+  * necessary for a listen, and if that works, we mark the socket as
+  * ready for listening.
+  */
+ static int
+ sock_listen(int fd, int backlog)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_listen: fd = %d\n", fd));
+   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+   if (sock->state != SS_UNCONNECTED) {
+ 	DPRINTF((DBG_NET, "NET: sock_listen: socket isn't unconnected\n"));
+ 	return(-EINVAL);
+   }
+   if (sock->ops && sock->ops->listen) sock->ops->listen(sock, backlog);
+   sock->flags |= SO_ACCEPTCON;
+   return(0);
+ }
+ 
+ 
+ /*
+  * For accept, we attempt to create a new socket, set up the link
+  * with the client, wake up the client, then return the new
+  * connected fd.
+  */
+ static int
+ sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
+ {
+   struct file *file;
+   struct socket *sock, *newsock;
+   int i;
+ 
+   DPRINTF((DBG_NET, "NET: sock_accept: fd = %d\n", fd));
+   if ((fd < 0) || (fd >= NR_OPEN) ||
+       ((file = current->files->fd[fd]) == NULL)) return(-EBADF);
+  
+   if (!(sock = sockfd_lookup(fd, &file))) return(-ENOTSOCK);
+   if (sock->state != SS_UNCONNECTED) {
+ 	DPRINTF((DBG_NET, "NET: sock_accept: socket isn't unconnected\n"));
+ 	return(-EINVAL);
+   }
+   if (!(sock->flags & SO_ACCEPTCON)) {
+ 	DPRINTF((DBG_NET, "NET: sock_accept: socket not accepting connections!\n"));
+ 	return(-EINVAL);
+   }
+ 
+   if (!(newsock = sock_alloc(0))) {
+ 	printk("NET: accept: no more sockets\n");
+ 	return(-EAGAIN);
+   }
+   newsock->type = sock->type;
+   newsock->ops = sock->ops;
+   if ((i = sock->ops->dup(newsock, sock)) < 0) {
+ 	sock_release(newsock);
+ 	return(i);
+   }
+ 
+   i = newsock->ops->accept(sock, newsock, file->f_flags);
+   if ( i < 0) {
+ 	sock_release(newsock);
+ 	return(i);
+   }
+ 
+   if ((fd = get_fd(SOCK_INODE(newsock))) < 0) {
+ 	sock_release(newsock);
+ 	return(-EINVAL);
+   }
+ 
+   DPRINTF((DBG_NET, "NET: sock_accept: conn socket 0x%X via 0x%X\n", sock, newsock));
+ 
+   if (upeer_sockaddr)
+ 	newsock->ops->getname(newsock, upeer_sockaddr, upeer_addrlen, 1);
+ 
+   return(fd);
+ }
+ 
+ 
+ /* Attempt to connect to a socket with the server address. */
+ static int
+ sock_connect(int fd, struct sockaddr *uservaddr, int addrlen)
+ {
+   struct socket *sock;
+   struct file *file;
+   int i;
+ 
+   DPRINTF((DBG_NET, "NET: sock_connect: fd = %d\n", fd));
+   if ((fd < 0) || (fd >= NR_OPEN) ||
+       ((file = current->files->fd[fd]) == NULL)) return(-EBADF);
+ 
+   if (!(sock = sockfd_lookup(fd, &file))) return(-ENOTSOCK);
+   switch(sock->state) {
+ 	case SS_UNCONNECTED:
+ 		/* This is ok... continue with connect */
+ 		break;
+ 	case SS_CONNECTED:
+ 		/* Socket is already connected */
+ 		return -EISCONN;
+ 	case SS_CONNECTING:
+ 		/* Not yet connected... we will check this. */
+ 		return(sock->ops->connect(sock, uservaddr,
+ 					  addrlen, file->f_flags));
+ 	default:
+ 		DPRINTF((DBG_NET, "NET: sock_connect: socket not unconnected\n"));
+ 		return(-EINVAL);
+   }
+   i = sock->ops->connect(sock, uservaddr, addrlen, file->f_flags);
+   if (i < 0) {
+ 	DPRINTF((DBG_NET, "NET: sock_connect: connect failed\n"));
+ 	return(i);
+   }
+   return(0);
+ }
+ 
+ 
+ static int
+ sock_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_getsockname: fd = %d\n", fd));
+   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+   return(sock->ops->getname(sock, usockaddr, usockaddr_len, 0));
+ }
+ 
+ 
+ static int
+ sock_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
+ {
+   struct socket *sock;
+ 
+   DPRINTF((DBG_NET, "NET: sock_getpeername: fd = %d\n", fd));
+   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+   return(sock->ops->getname(sock, usockaddr, usockaddr_len, 1));
+ }
+ 
+ 
+ static int
+ sock_send(int fd, void * buff, int len, unsigned flags)
+ {
+   struct socket *sock;
+   struct file *file;
+ 
+   DPRINTF((DBG_NET, "NET: sock_send(fd = %d, buff = %X, len = %d, flags = %X)\n",
+        						fd, buff, len, flags));
+ 
+   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
+ 								return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 
+   return(sock->ops->send(sock, buff, len, (file->f_flags & O_NONBLOCK), flags));
+ }
+ 
+ 
+ static int
+ sock_sendto(int fd, void * buff, int len, unsigned flags,
+ 	   struct sockaddr *addr, int addr_len)
+ {
+   struct socket *sock;
+   struct file *file;
+ 
+   DPRINTF((DBG_NET, "NET: sock_sendto(fd = %d, buff = %X, len = %d, flags = %X,"
+ 	 " addr=%X, alen = %d\n", fd, buff, len, flags, addr, addr_len));
+ 
+   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
+ 								return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 
+   return(sock->ops->sendto(sock, buff, len, (file->f_flags & O_NONBLOCK),
+ 			   flags, addr, addr_len));
+ }
+ 
+ 
+ static int
+ sock_recv(int fd, void * buff, int len, unsigned flags)
+ {
+   struct socket *sock;
+   struct file *file;
+ 
+   DPRINTF((DBG_NET, "NET: sock_recv(fd = %d, buff = %X, len = %d, flags = %X)\n",
+ 						fd, buff, len, flags));
+ 
+   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
+ 								return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 
+   return(sock->ops->recv(sock, buff, len,(file->f_flags & O_NONBLOCK), flags));
+ }
+ 
+ 
+ static int
+ sock_recvfrom(int fd, void * buff, int len, unsigned flags,
+ 	     struct sockaddr *addr, int *addr_len)
+ {
+   struct socket *sock;
+   struct file *file;
+ 
+   DPRINTF((DBG_NET, "NET: sock_recvfrom(fd = %d, buff = %X, len = %d, flags = %X,"
+ 	" addr=%X, alen=%X\n", fd, buff, len, flags, addr, addr_len));
+ 
+   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
+ 								return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 
+   return(sock->ops->recvfrom(sock, buff, len, (file->f_flags & O_NONBLOCK),
+ 			     flags, addr, addr_len));
+ }
+ 
+ 
+ static int
+ sock_setsockopt(int fd, int level, int optname, char *optval, int optlen)
+ {
+   struct socket *sock;
+   struct file *file;
+ 	
+   DPRINTF((DBG_NET, "NET: sock_setsockopt(fd=%d, level=%d, optname=%d,\n",
+ 							fd, level, optname));
+   DPRINTF((DBG_NET, "                     optval = %X, optlen = %d)\n",
+ 							optval, optlen));
+ 
+   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
+ 								return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 
+   return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
+ }
+ 
+ 
+ static int
+ sock_getsockopt(int fd, int level, int optname, char *optval, int *optlen)
+ {
+   struct socket *sock;
+   struct file *file;
+ 
+   DPRINTF((DBG_NET, "NET: sock_getsockopt(fd=%d, level=%d, optname=%d,\n",
+ 						fd, level, optname));
+   DPRINTF((DBG_NET, "                     optval = %X, optlen = %X)\n",
+ 						optval, optlen));
+ 
+   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
+ 								return(-EBADF);
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 	    
+   if (!sock->ops || !sock->ops->getsockopt) return(0);
+   return(sock->ops->getsockopt(sock, level, optname, optval, optlen));
+ }
+ 
+ 
+ static int
+ sock_shutdown(int fd, int how)
+ {
+   struct socket *sock;
+   struct file *file;
+ 
+   DPRINTF((DBG_NET, "NET: sock_shutdown(fd = %d, how = %d)\n", fd, how));
+ 
+   if ((fd < 0) || (fd >= NR_OPEN) ||
+       ((file = current->files->fd[fd]) == NULL)) return(-EBADF);
+ 
+   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ 
+   return(sock->ops->shutdown(sock, how));
+ }
+ 
+ 
+ int
+ sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
+ {
+   struct socket *sock;
+ 
+   sock = socki_lookup (filp->f_inode);
+   if (sock != NULL && sock->ops != NULL && sock->ops->fcntl != NULL)
+ 				return(sock->ops->fcntl(sock, cmd, arg));
+   return(-EINVAL);
+ }
+ 
+ 
+ /*
+  * System call vectors. Since I (RIB) want to rewrite sockets as streams,
+  * we have this level of indirection. Not a lot of overhead, since more of
+  * the work is done via read/write/select directly.
+  */
+ asmlinkage int
+ sys_socketcall(int call, unsigned long *args)
+ {
+   int err;
+ 
+   switch(call) {
+ 	case SYS_SOCKET:
+ 		err = verify_area(VERIFY_READ, args, 3 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_socket(get_fs_long(args+0),
+ 				   get_fs_long(args+1),
+ 				   get_fs_long(args+2)));
+ 
+ 	case SYS_BIND:
+ 		err = verify_area(VERIFY_READ, args, 3 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_bind(get_fs_long(args+0),
+ 				 (struct sockaddr *)get_fs_long(args+1),
+ 				 get_fs_long(args+2)));
+ 
+ 	case SYS_CONNECT:
+ 		err = verify_area(VERIFY_READ, args, 3 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_connect(get_fs_long(args+0),
+ 				    (struct sockaddr *)get_fs_long(args+1),
+ 				    get_fs_long(args+2)));
+ 
+ 	case SYS_LISTEN:
+ 		err = verify_area(VERIFY_READ, args, 2 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_listen(get_fs_long(args+0),
+ 				   get_fs_long(args+1)));
+ 
+ 	case SYS_ACCEPT:
+ 		err = verify_area(VERIFY_READ, args, 3 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_accept(get_fs_long(args+0),
+ 				   (struct sockaddr *)get_fs_long(args+1),
+ 				   (int *)get_fs_long(args+2)));
+ 
+ 	case SYS_GETSOCKNAME:
+ 		err = verify_area(VERIFY_READ, args, 3 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_getsockname(get_fs_long(args+0),
+ 					(struct sockaddr *)get_fs_long(args+1),
+ 					(int *)get_fs_long(args+2)));
+ 
+ 	case SYS_GETPEERNAME:
+ 		err = verify_area(VERIFY_READ, args, 3 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_getpeername(get_fs_long(args+0),
+ 					(struct sockaddr *)get_fs_long(args+1),
+ 					(int *)get_fs_long(args+2)));
+ 
+ 	case SYS_SOCKETPAIR:
+ 		err = verify_area(VERIFY_READ, args, 4 * sizeof(long));
+ 		if (err < 0) return(err);
+ 		return(sock_socketpair(get_fs_long(args+0),
+ 				       get_fs_long(args+1),
+ 				       get_fs_long(args+2),
+ 				       (unsigned long *)get_fs_long(args+3)));
+ 
+ 	case SYS_SEND:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  4 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_send(get_fs_long(args+0),
+ 				 (void *)get_fs_long(args+1),
+ 				 get_fs_long(args+2),
+ 				 get_fs_long(args+3)));
+ 
+ 	case SYS_SENDTO:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  6 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_sendto(get_fs_long(args+0),
+ 				   (void *)get_fs_long(args+1),
+ 				   get_fs_long(args+2),
+ 				   get_fs_long(args+3),
+ 				   (struct sockaddr *)get_fs_long(args+4),
+ 				   get_fs_long(args+5)));
+ 
+ 	case SYS_RECV:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  4 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_recv(get_fs_long(args+0),
+ 				 (void *)get_fs_long(args+1),
+ 				 get_fs_long(args+2),
+ 				 get_fs_long(args+3)));
+ 
+ 	case SYS_RECVFROM:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  6 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_recvfrom(get_fs_long(args+0),
+ 				     (void *)get_fs_long(args+1),
+ 				     get_fs_long(args+2),
+ 				     get_fs_long(args+3),
+ 				     (struct sockaddr *)get_fs_long(args+4),
+ 				     (int *)get_fs_long(args+5)));
+ 
+ 	case SYS_SHUTDOWN:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  2 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_shutdown(get_fs_long(args+0),
+ 				     get_fs_long(args+1)));
+ 
+ 	case SYS_SETSOCKOPT:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  5 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_setsockopt(get_fs_long(args+0),
+ 				       get_fs_long(args+1),
+ 				       get_fs_long(args+2),
+ 				       (char *)get_fs_long(args+3),
+ 				       get_fs_long(args+4)));
+ 
+ 	case SYS_GETSOCKOPT:
+ 		err = verify_area(VERIFY_READ, args,
+ 				  5 * sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		return(sock_getsockopt(get_fs_long(args+0),
+ 				       get_fs_long(args+1),
+ 				       get_fs_long(args+2),
+ 				       (char *)get_fs_long(args+3),
+ 				       (int *)get_fs_long(args+4)));
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+ }
+ 
+ 
+ /*
+  * Handle the IOCTL system call for the NET devices.  This basically
+  * means I/O control for the SOCKET layer (future expansions could be
+  * a variable number of socket table entries, et al), and for the more
+  * general protocols like ARP and ETH.
+  */
+ static int
+ net_fioctl(struct inode *inode, struct file *file,
+ 	   unsigned int cmd, unsigned long arg)
+ {
+   int ret, i;
+ 
+   /* Dispatch on the minor device. */
+   i = MINOR(inode->i_rdev);
+   if (net_io_vec[i] != NULL)
+ 	ret = (*net_io_vec[i])(NULL, NULL, cmd, arg);
+     else ret = -ENODEV;
+ 
+   return(ret);
+ }
+ 
+ 
+ void
+ net_add_minor(int minor, int (*ptr)(struct inode *, struct file *,
+ 				     unsigned int, unsigned long))
+ {
+   if (net_io_vec[minor] == NULL) net_io_vec[minor] = ptr;
+ }
+ 
+ 
+ static struct file_operations net_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   net_fioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   NULL,		/* OPEN		*/
+   NULL		/* CLOSE	*/
+ };
+ 
+ 
+ void
+ net_init(void)
+ {
+   struct socket *sock;
+   int i;
+ 
+   printk("NET: version %s: device %d, %d sockets, %d families: ",
+ 			VERSION, NET_MAJOR, NSOCKETS, NFAMILY);
+ 
+   /* Set up our NET VFS major device. */
+   i = register_chrdev(NET_MAJOR, "net", &net_fops);
+   printk("%s\n", (i == 0) ? "OK" : "ERROR");
+   if (i != 0) return;
+ 
+   /* Release all sockets. */
+   for (sock = sockets; sock <= last_socket; sock++)
+ 				sock->state = SS_FREE;
+ 
+   /* Initialize all address (protocol) families. */
+   net_af_init();
+ 
+   /* Initialize all possible minor devices. */
+   for (i = 0; i < 255; ++i) net_io_vec[i] = NULL;
+   net_io_vec[NET_MINOR] = net_ioctl;
+ 
+   /* Set up the DEV handler. */
+   dev_init();
+ 
+   /* Initialize the DDI module. */
+   ddi_init();
+ }
*** original/net/socket/socket.h	Thu Jan  1 01:00:00 1970
--- linux/net/socket/socket.h	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,64 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		Various definitions for the NET layer.
+  *
+  * Version:	@(#)socket.h	1.2.2	03/20/94
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	NET_DEBUG
+ 
+ /* Has the master DEBUG flag been set? */
+ #ifdef	NETDBG
+ #   undef	NET_DEBUG
+ #   define	NET_DEBUG
+ #endif
+ 
+ #ifdef	NET_DEBUG
+ #   define DPRINTF(x)	net_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ #define DBG_OFF		0		/* no debug			*/
+ #define DBG_NET		1		/* general SOCKET flag		*/
+ #define DBG_DEV		2		/* interface handling module	*/
+ #define DBG_IOC		3		/* interface I/O control module	*/
+ #define DBG_PKT		4		/* packet handling module	*/
+ #define DBG_PRO		5		/* protocol handler module	*/
+ #define DBG_ETH		6		/* Ethernet support module	*/
+ #define DBG_SKB		7		/* socket buffer module		*/
+ 
+ 
+ extern int		net_debug;
+ extern int		(*net_io_vec[])(struct inode *, struct file *,
+ 					unsigned int, unsigned long);
+ 
+ 
+ extern struct socket	*socki_lookup(struct inode *inode);
+ 
+ extern void		net_bh(void *tmp);
+ extern int		net_proto_send(unsigned short type,
+ 				       struct sk_buff *skb);
+ 
+ extern int		net_ioctl(struct inode *, struct file *,
+ 				   unsigned int, unsigned long);
+ 
+ extern void		net_dprintf(int level, char *fmt, ...);
+ extern int		net_dbg_ioctl(unsigned long, int);
+ extern void		dev_init(void);
+ 
+ /* End of SOCKET.H */
*** original/net/socket/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/socket/utils.c	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,86 ----
+ /*
+  * NET		An implementation of the SOCKET network access protocol.
+  *		Various kernel-resident utility functions; mainly for
+  *		format conversion and debugging output.
+  *
+  * Version:	@(#)utils.c	1.2.1	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/in.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/skbuff.h>
+ #include <linux/if.h>
+ #include <stdarg.h>
+ #include "socket.h"
+ 
+ 
+ extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+ 
+ int net_debug = 0;			/* NET debugging flag		*/
+ 
+ 
+ /*
+  * I know that this function _should_ be using the kmalloc
+  * kernel routines, but this often causes massive system
+  * crashes.  So, I use the static buffer for now... -FvK
+  */
+ #ifdef NET_DEBUG
+ void
+ net_dprintf(int level, char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+ 
+   if ((net_debug & (1 << level)) == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* NET_DEBUG */
+ 
+ 
+ int
+ net_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		net_debug = 0;
+ 		break;
+ 
+ 	default:
+ 		if (val > 0 && val < 33) {
+ 			net_debug |= (1 << val);
+ 			return(0);
+ 		}
+ 		if (val >= 128) {
+ 			net_debug &= ~(1 << (val - 128));
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
*** original/net/unix/Makefile	Thu Jan  1 01:00:00 1970
--- linux/net/unix/Makefile	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,38 ----
+ #
+ # Makefile for the Linux UNIX protocol layer.
+ #
+ # 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 definition is now in the main makefile...
+ 
+ .c.o:
+ 	$(CC) $(CFLAGS) -c -o $*.o $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ .c.s:
+ 	$(CC) $(CFLAGS) -S -o $*.s $<
+ 
+ 
+ OBJS	= sock.o ioctl.o proc.o utils.o
+ 
+ 
+ unix.o: 	$(OBJS)
+ 		$(LD) -r -o unix.o $(OBJS)
+ 
+ clean:
+ 		rm -f core *.o *.a *.s tmp_make
+ 
+ mrproper:
+ 		rm -f core *.o *.a *.s tmp_make .depend
+ 
+ dep:
+ 		$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
*** original/net/unix/global.h	Thu Jan  1 01:00:00 1970
--- linux/net/unix/global.h	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,72 ----
+ /*
+  * UNIX		An implementation of the AF_UNIX network domain for the
+  *		LINUX operating system.  UNIX is implemented using the
+  *		BSD Socket interface as the means of communication with
+  *		the user level.
+  *
+  *		Global definitions for the UNIX module.
+  *
+  * Version:	@(#)global.h	1.1.3	03/13/94
+  *
+  * Authors:	Orest Zborowski, <obz@Kodak.COM>
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1991 Orest Zborowski
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *
+  *		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.
+  */
+ 
+ 
+ struct unix_proto_data {
+   int			refcnt;		/* cnt of reference 0=free	*/
+ 					/* -1 == not yet init'ed -bgm	*/
+   struct socket		*socket;	/* socket we're bound to	*/
+   int			protocol;
+   struct sockaddr_un	sockaddr_un;
+   short			sockaddr_len;	/* >0 if name bound		*/
+   char			*buf;
+   int			bp_head, bp_tail;
+   struct inode		*inode;
+   struct unix_proto_data	*peerupd;
+   struct wait_queue	*wait;		/* lock across potential page faults*/
+   int			lock_flag;	/* 0=unlocked, 1=locked, -1=req */
+ };
+ 
+ 
+ extern struct unix_proto_data unix_datas[NSOCKETS];
+ 
+ 
+ #define last_unix_data		(unix_datas + NSOCKETS - 1)
+ 
+ 
+ #define UN_DATA(SOCK) 		((struct unix_proto_data *)(SOCK)->data)
+ #define UN_PATH_OFFSET		((unsigned long)((struct sockaddr_un *)0) \
+ 							->sun_path)
+ 
+ /*
+  * Buffer size must be power of 2. buffer mgmt inspired by pipe code.
+  * note that buffer contents can wraparound, and we can write one byte less
+  * than full size to discern full vs empty.
+  */
+ #define BUF_SIZE		PAGE_SIZE
+ #define UN_BUF_AVAIL(UPD)	(((UPD)->bp_head - (UPD)->bp_tail) & \
+ 								(BUF_SIZE-1))
+ #define UN_BUF_SPACE(UPD)	((BUF_SIZE-1) - UN_BUF_AVAIL(UPD))
+ 
+ 
+ extern int	unix_debug;
+ 
+ 
+ extern void	unix_dprintf(char *fmt, ...);
+ extern int	unix_proto_ioctl(struct socket *sock, unsigned int cmd,
+ 				 unsigned long arg);
+ extern int	unix_dbg_ioctl(unsigned long arg, int level);
+ extern void	unix_lock(struct unix_proto_data *upd);
+ extern void	unix_unlock(struct unix_proto_data *upd);
*** original/net/unix/ioctl.c	Thu Jan  1 01:00:00 1970
--- linux/net/unix/ioctl.c	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,85 ----
+ /*
+  * UNIX		An implementation of the AF_UNIX network domain for the
+  *		LINUX operating system.  UNIX is implemented using the
+  *		BSD Socket interface as the means of communication with
+  *		the user level.
+  *
+  *		UNIX I/O request handler.
+  *
+  * Version:	@(#)ioctl.c	1.1.3	12/17/93
+  *
+  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or(at your option) any later version.
+  */
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/stat.h>
+ #include <linux/socket.h>
+ #include <linux/un.h>
+ #include <linux/fcntl.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/fs.h>
+ #include <linux/ddi.h>
+ #include "unix.h"
+ #include "global.h"
+ 
+ 
+ int
+ unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+ {
+   struct unix_proto_data *upd, *peerupd;
+   int err;
+ 
+   upd = NULL;
+   peerupd = NULL;
+   if (sock != NULL) {
+ 	upd = UN_DATA(sock);
+ 	peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
+   }
+ 
+   switch(cmd) {
+ 	case DDIOCSDBG:
+ 		if (!suser()) return(-EPERM);
+ 		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ 		if (err < 0) return(err);
+ 		return(unix_dbg_ioctl(arg, 1));
+ 
+ 	case TIOCINQ:
+ 		if (sock == NULL) break;
+ 		if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+ 		err = verify_area(VERIFY_WRITE, (long *) arg,
+ 				  sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		if (UN_BUF_AVAIL(upd) || peerupd)
+ 			put_fs_long(UN_BUF_AVAIL(upd), (long *)arg);
+ 	  	  else
+ 			put_fs_long(0, (long *) arg); /* read EOF */
+ 		break;
+ 
+ 	case TIOCOUTQ:
+ 		if (sock == NULL) break;
+ 		if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+ 		err = verify_area(VERIFY_WRITE, (long *) arg,
+ 				  sizeof(unsigned long));
+ 		if (err < 0) return(err);
+ 		if (peerupd) put_fs_long(UN_BUF_SPACE(peerupd), (long *) arg);
+ 		  else
+ 			put_fs_long(0, (long *) arg);
+ 		break;
+ 
+ 	default:
+ 		return(-EINVAL);
+   }
+   return(0);
+ }
*** original/net/unix/proc.c	Thu Jan  1 01:00:00 1970
--- linux/net/unix/proc.c	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,117 ----
+ /*
+  * UNIX		An implementation of the AF_UNIX network domain for the
+  *		LINUX operating system.  UNIX is implemented using the
+  *		BSD Socket interface as the means of communication with
+  *		the user level.
+  *
+  *		The functions in this file provide an interface between
+  *		the PROC file system and the "unix" family of networking
+  *		protocols. It is mainly used for debugging and statistics.
+  *
+  * Version:	@(#)proc.c	1.1.6	03/13/94
+  *
+  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
+  *		Fred Baumgarten, <dc6iq@insu1.etec.uni-kalrsruhe.de>
+  *
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *		Portions Copyright 1993 Gerald J. Heim
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <linux/autoconf.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/socket.h>
+ #include <linux/ddi.h>
+ #include <linux/un.h>
+ #include <linux/param.h>
+ #include <linux/proc_fs.h>
+ #include "unix.h"
+ #include "global.h"
+ 
+ 
+ /* Called from PROCfs. */
+ struct pagelist *
+ unix_get_info(void)
+ {
+   char *pos;
+   int i;
+   struct pagelist *fp, *pp;
+ 
+   fp = (struct pagelist *) __get_free_page(GFP_KERNEL);
+   if (fp == NULL) return(NULL);
+   pp = fp;
+   pp->page = (char *) __get_free_page(GFP_KERNEL);
+   if (pp->page == NULL) {
+         free_page((unsigned long) pp);
+         return(NULL);
+   }
+   pp->next = NULL;
+   pp->prev = NULL;
+   pos = pp->page;
+   pos += sprintf(pos, "Num RefCount Protocol Flags    Type St Path\n");
+ 
+   for(i = 0; i < NSOCKETS; i++) {
+ 	if (unix_datas[i].refcnt > 0) {
+ 		if (unix_datas[i].socket) {
+ 			pos += sprintf(pos, "%2d: %08X %08X %08X %04X %02X",
+ 				i, unix_datas[i].refcnt,
+ 				unix_datas[i].protocol,
+ 				unix_datas[i].socket->flags,
+ 				unix_datas[i].socket->type,
+ 				unix_datas[i].socket->state
+ 			);
+ 		} else {
+ 			pos += sprintf(pos, "%2d: %08X %08X %08X %04X %02X",
+ 				i, unix_datas[i].refcnt,
+ 				unix_datas[i].protocol,
+ 				0xffffffff, 0xffff, 0xff
+ 			);
+ 		}
+ 
+ 		/* If socket is bound to a filename, we'll print it. */
+ 		if (unix_datas[i].sockaddr_len > 0) {
+ 			pos += sprintf(pos, " %s\n",
+ 				unix_datas[i].sockaddr_un.sun_path);
+ 		} else { /* just add a newline */
+ 			*pos = '\n';
+ 			pos++;
+ 			*pos = '\0';
+ 		}
+ 
+ 		/*
+ 		 * Check wether buffer _may_ overflow in the next loop.
+ 		 * Since sockets may have very very long paths, we make
+ 		 * PATH_MAX+50 the minimum space left for a new line.
+ 		 */
+ 		if (pos > (pp->page + PAGE_SIZE - 50 - PATH_MAX)) {
+ 			pp->length = (long)pos - (long)pp->page;
+ 			pp->next = (struct pagelist *)
+ 					__get_free_page(GFP_KERNEL);
+ 			if (pp->next == NULL) {
+ 				DPRINTF(("UNIX: netinfo: out of memory !\n"));
+ 				return(fp);
+ 			}
+ 			pp->next->prev = pp;
+ 			pp = pp->next;
+ 			pp->next = NULL;
+ 			pp->page = (char *) __get_free_page(GFP_KERNEL);
+ 			if (pp->page == NULL) {
+ 				DPRINTF(("UNIX: netinfo: out of memory !\n"));
+ 				pp->prev->next = NULL;
+ 				free_page((unsigned long) pp);
+ 				return(fp);
+ 			}
+ 			pos = pp->page;
+ 		}
+ 	}
+   }
+   pp->length = (long)pos - (long)pp->page;
+   return(fp);
+ }
*** original/net/unix/sock.c	Thu Jan  1 01:00:00 1970
--- linux/net/unix/sock.c	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,815 ----
+ /*
+  * UNIX		An implementation of the AF_UNIX network domain for the
+  *		LINUX operating system.  UNIX is implemented using the
+  *		BSD Socket interface as the means of communication with
+  *		the user level.
+  *
+  * Version:	@(#)sock.c	1.1.8	03/17/94
+  *
+  * Authors:	Orest Zborowski, <obz@Kodak.COM>
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Rick Sladkey, <jrs@world.std.com>
+  *
+  *		Copyright 1991 Orest Zborowski
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or(at your option) any later version.
+  */
+ #include <asm/system.h>
+ #include <asm/segment.h>
+ #include <linux/kernel.h>
+ #include <linux/signal.h>
+ #include <linux/sched.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/stat.h>
+ #include <linux/socket.h>
+ #include <linux/un.h>
+ #include <linux/fcntl.h>
+ #include <linux/termios.h>
+ #include <linux/sockios.h>
+ #include <linux/fs.h>
+ #include <linux/ddi.h>
+ #include "unix.h"
+ #include "global.h"
+ 
+ 
+ struct unix_proto_data	unix_datas[NSOCKETS];
+ 
+ 
+ static int		unix_proto_create(struct socket *, int);
+ static int		unix_proto_dup(struct socket *, struct socket *);
+ static int		unix_proto_release(struct socket *, struct socket *);
+ static int		unix_proto_bind(struct socket *, struct sockaddr *, int);
+ static int		unix_proto_connect(struct socket *, struct sockaddr *,
+ 					   int, int);
+ static int		unix_proto_socketpair(struct socket *, struct socket *);
+ static int		unix_proto_accept(struct socket *, struct socket *, int);
+ static int		unix_proto_getname(struct socket *, struct sockaddr *,
+ 					   int *, int);
+ static int		unix_proto_read(struct socket *, char *, int, int);
+ static int		unix_proto_write(struct socket *, char *, int, int);
+ static int		unix_proto_select(struct socket *, int, select_table *);
+ static int		unix_proto_listen(struct socket *, int);
+ static int		unix_proto_send(struct socket *, void *, int, int,
+ 					unsigned int);
+ static int		unix_proto_recv(struct socket *, void *, int, int,
+ 					unsigned int);
+ static int		unix_proto_sendto(struct socket *, void *, int, int,
+ 					  unsigned int, struct sockaddr *,
+ 					  int);
+ static int		unix_proto_recvfrom(struct socket *, void *, int, int,
+ 					    unsigned int, struct sockaddr *,
+ 					    int *);
+ static int		unix_proto_shutdown(struct socket *, int);
+ static int		unix_proto_setsockopt(struct socket *, int, int,
+ 					      char *, int);
+ static int		unix_proto_getsockopt(struct socket *, int, int,
+ 					      char *, int *);
+ 
+ 
+ static inline int
+ min(int a, int b)
+ {
+   if (a < b) return(a);
+   return(b);
+ }
+ 
+ 
+ void
+ sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
+ {
+   char buf[sizeof(sockun->sun_path) + 1];
+ 
+   if (unix_debug == 0) return;
+ 
+   sockaddr_len -= UN_PATH_OFFSET;
+   if (sockun->sun_family != AF_UNIX)
+ 	printk("UNIX: Badd addr family %d>\n", sockun->sun_family);
+     else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf))
+ 	printk("UNIX: Bad addr len %d>\n", sockaddr_len);
+     else {
+ 	memcpy(buf, sockun->sun_path, sockaddr_len);
+ 	buf[sockaddr_len] = '\0';
+ 	printk("\"%s\"[%d]\n", buf, sockaddr_len + UN_PATH_OFFSET);
+   }
+ }
+   
+ 
+ /* don't have to do anything. */
+ static int
+ unix_proto_listen(struct socket *sock, int backlog)
+ {
+   return(0);
+ }
+ 
+ 
+ static int
+ unix_proto_setsockopt(struct socket *sock, int level, int optname,
+ 		      char *optval, int optlen)
+ {
+   return(-EOPNOTSUPP);
+ }
+ 
+ 
+ static int
+ unix_proto_getsockopt(struct socket *sock, int level, int optname,
+ 		      char *optval, int *optlen)
+ {
+   return(-EOPNOTSUPP);
+ }
+ 
+ 
+ static int
+ unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock, 
+ 		  unsigned flags,  struct sockaddr *addr, int addr_len)
+ {
+   return(-EOPNOTSUPP);
+ }     
+ 
+ 
+ static int
+ unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock, 
+ 		    unsigned flags, struct sockaddr *addr, int *addr_len)
+ {
+   return(-EOPNOTSUPP);
+ }     
+ 
+ 
+ static int
+ unix_proto_shutdown(struct socket *sock, int how)
+ {
+   return(-EOPNOTSUPP);
+ }
+ 
+ 
+ /* This error needs to be checked. */
+ static int
+ unix_proto_send(struct socket *sock, void *buff, int len, int nonblock,
+ 		unsigned flags)
+ {
+   if (flags != 0) return(-EINVAL);
+   return(unix_proto_write(sock, (char *) buff, len, nonblock));
+ }
+ 
+ 
+ /* This error needs to be checked. */
+ static int
+ unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock,
+ 		unsigned flags)
+ {
+   if (flags != 0) return(-EINVAL);
+   return(unix_proto_read(sock, (char *) buff, len, nonblock));
+ }
+ 
+ 
+ static struct unix_proto_data *
+ unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len,
+ 		 struct inode *inode)
+ {
+   struct unix_proto_data *upd;
+ 
+   for(upd = unix_datas; upd <= last_unix_data; ++upd) {
+ 	if (upd->refcnt > 0 && upd->socket &&
+ 	    upd->socket->state == SS_UNCONNECTED &&
+ 	    upd->sockaddr_un.sun_family == sockun->sun_family &&
+ 	    upd->inode == inode) return(upd);
+   }
+   return(NULL);
+ }
+ 
+ 
+ static struct unix_proto_data *
+ unix_data_alloc(void)
+ {
+   struct unix_proto_data *upd;
+ 
+   cli();
+   for(upd = unix_datas; upd <= last_unix_data; ++upd) {
+ 	if (! upd->refcnt) {
+ 		upd->refcnt = -1;
+ 		sti();
+ 		upd->socket = NULL;
+ 		upd->sockaddr_len = 0;
+ 		upd->sockaddr_un.sun_family = 0;
+ 		upd->buf = NULL;
+ 		upd->bp_head = upd->bp_tail = 0;
+ 		upd->inode = NULL;
+ 		upd->peerupd = NULL;
+ 		return(upd);
+ 	}
+   }
+   sti();
+   return(NULL);
+ }
+ 
+ 
+ static inline void
+ unix_data_ref(struct unix_proto_data *upd)
+ {
+   upd->refcnt++;
+   DPRINTF(("UNIX: data_ref: refing data 0x%X(%d)\n", upd, upd->refcnt));
+ }
+ 
+ 
+ static void
+ unix_data_deref(struct unix_proto_data *upd)
+ {
+   if (upd->refcnt == 1) {
+ 	DPRINTF(("UNIX: data_deref: releasing data 0x%X\n", upd));
+ 	if (upd->buf) {
+ 		free_page((unsigned long)upd->buf);
+ 		upd->buf = NULL;
+ 		upd->bp_head = upd->bp_tail = 0;
+ 	}
+   }
+   upd->refcnt--;
+ }
+ 
+ 
+ /*
+  * Upon a create, we allocate an empty protocol data,
+  * and grab a page to buffer writes.
+  */
+ static int
+ unix_proto_create(struct socket *sock, int protocol)
+ {
+   struct unix_proto_data *upd;
+ 
+   DPRINTF(("UNIX: create: socket 0x%X, proto %d\n", sock, protocol));
+   if (protocol != 0) {
+ 	DPRINTF(("UNIX: create: protocol != 0\n"));
+ 	return(-EINVAL);
+   }
+   if ((upd = unix_data_alloc()) == NULL) {
+ 	DPRINTF(("UNIX: create: can't allocate buffer\n"));
+ 	return(-ENOMEM);
+   }
+   if ((upd->buf =(char *)get_free_page(GFP_USER)) == NULL) {
+ 	DPRINTF(("UNIX: create: can't get page!\n"));
+ 	unix_data_deref(upd);
+ 	return(-ENOMEM);
+   }
+   upd->protocol = protocol;
+   upd->socket = sock;
+   UN_DATA(sock) = upd;
+   upd->refcnt = 1;	/* now it is complete - bgm */
+   DPRINTF(("UNIX: create: allocated data 0x%X\n", upd));
+   return(0);
+ }
+ 
+ 
+ static int
+ unix_proto_dup(struct socket *newsock, struct socket *oldsock)
+ {
+   struct unix_proto_data *upd = UN_DATA(oldsock);
+ 
+   return(unix_proto_create(newsock, upd->protocol));
+ }
+ 
+ 
+ static int
+ unix_proto_release(struct socket *sock, struct socket *peer)
+ {
+   struct unix_proto_data *upd = UN_DATA(sock);
+ 
+   DPRINTF(("UNIX: release: socket 0x%X, unix_data 0x%X\n", sock, upd));
+   if (upd == NULL) return(0);
+   if (upd->socket != sock) {
+ 	printk("UNIX: release: socket link mismatch!\n");
+ 	return(-EINVAL);
+   }
+   if (upd->inode) {
+ 	DPRINTF(("UNIX: release: releasing inode 0x%X\n", upd->inode));
+ 	iput(upd->inode);
+ 	upd->inode = NULL;
+   }
+   UN_DATA(sock) = NULL;
+   upd->socket = NULL;
+   if (upd->peerupd) unix_data_deref(upd->peerupd);
+   unix_data_deref(upd);
+   return(0);
+ }
+ 
+ 
+ /*
+  * Bind a name to a socket.
+  * This is where much of the work is done: we allocate a fresh page for
+  * the buffer, grab the appropriate inode and set things up.
+  *
+  * FIXME: what should we do if an address is already bound?
+  *	  Here we return EINVAL, but it may be necessary to re-bind.
+  *	  I think thats what BSD does in the case of datagram sockets...
+  */
+ static int
+ unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
+ 		int sockaddr_len)
+ {
+   char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
+   struct unix_proto_data *upd = UN_DATA(sock);
+   unsigned long old_fs;
+   int i, err;
+ 
+   DPRINTF(("UNIX: bind: socket 0x%X, len=%d\n", sock, sockaddr_len));
+   if (sockaddr_len <= UN_PATH_OFFSET ||
+       sockaddr_len > sizeof(struct sockaddr_un)) {
+ 	DPRINTF(("UNIX: bind: bad length %d\n", sockaddr_len));
+ 	return(-EINVAL);
+   }
+   if (upd->sockaddr_len || upd->inode) {
+ 	DPRINTF(("UNIX: bind: already bound!\n"));
+ 	return(-EINVAL);
+   }
+   err = verify_area(VERIFY_WRITE, umyaddr, sockaddr_len);
+   if (err < 0) return(err);
+   memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
+   upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
+   if (upd->sockaddr_un.sun_family != AF_UNIX) {
+ 	DPRINTF(("UNIX: bind: family is %d, not AF_UNIX(%d)\n",
+ 	       			upd->sockaddr_un.sun_family, AF_UNIX));
+ 	return(-EINVAL);
+   }
+ 
+   memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET);
+   fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
+   old_fs = get_fs();
+   set_fs(get_ds());
+   i = do_mknod(fname, S_IFSOCK | 0777, 0);
+   if (i == -EEXIST) i = -EADDRINUSE;
+   if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
+   set_fs(old_fs);
+   if (i < 0) {
+ 	DPRINTF(("UNIX: bind: can't open socket %s\n", fname));
+ 	return(i);
+   }
+   upd->sockaddr_len = sockaddr_len;	/* now its legal */
+ 
+   DPRINTF(("UNIX: bind: bound socket address: "));
+   sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len);
+   DPRINTF(("to inode 0x%X\n", upd->inode));
+   return(0);
+ }
+ 
+ 
+ /*
+  * Perform a connection. we can only connect to unix sockets
+  * (I can't for the life of me find an application where that
+  * wouldn't be the case!)
+  */
+ static int
+ unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
+ 		   int sockaddr_len, int flags)
+ {
+   char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
+   struct sockaddr_un sockun;
+   struct unix_proto_data *serv_upd;
+   struct inode *inode;
+   unsigned long old_fs;
+   int i, err;
+ 
+   DPRINTF(("UNIX: connect: socket 0x%X, servlen=%d\n", sock, sockaddr_len));
+ 
+   if (sockaddr_len <= UN_PATH_OFFSET ||
+       sockaddr_len > sizeof(struct sockaddr_un)) {
+ 	DPRINTF(("UNIX: connect: bad length %d\n", sockaddr_len));
+ 	return(-EINVAL);
+   }
+   if (sock->state == SS_CONNECTING) return(-EINPROGRESS);
+   if (sock->state == SS_CONNECTED) return(-EISCONN);
+ 
+   err = verify_area(VERIFY_WRITE, uservaddr, sockaddr_len);
+   if (err < 0) return(err);
+   memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
+   sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
+   if (sockun.sun_family != AF_UNIX) {
+ 	DPRINTF(("UNIX: connect: family is %d, not AF_UNIX(%d)\n",
+ 	       					sockun.sun_family, AF_UNIX));
+ 	return(-EINVAL);
+   }
+ 
+   /*
+    * Try to open the name in the filesystem - this is how we
+    * identify ourselves and our server. Note that we don't
+    * hold onto the inode that long, just enough to find our
+    * server. When we're connected, we mooch off the server.
+    */
+   memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET);
+   fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
+   old_fs = get_fs();
+   set_fs(get_ds());
+   i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
+   set_fs(old_fs);
+   if (i < 0) {
+ 	DPRINTF(("UNIX: connect: can't open socket %s\n", fname));
+ 	return(i);
+   }
+   serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode);
+   iput(inode);
+   if (serv_upd == NULL) {
+ 	DPRINTF(("UNIX: connect: can't locate peer %s at inode 0x%X\n",
+ 								fname, inode));
+ 	return(-EINVAL);
+   }
+   if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
+ 	DPRINTF(("UNIX: connect: can't await connection\n"));
+ 	return(i);
+   }
+   if (sock->conn) {
+ 	unix_data_ref(UN_DATA(sock->conn));
+ 	UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */
+   }
+   return(0);
+ }
+ 
+ 
+ /*
+  * To do a socketpair, we just connect the two datas, easy!
+  * Since we always wait on the socket inode, they're no contention
+  * for a wait area, and deadlock prevention in the case of a process
+  * writing to itself is, ignored, in true unix fashion!
+  */
+ static int
+ unix_proto_socketpair(struct socket *sock1, struct socket *sock2)
+ {
+   struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2);
+ 
+   unix_data_ref(upd1);
+   unix_data_ref(upd2);
+   upd1->peerupd = upd2;
+   upd2->peerupd = upd1;
+   return(0);
+ }
+ 
+ 
+ /* On accept, we ref the peer's data for safe writes. */
+ static int
+ unix_proto_accept(struct socket *sock, struct socket *newsock, int flags)
+ {
+   struct socket *clientsock;
+ 
+   DPRINTF(("UNIX: accept: socket 0x%x accepted via socket 0x%X\n",
+ 							sock, newsock));
+ 
+   /*
+    * If there aren't any sockets awaiting connection,
+    * then wait for one, unless nonblocking.
+    */
+   while((clientsock = sock->iconn) == NULL) {
+ 	if (flags & O_NONBLOCK) return(-EAGAIN);
+ 	interruptible_sleep_on(sock->wait);
+ 	if (current->signal & ~current->blocked) {
+ 		DPRINTF(("UNIX: accept: sleep was interrupted\n"));
+ 		return(-ERESTARTSYS);
+ 	}
+   }
+ 
+   /*
+    * Great. Finish the connection relative to server and client,
+    * wake up the client and return the new fd to the server.
+    */
+   sock->iconn = clientsock->next;
+   clientsock->next = NULL;
+   newsock->conn = clientsock;
+   clientsock->conn = newsock;
+   clientsock->state = SS_CONNECTED;
+   newsock->state = SS_CONNECTED;
+   unix_data_ref(UN_DATA(clientsock));
+   UN_DATA(newsock)->peerupd = UN_DATA(clientsock);
+   UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
+   UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
+   wake_up_interruptible(clientsock->wait);
+   return(0);
+ }
+ 
+ 
+ /* Gets the current name or the name of the connected socket. */
+ static int
+ unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
+ 		   int *usockaddr_len, int peer)
+ {
+   struct unix_proto_data *upd;
+   int len, err;
+ 
+   DPRINTF(("UNIX: getname: socket 0x%X for %s\n", sock, peer?"peer":"self"));
+   if (peer) {
+ 	if (sock->state != SS_CONNECTED) {
+ 		DPRINTF(("UNIX: getname: socket not connected\n"));
+ 		return(-EINVAL);
+ 	}
+ 	upd = UN_DATA(sock->conn);
+   } else {
+ 	upd = UN_DATA(sock);
+   }
+ 
+   err = verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len));
+   if (err < 0) return(err);
+   if ((len = get_fs_long((int *) usockaddr_len)) <= 0) return(-EINVAL);
+   if (len > upd->sockaddr_len) len = upd->sockaddr_len;
+   if (len) {
+ 	err = verify_area(VERIFY_WRITE, usockaddr, len);
+ 	if (err < 0) return(err);
+ 	memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
+   }
+   put_fs_long(len, (int *) usockaddr_len);
+   return(0);
+ }
+ 
+ 
+ /* We read from our own buf. */
+ static int
+ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
+ {
+   struct unix_proto_data *upd;
+   int avail;
+   int r = 0;
+ 
+   if (size < 0) return(0);
+ 
+   upd = UN_DATA(sock);
+   while((avail = UN_BUF_AVAIL(upd)) == 0) {
+ 	if (sock->state != SS_CONNECTED) {
+ 		DPRINTF(("UNIX: read: socket not connected\n"));
+ 		return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL);
+ 	}
+ 	DPRINTF(("UNIX: read: no data available...\n"));
+ 	if (nonblock) return(-EAGAIN);
+ 	interruptible_sleep_on(sock->wait);
+ 	if (current->signal & ~current->blocked) {
+ 		DPRINTF(("UNIX: read: interrupted\n"));
+ 		return(-ERESTARTSYS);
+ 	}
+   }
+ 
+   /*
+    * Copy from the read buffer into the user's buffer,
+    * watching for wraparound. Then we wake up the writer.
+    */
+   unix_lock(upd);
+   while (r < size && (avail = UN_BUF_AVAIL(upd))) {
+ 	int err, part;
+ 	int todo = size - r;
+ 
+ 	if (avail <= 0) {
+ 		printk("UNIX: read: AVAIL IS NEGATIVE!!!\n");
+ 		send_sig(SIGKILL, current, 1);
+ 		r = -EINTR;
+ 		break;
+ 	}
+ 	if (todo > avail) todo = avail;
+ 	if (todo > (part = BUF_SIZE - upd->bp_tail)) todo = part;
+ 
+ 	DPRINTF(("UNIX: read: avail=%d, todo=%d, r=%d\n", avail, todo, r));
+ 	err = verify_area(VERIFY_WRITE, ubuf, todo);
+ 	if (err < 0) {
+ 		r = err;
+ 		break;
+ 	}
+ 
+ 	memcpy_tofs(ubuf, upd->buf + upd->bp_tail, todo);
+ 	upd->bp_tail =(upd->bp_tail + todo) &(BUF_SIZE-1);
+ 	ubuf += todo;
+ 	r += todo;
+ 	if (sock->state == SS_CONNECTED)
+ 		wake_up_interruptible(sock->conn->wait);
+   }
+   unix_unlock(upd);
+   return(r);
+ }
+ 
+ 
+ /*
+  * We write to our peer's buf. When we connected we ref'd this
+  * peer so we are safe that the buffer remains, even after the
+  * peer has disconnected, which we check other ways.
+  */
+ static int
+ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
+ {
+   struct unix_proto_data *pupd;
+   int r = 0;
+ 
+   if (size <= 0) return(0);
+   if (sock->state != SS_CONNECTED) {
+ 	DPRINTF(("UNIX: write: socket not connected\n"));
+ 	if (sock->state == SS_DISCONNECTING) {
+ 		send_sig(SIGPIPE, current, 1);
+ 		return(-EINTR);
+ 	}
+ 	return(-EINVAL);
+   }
+ 
+   /*
+    * pupd=peerudp.  Note that sock->conn can go away if the other side
+    * closes during a sleep or memcpy_tofs/fromfs()
+    */
+   pupd = UN_DATA(sock)->peerupd;
+ 
+   unix_lock(pupd);
+ 
+   while (r < size) {
+ 	int todo = size - r;
+ 	int part, space, err;
+ 
+ 	if ((space = UN_BUF_SPACE(pupd)) == 0) {
+ 		if (nonblock) {
+ 			if (r == 0) r = -EAGAIN;
+ 			break;
+ 		}
+ 		unix_unlock(pupd);
+ 		interruptible_sleep_on(sock->wait);
+ 		unix_lock(pupd);
+ 		if (current->signal & ~current->blocked) {
+ 			DPRINTF(("UNIX: write: interrupted\n"));
+ 			if (r == 0) r = -ERESTARTSYS;
+ 			break;
+ 		}
+ 		if (sock->state == SS_DISCONNECTING) {
+ 			DPRINTF(("UNIX: write: disconnected(SIGPIPE)\n"));
+ 			send_sig(SIGPIPE, current, 1);
+ 			r = -EINTR;
+ 			break;
+ 		}
+ 		continue;
+ 	}
+ 
+ 	/*
+ 	 * Copy from the user's buffer to the write buffer,
+ 	 * watching for wraparound. Then we wake up the reader.
+ 	 */
+ 	if (space <= 0) {
+ 		printk("UNIX: write: SPACE IS NEGATIVE!!!\n");
+ 		send_sig(SIGKILL, current, 1);
+ 		r = -EINTR;
+ 		break;
+ 	}
+ 
+ 	/*
+ 	 * We may become disconnected inside this loop, so watch
+ 	 * for it (peerupd is safe until we close).
+ 	 */
+ 	if (sock->state == SS_DISCONNECTING) {
+ 		send_sig(SIGPIPE, current, 1);
+ 		r = -EINTR;
+ 		break;
+ 	}
+ 	if (todo > space) todo = space;
+ 	if (todo > (part = BUF_SIZE - pupd->bp_head)) todo = part;
+ 	DPRINTF(("UNIX: write: space=%d, todo=%d, r=%d\n", space, todo, r));
+ 	err = verify_area(VERIFY_WRITE, ubuf, todo);
+ 	if (err < 0) {
+ 		r = err;
+ 		break;
+ 	}
+ 
+ 	memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, todo);
+ 	pupd->bp_head =(pupd->bp_head + todo) &(BUF_SIZE-1);
+ 	ubuf += todo;
+ 	r += todo;
+ 	if (sock->state == SS_CONNECTED)
+ 		wake_up_interruptible(sock->conn->wait);
+   }
+   unix_unlock(pupd);
+   return(r);
+ }
+ 
+ 
+ static int
+ unix_proto_select(struct socket *sock, int sel_type, select_table *wait)
+ {
+   struct unix_proto_data *upd, *peerupd;
+ 
+   /* Handle server sockets specially. */
+   if (sock->flags & SO_ACCEPTCON) {
+ 	if (sel_type == SEL_IN) {
+ 		DPRINTF(("UNIX: select: %sconnections pending\n",
+ 		       				sock->iconn ? "" : "no "));
+ 		if (sock->iconn) return(1);
+ 		select_wait(sock->wait, wait);
+ 		return(sock->iconn ? 1 : 0);
+ 	}
+ 	DPRINTF(("UNIX: select: nothing else for server socket\n"));
+ 	select_wait(sock->wait, wait);
+ 	return(0);
+   }
+ 
+   if (sel_type == SEL_IN) {
+ 	upd = UN_DATA(sock);
+ 	DPRINTF(("UNIX: select: there is%s data available\n",
+ 	       				UN_BUF_AVAIL(upd) ? "" : " no"));
+ 	if (UN_BUF_AVAIL(upd))	/* even if disconnected */
+ 			return(1);
+ 	else if (sock->state != SS_CONNECTED) {
+ 		DPRINTF(("UNIX: select: socket not connected(read EOF)\n"));
+ 		return(1);
+ 	}
+ 	select_wait(sock->wait, wait);
+ 	return(0);
+   }
+   if (sel_type == SEL_OUT) {
+ 	if (sock->state != SS_CONNECTED) {
+ 		DPRINTF(("UNIX: select: socket not connected(write EOF)\n"));
+ 		return(1);
+ 	}
+ 	peerupd = UN_DATA(sock->conn);
+ 	DPRINTF(("UNIX: select: there is%s space available\n",
+ 	       				UN_BUF_SPACE(peerupd) ? "" : " no"));
+ 	if (UN_BUF_SPACE(peerupd) > 0) return(1);
+ 	select_wait(sock->wait, wait);
+ 	return(0);
+   }
+ 
+   /* SEL_EX */
+   DPRINTF(("UNIX: select: there are no exceptions here?!\n"));
+   return(0);
+ }
+ 
+ 
+ static int
+ unix_open(struct inode * inode, struct file * file)
+ {
+   int minor;
+ 
+   DPRINTF(("UNIX: open\n"));
+   minor = MINOR(inode->i_rdev);
+   if (minor != 0) return(-ENODEV);
+ 
+   return(0);
+ }
+ 
+ 
+ static void
+ unix_close(struct inode * inode, struct file * file)
+ {
+   DPRINTF(("UNIX: close\n"));
+ }
+ 
+ 
+ static int
+ unix_ioctl(struct inode *inode, struct file *file,
+ 	 unsigned int cmd, unsigned long arg)
+ {
+   int minor;
+ 
+   DPRINTF(("UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg));
+   minor = MINOR(inode->i_rdev);
+   if (minor != 0) return(-ENODEV);
+ 
+   return(unix_proto_ioctl(NULL, cmd, arg));
+ }
+ 
+ 
+ static struct file_operations unix_fops = {
+   NULL,		/* LSEEK	*/
+   NULL,		/* READ		*/
+   NULL,		/* WRITE	*/
+   NULL,		/* READDIR	*/
+   NULL,		/* SELECT	*/
+   unix_ioctl,	/* IOCTL	*/
+   NULL,		/* MMAP		*/
+   unix_open,	/* OPEN		*/
+   unix_close	/* CLOSE	*/
+ };
+ 
+ 
+ static struct proto_ops unix_proto_ops = {
+   AF_UNIX,
+   unix_proto_create,	unix_proto_dup,		unix_proto_release,
+   unix_proto_bind,	unix_proto_connect,	unix_proto_socketpair,
+   unix_proto_accept,	unix_proto_getname,	unix_proto_read,
+   unix_proto_write,	unix_proto_select,	unix_proto_ioctl,
+   unix_proto_listen,	unix_proto_send,	unix_proto_recv,
+   unix_proto_sendto,	unix_proto_recvfrom,	unix_proto_shutdown,
+   unix_proto_setsockopt,unix_proto_getsockopt,	NULL				/* unix_proto_fcntl	*/
+ };
+ 
+ 
+ int
+ unix_proto_init(struct ddi *ddi)
+ {
+   struct unix_proto_data *upd;
+ 
+   if (register_chrdev(ddi->major, ddi->name, &unix_fops) < 0) {
+ 	printk("%s: cannot register device %d !\n", ddi->name, ddi->major);
+ 	return(-1);
+   }
+ 
+   /* Tell NET that we are alive... */
+   if (net_af_reg(unix_proto_ops.family, &unix_proto_ops) != 0) {
+ 	printk("%s: cannot register address family %d !\n",
+ 				ddi->name, unix_proto_ops.family);
+ 	return(-EPFNOSUPPORT);
+   }
+ 
+   /* Clean out all UNIX sockets. */
+   for(upd = unix_datas; upd <= last_unix_data; ++upd) {
+ 	upd->refcnt = 0;
+   }
+ 
+   /* All done. */
+   return(0);
+ }
diff -c --recur --new-file original/net/unix/unix.h linux/net/unix/unix.h
*** original/net/unix/unix.h	Thu Jan  1 01:00:00 1970
--- linux/net/unix/unix.h	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,45 ----
+ /*
+  * UNIX		An implementation of the AF_UNIX network domain for the
+  *		LINUX operating system.  UNIX is implemented using the
+  *		BSD Socket interface as the means of communication with
+  *		the user level.
+  *
+  *		Defintions for the UNIX protocol module.
+  *
+  * Version:	@(#)unix.h	1.1.3	12/17/93
+  *
+  * Authors:	Orest Zborowski, <obz@Kodak.COM>
+  *		Ross Biro, <bir7@leland.Stanford.Edu>
+  *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *
+  *		Copyright 1991 Orest Zborowski
+  *		Copyright 1992 Ross Biro
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ 
+ #include <linux/config.h>
+ 
+ 
+ #undef	UNIX_DEBUG
+ 
+ 
+ /* Has the master NETDBG flag been set? */
+ #ifdef	NETDBG
+ #   undef  UNIX_DEBUG
+ #   define UNIX_DEBUG
+ #endif
+ 
+ #ifdef	UNIX_DEBUG
+ #   define DPRINTF(x)	unix_dprintf x
+ #else
+ #   define DPRINTF(x)	/*nothing*/
+ #endif
+ 
+ 
+ extern int		unix_proto_init(struct ddi *ddi);
*** original/net/unix/utils.c	Thu Jan  1 01:00:00 1970
--- linux/net/unix/utils.c	Mon May  9 13:13:16 1994
***************
*** 0 ****
--- 1,127 ----
+ /*
+  * UNIX		An implementation of the AF_UNIX network domain for the
+  *		LINUX operating system.  UNIX is implemented using the
+  *		BSD Socket interface as the means of communication with
+  *		the user level.
+  *
+  *		Various kernel-resident utility functions; mainly for
+  *		format conversion and debugging output.
+  *
+  * Version:	@(#)utils.c	1.1.5	02/03/94
+  *
+  * Authors:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+  *		Matt Dillon, <dillon@apollo.west.oic.com>
+  *
+  *		Copyright 1993,1994 Fred N. van Kempen
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/socket.h>
+ #include <linux/in.h>
+ #include <linux/un.h>
+ #include <linux/errno.h>
+ #include <linux/stat.h>
+ #include <linux/ddi.h>
+ #include <stdarg.h>
+ #include "unix.h"
+ #include "global.h"
+ 
+ 
+ extern int vsprintf(char *buf, const char *fmt, va_list args);
+ 
+ 
+ int unix_debug = 0;			/* UNIX debugging flag		*/
+ 
+ 
+ #ifdef	UNIX_DEBUG
+ void
+ unix_dprintf(char *fmt, ...)
+ {
+   char buff[256];
+   va_list args;
+ 
+   if (unix_debug == 0) return;
+ 
+   va_start(args, fmt);
+   vsprintf(buff, fmt, args);
+   va_end(args);
+   printk("%s", buff);
+ }
+ #endif	/* UNIX_DEBUG */
+ 
+ 
+ int
+ unix_dbg_ioctl(unsigned long arg, int level)
+ {
+   int val;
+ 
+   val = get_fs_long((int *) arg);
+   switch(val) {
+ 	case 0:	/* OFF */
+ 		unix_debug = 0;
+ 		break;
+ 
+ 	case 1:	/* ON, INET */
+ 		unix_debug = level;
+ 		break;
+ 
+ 	default:
+ 		if (val >= 128) {
+ 			unix_debug = 0;
+ 			return(0);
+ 		}
+ 		return(-EINVAL);
+   }
+ 
+   return(0);
+ }
+ 
+ 
+ /*
+  * Locking for unix-domain sockets.  We don't use the socket structure's
+  * wait queue because it is allowed to 'go away' outside of our control,
+  * whereas unix_proto_data structures stick around.
+  */
+ void
+ unix_lock(struct unix_proto_data *upd)
+ {
+   while (upd->lock_flag) {
+ 	/*
+ 	 * This is not an error.  Matt just needed to know
+ 	 * whether this situation could occur, and guess
+ 	 * what?  It can.  This printk will be changed into
+ 	 * a DPRINTF soon. -FvK
+ 	 */
+ 	printk("UNIX proto lock: conflict, sleeping\n");
+ 	upd->lock_flag = -1;
+ 	sleep_on(&upd->wait);
+   }
+   upd->lock_flag = 1;
+ }
+ 
+ 
+ void
+ unix_unlock(struct unix_proto_data *upd)
+ {
+   if (upd->lock_flag == -1) {
+ 	/*
+ 	 * This is not an error.  Matt just needed to know
+ 	 * whether this situation could occur, and guess
+ 	 * what?  It can.  This printk will be changed into
+ 	 * a DPRINTF soon. -FvK
+ 	 */
+ 	printk("UNIX proto unlock: end conflict, waking\n");
+ 	wake_up_interruptible(&upd->wait);
+   }
+   upd->lock_flag = 0;
+ }
~~~~~~~~~~~~~~

