[3766] in linux-scsi channel archive

home help back first fref pref prev next nref lref last post

IP over SCSI

daemon@ATHENA.MIT.EDU (Randy D. Scott)
Sat Apr 25 00:53:32 1998

From: scottr@wwa.com (Randy D. Scott)
To: linux-kernel@vger.rutgers.edu, linux-scsi@vger.rutgers.edu
Date: 	Fri, 24 Apr 1998 23:46:54 -0500 (CDT)
Cc: frantzc@insync.net

Due to the recent interest in the IP over SCSI project, I have created a
patch against the 2.1.97 kernel for the IP over SCSI stuff.

I don't have access to the hardware that we used anymore, so I was unable
to test this.  Actually, I didn't even bother to compile it.  So, I can't
give ANY gaurantees.

This patch can also be found at:
http://www.wwa.com/~scottr/sd/design/patch.scsinet-2.1.97

Note that you will need the configuration utility from the original source
distribution in order to configure the SCSI interface.  This can be found at:
http://www.wwa.com/~scottr/sd/design/scsinet-2.1.25.tar.gz


Have fun.


diff -u -r -N -x *.o linux/drivers/net/Config.in linux-scsi/drivers/net/Config.in
--- linux/drivers/net/Config.in	Sun Apr 19 13:19:42 1998
+++ linux-scsi/drivers/net/Config.in	Fri Apr 24 22:33:14 1998
@@ -177,6 +177,10 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER
 fi
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'IP-in-SCSI encapsulation (EXPERIMENTAL)' CONFIG_SCSINET
+fi
 #
 # WAN drivers support
 #
diff -u -r -N -x *.o linux/drivers/net/Makefile linux-scsi/drivers/net/Makefile
--- linux/drivers/net/Makefile	Mon Apr 13 21:04:44 1998
+++ linux-scsi/drivers/net/Makefile	Fri Apr 24 22:33:14 1998
@@ -59,6 +59,10 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSINET),y)
+L_OBJS += scsinet.o
+endif
+
 ifeq ($(CONFIG_SHAPER),y)
 L_OBJS += shaper.o
 else
diff -u -r -N -x *.o linux/drivers/net/Space.c linux-scsi/drivers/net/Space.c
--- linux/drivers/net/Space.c	Mon Apr 13 21:04:44 1998
+++ linux-scsi/drivers/net/Space.c	Fri Apr 24 22:33:14 1998
@@ -467,6 +467,14 @@
 #   define	NEXT_DEV	(&dummy_dev)
 #endif
 
+#ifdef CONFIG_SCSINET
+    extern int sn_init(struct device *dev);
+    static struct device sn0_dev = {
+	"sn0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, sn_init, };
+#   undef	NEXT_DEV
+#   define	NEXT_DEV	(&sn0_dev)
+#endif
+
 #ifdef CONFIG_EQUALIZER
 extern int eql_init(struct device *dev);
 struct device eql_dev = {
diff -u -r -N -x *.o linux/drivers/net/scsinet.c linux-scsi/drivers/net/scsinet.c
--- linux/drivers/net/scsinet.c	Wed Dec 31 18:00:00 1969
+++ linux-scsi/drivers/net/scsinet.c	Fri Apr 24 22:33:14 1998
@@ -0,0 +1,537 @@
+/*
+ *      scsinet.c -- IP over SCSI implementation
+ *
+ *      Authors:  Chris Frantz (frantzc@bork.com)
+ *                Randy Scott (scottr@bork.com)
+ *                Alan Bork (borka@bork.com)
+ *
+ *      Bork Systems Group
+ *      http://www.bork.com
+ *
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/ip.h>
+#include <linux/config.h>
+#include <net/dst.h>
+#include <net/arp.h>
+#include <net/sock.h>
+#include <net/ipv6.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+#include <linux/in6.h>
+#include <net/ndisc.h>
+#endif
+#include <asm/checksum.h>
+
+#include <linux/scsinet.h>
+
+extern int sn_net_init(struct device *net_device);
+extern int sn_tx_packet(int bus, char *packet, int len);
+extern int sn_net_down(struct sn_info *sni);
+extern unsigned char sn_get_my_id(int bus);
+
+static int scsi_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+		       void *daddr, void *saddr, unsigned len);
+static int scsi_rebuild_header(struct sk_buff *skb);
+static int scsi_header_cache(struct dst_entry *dst, struct dst_entry *neigh, struct hh_cache *hh);
+static void scsi_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char *haddr);
+
+unsigned short scsi_type_trans(struct sk_buff *skb, struct device *dev);
+
+static int sn_init_flag = 0;
+
+/*
+ * sn_open() sets up device for operation
+ */
+static int sn_open(struct device *dev)
+{
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return sn_net_init(dev);
+}
+
+/*
+ * sn_close() shuts the device down
+ */
+static int sn_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+	return sn_net_down((struct sn_info *) dev->priv);
+}
+
+/*
+ * sn_get_stats() returns a statistics structure
+ */
+static struct net_device_stats *sn_get_stats(struct device *dev)
+{
+	struct sn_info *info = (struct sn_info *) dev->priv;
+	return &(info->stats);
+}
+
+/*
+ * sn_send_packet() passes a properly formatted packet to the SCSI
+ * 	driver
+ */
+static int sn_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct sn_info *info = (struct sn_info *) dev->priv;
+
+#if 0
+	printk("sn: sending packet (skb = %p)\n", skb);
+#endif
+
+	/*
+	 * We can't do anything with a NULL device structure
+	 */
+	if (dev == NULL)
+		return 0;
+
+	/*
+	 * If we are passed a NULL skb, some higher level networking
+	 * code think's we're broken.  Tell it we aren't
+	 */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (sn_tx_packet(info->scsi_bus_id, skb->data, skb->len) < 0) {
+		dev->tbusy = 1;
+		info->stats.tx_errors++;
+		return 1;
+	} else {
+		dev_kfree_skb(skb, FREE_WRITE);
+		info->stats.tx_packets++;
+		info->stats.tx_bytes+=skb->len;
+	}
+
+	return 0;
+}
+
+/*
+ * sn_rx_packet() takes a packet from the SCSI driver, queues it
+ *	onto the receive queue, and informs the bottom half handler
+ */
+void sn_rx_packet(struct device *dev, void *pkt_data, unsigned int pkt_len)
+{
+	struct sn_info *info = (struct sn_info *) dev->priv;
+	struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+#if 0
+	printk("%s: receiving packet (%p, %d)\n", dev->name, pkt_data, pkt_len);
+#endif
+
+	if (skb == NULL) {
+		printk(KERN_NOTICE "%s:  Memory squeeze, dropping packet.\n",
+		       dev->name);
+		info->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+
+	/*
+	 * Put the data in its place
+	 */
+	memcpy(skb_put(skb, pkt_len), pkt_data, pkt_len);
+	skb->protocol = scsi_type_trans(skb, dev);
+
+	/*
+	 * Queue the packet and inform the bottom half handler and
+	 * update statistics
+	 */
+	netif_rx(skb);
+	mark_bh(NET_BH);
+	info->stats.rx_packets++;
+	info->stats.rx_bytes+=pkt_len;
+}
+
+/*
+ * sn_do_ioctl modifies the list of SCSI IDs which the SCSI driver
+ * should NOT use.
+ */
+static int sn_do_ioctl(struct device *dev, struct ifreq *ifr, int command)
+{
+	struct sn_info *info = (struct sn_info *)dev->priv;
+	int i, j = 0, k, idlist[SN_MAX_DEV];
+	int retval;
+
+	/*
+	 * Copy the id list into kernel address space
+	 */
+	if (copy_from_user(idlist, ifr->ifr_data, sizeof(idlist)))
+		return -EFAULT;
+
+	switch (command) {
+	case SIOCSSCSIADD:
+		while (idlist[j] != 0) {
+			/*
+			 * Check for duplicates
+			 */
+			k = 0;
+			for (i = 0; i < info->scsi_dont_use_len; i++)
+				if (info->scsi_dont_use[i] == idlist[j] - 1)
+					k++;
+			if (k == 0)
+				info->scsi_dont_use[info->scsi_dont_use_len++]
+					 = idlist[j] - 1;
+			j++;
+		}
+
+		retval = 0;
+		break;
+
+	case SIOCSSCSIDEL:
+		while (idlist[j] != 0) {
+			/*
+			 * Find those to be removed
+			 */
+			k = SN_MAX_DEV;
+			for (i = 0; i < info->scsi_dont_use_len; i++)
+				if (info->scsi_dont_use[i] == idlist[j] - 1)
+					k = i;
+
+			if (k != SN_MAX_DEV) {
+				info->scsi_dont_use[k] = 
+					info->scsi_dont_use[info->scsi_dont_use_len - 1];
+				info->scsi_dont_use_len--;
+			}
+			j++;
+		}
+		retval = 0;
+		break;
+
+	default:
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+/*
+ * sn_init() initializes one "sn" device for each SCSI bus in the
+ * system.  There is a one-to-one correspondence between the interface
+ * name and the SCSI bus in the system.  (sn0 == SCSI bus zero, etc)
+ */
+int sn_init(struct device *dev)
+{
+	struct device *new;
+	struct sn_info *info;
+	unsigned char bus_id = 0;
+
+	/*
+	 * This function should be called only once
+	 */
+	if (sn_init_flag)
+		return 0;
+
+	/*
+	 * If there are no SCSI busses in the system, don't do anything
+	 */
+	if (sn_get_my_id(bus_id) == 255) {
+		return -ENODEV;
+	}
+
+	/*
+	 * Fill in the device structure, and insert new ones into the
+	 * device chain for each bus.
+	 */
+	do {
+		dev->priv = kmalloc(sizeof(struct sn_info), GFP_KERNEL);
+		if (dev->priv == NULL)
+			/* FIXME:  you get screwed here if a memory allocation fails
+			   in the middle of allocating the device chain */
+			return -ENOMEM;
+
+		memset(dev->priv, 0, sizeof(struct sn_info));
+		info = (struct sn_info *) dev->priv;
+		info->scsi_bus_id = bus_id;
+
+		dev->open = sn_open;
+		dev->stop = sn_close;
+		dev->get_stats = sn_get_stats;
+		dev->hard_start_xmit = sn_send_packet;
+
+		dev->hard_header = scsi_header;
+		dev->rebuild_header = scsi_rebuild_header;
+		dev->hard_header_cache = scsi_header_cache;
+		dev->header_cache_update = scsi_header_cache_update;
+
+		dev->do_ioctl = sn_do_ioctl;
+
+		dev->type = ARPHRD_SCSI;
+		dev->hard_header_len = SCSI_HLEN;
+		dev->mtu = 3584;
+		dev->addr_len = SCSI_ALEN;
+		dev->tx_queue_len = 100;
+		dev->flags = IFF_BROADCAST;
+		dev->family = AF_INET;
+		dev->dev_addr[0] = sn_get_my_id(bus_id);
+		memset(dev->broadcast, SN_DEST_BCAST, SCSI_ALEN);
+
+		/*
+		 * Look for a new bus, and insert it into the list
+		 */
+		bus_id++;
+		if (sn_get_my_id(bus_id) != 255) {
+			new = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL);
+			memset(new, 0, sizeof(struct device));
+			new->next = dev->next;
+			dev->next = new;
+			dev = new;
+			dev->name = (char *) kmalloc(16, GFP_KERNEL);
+			sprintf(dev->name, "sn%d", bus_id);
+			dev->init = sn_init;
+		}
+	} while (sn_get_my_id(bus_id) != 255);
+
+	/*
+	 * Ok, we're done.  Print a happy message.
+	 */
+	printk("scsi-net driver (01/08/96)\n");
+	sn_init_flag = 1;
+	return 0;
+}
+
+
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+extern int (*ndisc_eth_hook) (unsigned char *, struct device *,
+			      struct sk_buff *);
+#endif
+
+/*
+ *	This code was borrowed from net/ethernet/eth.c
+ *	Create the SCSI header for an arbitrary protocol layer 
+ *
+ *	saddr=NULL      means use device source address
+ *	daddr=NULL      means leave destination address (eg unresolved arp)
+ */
+static int scsi_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+		       void *daddr, void *saddr, unsigned len)
+{
+	struct scsihdr *scsi = (struct scsihdr *) skb_push(skb, SCSI_HLEN);
+
+	/* 
+	 *    Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+	 *      in here instead. It is up to the 802.2 layer to carry protocol information.
+	 */
+
+	memset(scsi, 0, SCSI_HLEN);
+
+	if (type != ETH_P_802_3)
+		scsi->h_proto = htons(type);
+	else
+		scsi->h_proto = htons(len);
+
+	/*
+	 *    Set the source hardware address. 
+	 */
+
+	if (saddr)
+		memcpy(scsi->h_source, saddr, dev->addr_len);
+	else
+		memcpy(scsi->h_source, dev->dev_addr, dev->addr_len);
+
+	/*
+	 *    Anyway, the loopback-device should never use this function... 
+	 */
+
+	if (dev->flags & IFF_LOOPBACK) {
+		memset(scsi->h_dest, 0, dev->addr_len);
+		return (dev->hard_header_len);
+	}
+	if (daddr) {
+		memcpy(scsi->h_dest, daddr, dev->addr_len);
+		return dev->hard_header_len;
+	}
+	return -dev->hard_header_len;
+}
+
+
+/*
+ *	Rebuild the SCSI header. This is called after an ARP
+ *	(or in future other address resolution) has completed on this
+ *	sk_buff. We now let ARP fill in the other fields.
+ */
+
+static int scsi_rebuild_header(struct sk_buff *skb)
+{
+	struct scsihdr *scsi = (struct scsihdr *) skb->data;
+	struct device *dev = skb->dev;
+
+	/*
+	 *    Only ARP/IP and NDISC/IPv6 are currently supported
+	 */
+
+	switch (scsi->h_proto) {
+#ifdef CONFIG_INET
+	case __constant_htons(ETH_P_IP):
+
+		/*
+		 *    Try to get ARP to resolve the header.
+		 */
+
+		return arp_find(scsi->h_dest, skb) ? 1 : 0;
+		break;
+#endif
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+	case __constant_htons(ETH_P_IPV6):
+#ifdef CONFIG_IPV6
+		return (ndisc_eth_resolv(scsi->h_dest, dev, skb));
+#else
+		if (ndisc_eth_hook)
+			return (ndisc_eth_hook(scsi->h_dest, dev, skb));
+#endif
+#endif
+	default:
+		printk(KERN_DEBUG
+		       "%s: unable to resolve type %X addresses.\n",
+		       dev->name, (int) scsi->h_proto);
+
+		memcpy(scsi->h_source, dev->dev_addr, dev->addr_len);
+		return 0;
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ *	Determine the packet's protocol ID. The rule here is that we 
+ *	assume 802.3 if the type field is short enough to be a length.
+ *	This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short scsi_type_trans(struct sk_buff *skb, struct device *dev)
+{
+	struct scsihdr *scsi;
+	unsigned char *rawp;
+	int i;
+
+	skb->mac.raw = skb->data;
+	skb_pull(skb, dev->hard_header_len);
+	scsi = skb->mac.ethernet;
+
+#if 0
+	printk("scsi_type_trans:  ");
+	for(i=0; i<10; i++)
+		printk("0x%02x ", skb->mac.raw[i]);
+	printk("\n");
+#endif
+	
+	if (*scsi->h_dest == SN_DEST_BCAST)
+		skb->pkt_type = PACKET_BROADCAST;
+
+	/*
+	 *    This ALLMULTI check should be redundant by 1.4
+	 *      so don't forget to remove it.
+	 */
+
+	else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		if (memcmp(scsi->h_dest, dev->dev_addr, SCSI_ALEN) != 0)
+			skb->pkt_type = PACKET_OTHERHOST;
+	}
+
+	if (ntohs(scsi->h_proto) >= 1536)
+		return scsi->h_proto;
+
+	rawp = skb->data;
+
+	/*
+	 *    This is a magic hack to spot IPX packets. Older Novell breaks
+	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *      won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *) rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+
+	/*
+	 *    Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+}
+
+static int scsi_header_cache(struct dst_entry *dst, struct dst_entry *neigh,
+	struct hh_cache *hh)
+{
+	unsigned short type = hh->hh_type;
+	struct scsihdr *scsi = (struct scsihdr *) hh->hh_data;
+	struct device *dev = dst->dev;
+
+	if (type == ETH_P_802_3)
+		return -1;
+
+	scsi->h_proto = htons(type);
+
+	memcpy(scsi->h_source, dev->dev_addr, dev->addr_len);
+
+	if (dev->flags & IFF_LOOPBACK) {
+		memset(scsi->h_dest, 0, dev->addr_len);
+		hh->hh_uptodate = 1;
+		return 0;
+	}
+	if (type != ETH_P_IP) {
+		printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n", dev->name, (int) scsi->h_proto);
+		hh->hh_uptodate = 0;
+		return 0;
+	}
+#ifdef CONFIG_INET
+	hh->hh_uptodate = arp_find_1(scsi->h_dest, dst, neigh);
+#else
+	hh->hh_uptodate = 0;
+#endif
+	return 0;
+}
+
+/*
+ * Called by Address Resolution module to notify changes in address.
+ */
+static void scsi_header_cache_update(struct hh_cache *hh, struct device *dev,
+	unsigned char *haddr)
+{
+	if (hh->hh_type != ETH_P_IP) {
+		printk(KERN_DEBUG "scsi_header_cache_update: %04x cache is not implemented\n", hh->hh_type);
+		return;
+	}
+	memcpy(hh->hh_data, haddr, SCSI_ALEN);
+	hh->hh_uptodate = 1;
+}
diff -u -r -N -x *.o linux/drivers/scsi/AM53C974.c linux-scsi/drivers/scsi/AM53C974.c
--- linux/drivers/scsi/AM53C974.c	Sun Apr 19 13:18:54 1998
+++ linux-scsi/drivers/scsi/AM53C974.c	Fri Apr 24 22:56:08 1998
@@ -228,6 +228,16 @@
 #define CMDREG_CFIFO		0x01	/* clear FIFO 				   */
 #define CMDREG_RDEV		0x02	/* reset device 			   */
 #define CMDREG_RBUS		0x03	/* reset SCSI bus 			   */
+/*
+ * Target Mode Commands  (Need to add the rest of them...)
+ */
+#define CMDREG_SENDMSG		0x20	/* Send Message */
+#define CMDREG_SENDSTAT		0x21	/* Send Status */
+#define CMDREG_SENDDAT		0x22	/* Send Data */
+#define CMDREG_RECVMS		0x28	/* Receive message steps */
+#define CMDREG_RECVCMD		0x29	/* Receive Commands */
+#define CMDREG_RECVDATA		0x2A	/* Receive Data */
+#define CMDREG_RECVCS		0x2B	/* Receive Command Steps */
 
 #define STATREG		    	0x10	/* r 	SCSI status reg.		   */
 #define STATREG_INT		0x80	/* SCSI interrupt condition detected	   */
@@ -246,6 +256,8 @@
 #define INSTREG_SR		0x10	/* device on bus has service request       */
 #define INSTREG_SO		0x08	/* successful operation			   */
 #define INSTREG_RESEL		0x04	/* device reselected as initiator	   */
+#define INSTREG_SELA		0x02	/* device selected as target w/ ATN */
+#define INSTREG_SEL		0x01	/* device selected as target */
 
 #define ISREG		    	0x18	/* r	internal state reg.		   */
 #define ISREG_SOF		0x08	/* synchronous offset flag (act. low)	   */
@@ -379,6 +391,8 @@
 static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus, 
                                unsigned char statreg);
 static void AM53C974_intr_bus_reset(struct Scsi_Host *instance);
+static __inline__ void AM53C974_dma_message(struct Scsi_Host *instance, unsigned long length, char *data);
+static void AM53C974_intr_target(struct Scsi_Host *instance);
 
 static struct Scsi_Host *first_instance = NULL;
 static Scsi_Host_Template *the_template = NULL;
@@ -392,7 +406,6 @@
 	S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-#ifdef AM53C974_DEBUG
 static int deb_stop = 1;
 
 static struct {
@@ -471,8 +484,6 @@
 sti();
 }
 
-#endif /* AM53C974_DEBUG */
-
 /**************************************************************************
  * Function : void AM53C974_print(struct Scsi_Host *instance)
  *
@@ -711,6 +722,7 @@
 hostdata->selecting = 0;
 hostdata->disconnecting = 0;
 hostdata->dma_busy = 0;
+hostdata->target = 0;
 
 /* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
 for (search = first_host; 
@@ -766,7 +778,8 @@
 AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
 AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK);
 AM53C974_write_8(CNTLREG1, (DEF_ETM<<7) | CNTLREG1_DISR | (DEF_PERE<<4) | instance->this_id);
-AM53C974_write_8(CNTLREG2, (DEF_ENF<<6));
+			 /* Turn on G2CF for send_message, etc */
+AM53C974_write_8(CNTLREG2, (DEF_ENF<<6) | ( 1<<3 ));
 AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK<<7) | (DEF_FASTSCSI<<4) | (DEF_FASTCLK<<3));
 AM53C974_write_8(CNTLREG4, (DEF_GLITCH<<6) | (DEF_PWD<<5) | (DEF_RAE<<3) | (DEF_RADE<<2) | CNTLREG4_RES);
 }
@@ -943,7 +956,7 @@
        AM53C974_setio(instance);
        /* start to select target if we are not connected and not in the 
           selection process */
-       if (!hostdata->connected && !hostdata->sel_cmd) {
+       if (!hostdata->connected && !hostdata->sel_cmd && !hostdata->target) {
           /* Search through the issue_queue for a command destined for a target 
              that is not busy. */
           for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp; 
@@ -1011,6 +1024,7 @@
 struct Scsi_Host         *instance;
 struct AM53C974_hostdata *hostdata;
 unsigned char            cmdreg, dmastatus, statreg, isreg, instreg, cfifo;
+int i;
 
 /* find AM53C974 hostadapter responsible for this interrupt */
 for (instance = first_instance; instance; instance = instance->next)
@@ -1024,6 +1038,11 @@
 AM53C974_setio(instance);
 dmastatus = AM53C974_read_8(DMASTATUS);
 
+#if 0
+if (hostdata->target != 0)
+	printk("AM53C974_int: dmastatus = 0x%02x\n",dmastatus);
+#endif
+
 DEB_INTR(printk(SEPARATOR_LINE));
 DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus));
 KEYWAIT();
@@ -1073,7 +1092,7 @@
                                     dmastatus); }
    sti();
    }
-   
+
 if (!(dmastatus & DMASTATUS_SCSIINT)) {
    sti();
    return; }
@@ -1085,6 +1104,53 @@
 instreg = AM53C974_read_8(INSTREG);
 cfifo = AM53C974_cfifo();
 
+/* handle this in a better way, move it down lower... perhaps near
+   the normal disconnect handling */
+if (hostdata->target == 2) {
+#if 0
+   	printk("\nAM53C974.c: %d:\n", __LINE__);
+	AM53C974_print(instance);
+	AM53C974_print_queues(instance);
+#endif
+
+	cli();
+	hostdata->target = 0;
+        hostdata->dma_busy = 0;
+        hostdata->disconnecting = 0;
+        hostdata->connected = NULL;
+	hostdata->aborted = 0;
+	hostdata->msgout[0] = NOP;
+	sti();
+
+	sn_recv_msg(instance, 0, hostdata->mesg_buf, hostdata->mesg_len);	
+
+	kfree(hostdata->mesg_buf);
+	hostdata->mesg_len = 0;
+	hostdata->mesg_buf = NULL;
+
+	if (hostdata->sel_cmd != NULL) {
+		cli();
+		hostdata->busy[hostdata->sel_cmd->target] &= 
+			 		~(1 << hostdata->sel_cmd->lun);
+
+	 	LIST(hostdata->sel_cmd, hostdata->issue_queue);
+	 	hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+         	hostdata->issue_queue = hostdata->sel_cmd;
+
+         	hostdata->sel_cmd = NULL;
+         	hostdata->selecting = 0;
+
+		sti();
+	}
+
+   	AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+   	AM53C974_write_8(CMDREG, CMDREG_ESR); /* allow reselect */
+
+	run_main();
+
+	return;
+}
+
 DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
                 instance->host_no, statreg, isreg, instreg, cfifo));
 
@@ -1138,7 +1204,17 @@
    printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
           "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
            cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
-   panic("scsi%d: cannot recover\n", instance->host_no); }
+
+	AM53C974_write_8(CMDREG, CMDREG_RDEV);     /* reset device */
+	udelay(5);
+	AM53C974_write_8(CMDREG, CMDREG_NOP);
+	AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); 
+	AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
+	udelay(10);
+	AM53C974_config_after_reset(instance);
+
+/*   panic("scsi%d: cannot recover\n", instance->host_no); */
+   }
 
 if (instreg & INSTREG_DIS) {
    /* DISCONNECT INTERRUPT */
@@ -1158,6 +1234,39 @@
 
 if (instreg & INSTREG_SO) {
    DEB_INTR(printk("Successful operation interrupt received\n"));
+
+#if 0
+   if (!(hostdata->sel_cmd == NULL && hostdata->target == 1)) {
+   	printk("\nAM53C974.c: %d:\n", __LINE__);
+	   printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
+       		 "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
+		cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
+   }
+#endif
+
+   if (cmdreg == 0xAA) {
+#if 0
+	printk("\nAM53C974.c: %d:\n", __LINE__);
+	AM53C974_print(instance);
+	AM53C974_print_queues(instance);
+#endif
+#if 0
+	printk("AM53C974_intr: message (%p,%d): ",
+		hostdata->mesg_buf,hostdata->mesg_len);
+	for (i=0; i < hostdata->mesg_len; i++)
+		printk("0x%02x ", (hostdata->mesg_buf[i]) & 0xff);
+	printk("\n");
+#endif
+	AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+	AM53C974_write_8(FFREG, 0x00); /* Status Byte */
+	AM53C974_write_8(FFREG, 0x00); /* Message Byte = COMMAND COMPLETE */
+	AM53C974_write_8(CMDREG, 0x24);
+
+	hostdata->target = 2;
+	return;
+   }
+
    if (hostdata->selecting) {
       DEB_INTR(printk("DSR completed, starting select\n"));
       cli();
@@ -1208,10 +1317,37 @@
          return; }
       }
      else {
-      cli();
-      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
-      sti();
-      return; }
+#if 0
+	if (hostdata->target == 1) {
+		printk("\nAM53C974.c: %d:\n", __LINE__);
+		printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
+		        "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
+           		cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
+
+		printk("AM53C974_intr: message (%p,%d): ",
+			hostdata->mesg_buf,hostdata->mesg_len);
+		for (i=0; i < hostdata->mesg_len; i++)
+			printk("0x%02x ", (hostdata->mesg_buf[i]) & 0xff);
+		printk("\n");
+
+   		AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+		AM53C974_write_8(FFREG, 0x00); /* Status Byte */
+		AM53C974_write_8(FFREG, 0x00); /* Message Byte = COMMAND COMPLETE */
+		AM53C974_write_8(CMDREG, 0x24);
+
+		hostdata->target = 2;
+		return;
+	} else {
+#endif
+		cli();
+		AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+		sti();
+		return;
+#if 0
+	}
+#endif
+     }
    }
     
 if (instreg & INSTREG_SR) {
@@ -1227,6 +1363,23 @@
    return;
    }
 
+if (instreg & INSTREG_SELA) {
+#if 0
+	printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+                instance->host_no, statreg, isreg, instreg, cfifo);
+	printk("Selected with ATN\n");
+#endif
+	hostdata->target = 1;
+	AM53C974_intr_target (instance);
+	return;
+}
+
+ if (instreg & INSTREG_SEL) {
+	DEB_INTR(printk("Selected as target, "));
+	AM53C974_write_8(CMDREG, 0x2B);
+ }
+
+
 EXIT:
   DEB_INTR(printk("intr: starting main\n"));
   run_main();
@@ -1234,6 +1387,63 @@
 }
 
 /************************************************************************** 
+* Function : AM53C974_intr_target(struct Scsi_Host *instance)
+*
+* Purpose : handle "Target Mode"
+*
+* Inputs : instance -- which AM53C974
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_target(struct Scsi_Host *instance) 
+{
+	AM53C974_local_declare(); 
+	unsigned char cdb[32];  /* DEFINE ME SOMEWHERE */
+	unsigned char *buf;
+	int i, cfifo, len;
+	AM53C974_setio(instance);
+
+	cfifo = AM53C974_cfifo();
+
+	/*
+	 * Read command into buffer
+	 */
+	for (i = 0; i < cfifo; i++)
+		cdb[i] = AM53C974_read_8(FFREG) & 0xff;
+
+#if 0
+	printk("\nCommand: ");
+	for (i = 0; i < cfifo; i++)
+		printk ("0x%02x ", cdb[i]);
+	printk("\n");
+#endif
+	
+	if (cdb[2] == 0x2A) {
+#if 0
+		printk("SEND_MESSAGE(10) command received\n");
+#endif
+		len = ((unsigned int)cdb[9] << 8) | ((unsigned int)cdb[10]);
+		buf = (unsigned char *)kmalloc(len, GFP_ATOMIC|GFP_DMA);
+		((struct AM53C974_hostdata *)instance->hostdata)->mesg_buf = buf;
+		((struct AM53C974_hostdata *)instance->hostdata)->mesg_len = len;
+#if 0
+		printk("AM53C974_intr_target: allocated %d bytes at %p for message\n", len, buf);
+#endif
+		AM53C974_dma_message(instance, len, buf);	
+	} else {
+		printk("Weirdo command (0x%02x) received.\n",
+				cdb[2]);
+
+		((struct AM53C974_hostdata *)instance->hostdata)->target = 0;
+
+		AM53C974_write_8(FFREG, 0x01); /* Status Byte = CHECK CONDITION */
+		AM53C974_write_8(FFREG, 0x00); /* Message Byte = COMMAND COMPLETE */
+		AM53C974_write_8(CMDREG, 0x24);
+
+	}
+}
+
+/************************************************************************** 
 * Function : AM53C974_intr_disconnect(struct Scsi_Host *instance)
 *
 * Purpose : manage target disconnection
@@ -2092,6 +2302,37 @@
 AM53C974_write_32(DMASPA, virt_to_bus(data));
 AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA);
 AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START);
+}
+
+/************************************************************************** 
+* Function : AM53C974_dma_message(struct Scsi_Host *instance, short dir,
+*                                  unsigned long length, char *data)
+*
+* Purpose : setup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+*          dir -- direction flag, 0: write to device, read from memory; 
+*                                 1: read from device, write to memory
+*          length -- number of bytes to transfer to from buffer
+*          data -- pointer to data buffer
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_dma_message(struct Scsi_Host *instance,
+                                             unsigned long length, char *data)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(DMACMD, (1 << 7) | DMACMD_INTE_D);  /* idle command */
+AM53C974_write_8(STCLREG, (unsigned char)(length & 0xff));
+AM53C974_write_8(STCMREG, (unsigned char)((length & 0xff00) >> 8));
+AM53C974_write_8(STCHREG, (unsigned char)((length & 0xff0000) >> 16));
+AM53C974_write_32(DMASTC, length & 0xffffff);
+AM53C974_write_32(DMASPA, virt_to_bus(data));
+AM53C974_write_8(CMDREG, CMDREG_RECVDATA | CMDREG_DMA);
+AM53C974_write_8(DMACMD, (1 << 7) | DMACMD_INTE_D | DMACMD_START);
 }
 
 /************************************************************************** 
diff -u -r -N -x *.o linux/drivers/scsi/AM53C974.h linux-scsi/drivers/scsi/AM53C974.h
--- linux/drivers/scsi/AM53C974.h	Sun Dec 21 19:04:48 1997
+++ linux-scsi/drivers/scsi/AM53C974.h	Fri Apr 24 22:33:15 1998
@@ -48,6 +48,9 @@
     unsigned  char sync_en[8];          /* sync. negotiation performed (in effect) */
     unsigned  char max_rate[8];         /* max. transfer rate (setup) */
     unsigned  char max_offset[8];       /* max. sync. offset (setup), only valid if corresponding sync_en is nonzero */
+    volatile unsigned int    target;
+    volatile char *mesg_buf;
+    volatile int mesg_len;
     };
 
 extern struct proc_dir_entry proc_scsi_am53c974;
diff -u -r -N -x *.o linux/drivers/scsi/Makefile linux-scsi/drivers/scsi/Makefile
--- linux/drivers/scsi/Makefile	Mon Feb 23 18:01:00 1998
+++ linux-scsi/drivers/scsi/Makefile	Fri Apr 24 22:33:15 1998
@@ -83,6 +83,10 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSINET),y)
+L_OBJS += sn.o
+endif
+
 ifeq ($(CONFIG_SCSI_ADVANSYS),y)
 L_OBJS += advansys.o
 else
diff -u -r -N -x *.o linux/drivers/scsi/aha152x.c linux-scsi/drivers/scsi/aha152x.c
--- linux/drivers/scsi/aha152x.c	Fri Apr  4 10:52:23 1997
+++ linux-scsi/drivers/scsi/aha152x.c	Fri Apr 24 22:33:15 1998
@@ -493,6 +493,8 @@
   
   unsigned char message[256];
   int           message_len;
+  int		target;
+  unsigned char	*net_buf;
 
 #ifdef DEBUG_AHA152X
   int           debug;
@@ -1111,6 +1113,12 @@
     SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
   } else {
     SCpnt->SCp.ptr              = (char *)SCpnt->request_buffer;
+#if 0
+	printk("aha152x_queue:  packet = %p:\n", SCpnt->SCp.ptr);
+	for(i=0; i<SCpnt->request_bufflen; i++)
+		printk("0x%02x ", SCpnt->SCp.ptr[i] & 0xff);
+	printk("\n");
+#endif
     SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
     SCpnt->SCp.buffer           = NULL;
     SCpnt->SCp.buffers_residual = 0;
@@ -1261,7 +1269,7 @@
 
     /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
     SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
-    SETPORT(SIMODE1, ENSELTIMO);
+    SETPORT(SIMODE1, ENATNTARG | ENSELTIMO);
 
     /* Enable SELECTION OUT sequence */
     SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
@@ -1319,6 +1327,11 @@
   /* enable interrupts */
   SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
   SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+/*  SETBITS(SIMODE0, ENSELDI); */
+  SETBITS(SIMODE1, ENATNTARG); 
+  SETBITS(SCSISEQ, ENSELI); 
+
 }
 
 /*
@@ -1498,6 +1511,8 @@
 
     SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
     SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+    SETBITS(SIMODE1, ENATNTARG); 
+    SETBITS(SCSISEQ, ENSELI); 
 
 #if 0
 /* Why poll for the BUS FREE phase, when we have setup the interrupt!? */
@@ -1531,6 +1546,93 @@
 }
 
 /*
+ * aha152x_targ_datain_pio (or something...)
+ * based on aic_datain_pio() in aic6360.c from NetBSD source
+ */
+int aha152x_targ_datain_pio(struct Scsi_Host *shpnt, unsigned char *p, int n)
+{
+	int dmastat;
+	int in = 0;
+	int xfer = 0;
+	int x;
+
+#define min(a, b) ( ((a)<(b)) ? (a) : (b) )
+#undef TARGDEBUG
+#define DMAFIFOSIZE 128
+
+#ifdef TARGDEBUG
+	disp_ports(shpnt);
+#endif
+	SETSTCNT(n);
+
+	SETPORT(DMACNTRL0, RSTFIFO);
+	SETPORT(SXFRCTL0, CH1 | CLRCH1);
+	SETPORT(SXFRCTL0, SCSIEN | DMAEN | CH1);
+	SETPORT(DMACNTRL0, ENDMA);
+	SETBITS(SIMODE0, ENSWRAP | ENSDONE);
+
+
+
+	while(n > 0) {
+#ifdef TARGDEBUG
+		x=-1024;
+#endif
+		for(;;) {
+			dmastat = GETPORT(DMASTAT);
+#ifdef TARGDEBUG
+			if (x++ == 0) printk("1:  0x%02x 0x%02x 0x%02x %d %d %d\n",
+				dmastat&0xff, GETPORT(SSTAT0)&0xff, GETPORT(SSTAT1)&0xff,
+				GETSTCNT(), n, GETPORT(FIFOSTAT));
+#endif
+			if ((dmastat & (DFIFOFULL | INTSTAT | WORDRDY)) != 0)
+				break;
+		}
+		
+		if (dmastat & DFIFOFULL) {
+			n -= DMAFIFOSIZE;
+			in += DMAFIFOSIZE;
+
+			insw(DATAPORT, p, DMAFIFOSIZE/2);
+			p += DMAFIFOSIZE;
+		} else {
+			xfer = min(GETPORT(FIFOSTAT), n);
+
+			n -= xfer;
+			in += xfer;
+
+			if (xfer & 1) {
+				if (xfer > 1) {
+					insw(DATAPORT, p, xfer/2);
+					p += xfer & ~1;
+				}
+				SETPORT(DMACNTRL0, ENDMA | _8BIT);
+				insb(DATAPORT, p++, 1); 
+			} else {
+				insw(DATAPORT, p, xfer/2);
+				p += xfer;
+			}
+		}
+		if (dmastat & INTSTAT) {
+#ifdef TARGDEBUG
+			printk("2:  0x%02x 0x%02x 0x%02x %d %d %d\n",
+				dmastat&0xff, GETPORT(SSTAT0)&0xff, GETPORT(SSTAT1)&0xff,
+				GETSTCNT(), n, GETPORT(FIFOSTAT));
+#endif
+			break;
+		}
+	}
+
+	SETPORT(SXFRCTL0, CH1|CLRCH1);
+	SETBITS(SSTAT0, CLRSDONE);
+	SETPORT(DMACNTRL0, 0);
+	CLRBITS(SIMODE0, ENSWRAP);
+	return in;
+}
+			
+			
+
+
+/*
  * Interrupts handler (main routine of the driver)
  */
 void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs)
@@ -1558,6 +1660,189 @@
   CLRBITS(DMACNTRL0, INTEN);
   sti();  /* Yes, sti() really needs to be here */
 
+  if(TESTHI(SSTAT0, (SELDI|TARGET))) {
+	char grcode[8] = { 6, 10, 10, 0, 0, 12, 0, 0 };
+	char msg;
+	unsigned char tcmd[12];
+	int  i=0;
+
+	unsigned int  len;
+	unsigned char *buf;
+
+	HOSTDATA(shpnt)->target = 1;
+#ifdef TARGDEBUG
+	printk("Target mode!\n");
+	disp_ports(shpnt);
+	disp_enintr(shpnt);
+#endif
+	
+	SETPORT(SIMODE0, 0);
+	SETPORT(SIMODE1, 0);
+	SETPORT(SCSISEQ, 0);
+	SETPORT(SSTAT0, CLRSELDI|CLRSWRAP);
+	SETPORT(SSTAT1, CLRBUSFREE);
+
+	/*
+	 * 4) MESSAGE OUT
+	 * The target asserts the C/D and MSG signals, and negates the
+	 * I/O signal for the message transfer.
+	 */
+	CLRSETBITS(SCSISIG, (IOO), (CDO | MSGO));
+
+	/*
+	 * Assert REQO and wait for ACK to go high before reading the data
+	 */
+	SETBITS(SXFRCTL0, SPIOEN);
+	while(TESTLO(SSTAT0, SPIORDY))
+		barrier();
+
+	/*
+	 * Read the data, and negate the REQ signal
+	 */
+	CLRBITS(SXFRCTL0, SPIOEN);
+	msg = GETPORT(SCSIDAT);
+
+	if (TESTHI(SSTAT1, SCSIPERR)) {
+		printk("MSGI phase perr\n");
+		goto targdone;
+	}
+#ifdef TARGDEBUG
+	printk("Target mode: MSGI = 0x%02x\n", msg&0xff);
+	disp_ports(shpnt);
+#endif
+	
+	/*
+	 * 5) COMMAND phase.  The target aserts the CD signal, negates
+	 * the IO and MSG signal.
+	 */
+	CLRSETBITS(SCSISIG, (IOO | MSGO), CDO);
+
+	/*
+	 * Target asserts REQO (starts handshake proc), and
+	 * waits for ACK to go high before reading the data bus
+	 */
+	SETBITS(SXFRCTL0, SPIOEN);
+#ifdef TARGDEBUG
+	disp_ports(shpnt);
+	printk("Command bytes: ");
+#endif
+
+	do {
+		while(TESTLO(SSTAT0, SPIORDY))
+			barrier();
+		tcmd[i] = GETPORT(SCSIDAT);
+#ifdef TARGDEBUG
+		printk("0x%02x ", tcmd[i] & 0xff);
+#endif
+	} while(i++ < grcode[(tcmd[0] >> 5) & 7] - 1);
+
+	while(TESTLO(SSTAT0, SPIORDY))
+		barrier();
+	CLRBITS(SXFRCTL0, SPIOEN);
+	tcmd[i] = GETPORT(SCSIDAT);
+	if (tcmd[0] != 0x2a) {
+		printk("Don't understand command 0x%02x\n", tcmd[0]&0xff);
+		goto targdone;
+	}
+#ifdef TARGDEBUG
+	printk("0x%02x\n", tcmd[i] & 0xff);
+#endif
+	if (TESTHI(SSTAT1, SCSIPERR)) {
+		printk("CMD phase perr\n");
+		goto targdone;
+	}
+	
+	/*
+	 * 7) DATA OUT phase.  The target negates the CD, IO and MSG 
+	 * signls for each byte transfered during this phase.
+	 */
+	CLRBITS(SCSISIG, (CDO | IOO | MSGO));
+	len = ((unsigned int)tcmd[7] <<8) | ((unsigned int)tcmd[8]);
+
+	if (HOSTDATA(shpnt)->net_buf == NULL)
+		HOSTDATA(shpnt)->net_buf = (char *)kmalloc(0xffff, GFP_ATOMIC);
+
+	buf = HOSTDATA(shpnt)->net_buf;
+	if (buf == NULL) goto targdone;
+
+#ifdef TARGDEBUG
+	printk("Receive packet (%d bytes):  ", len);
+#endif
+	i = aha152x_targ_datain_pio(shpnt, buf, len);
+
+#ifdef TARGDEBUG
+	printk("got %d bytes\n", i);
+#endif
+
+	if (TESTHI(SSTAT1, SCSIPERR)) {
+		printk("DATAIN phase perr\n");
+		goto targdone;
+	}
+	if (i == len)
+		sn_recv_msg(shpnt, 0, buf, len);
+	else 
+		SETPORT(DMACNTRL0, RSTFIFO);
+		
+	/*
+	 * 8) STATUS phase.  The target asserts the CD and IO signals
+	 * and negates the MSG signal for the byte transfered during
+	 * this phase.  Uses the same handshake as the DATA IN phase.
+	 */
+
+	CLRSETBITS(SCSISIG, MSGO, (CDO | IOO));
+
+	SETBITS(SXFRCTL0, SPIOEN);
+	while(TESTLO(SSTAT0, SPIORDY))
+		barrier();
+	SETPORT(SCSIDAT, 0x00);
+	while(TESTLO(SSTAT0, SPIORDY))
+		barrier();
+	CLRBITS(SXFRCTL0, SPIOEN);	
+	GETPORT(SCSIDAT);
+
+	/*
+	 * 9) MESSAGE IN phase.  The target aserts CS, IO and MSG
+	 * during the byte transfered during this phase.
+	 * Same handshake as the DATA IN phase.
+	 */
+
+	SETBITS(SCSISIG, (CDO | IOO | MSGO));
+
+	SETBITS(SXFRCTL0, SPIOEN);
+	while(TESTLO(SSTAT0, SPIORDY))
+		barrier();
+	SETPORT(SCSIDAT, COMMAND_COMPLETE);
+	CLRBITS(SXFRCTL0, SPIOEN);	
+	GETPORT(SCSIDAT);
+#ifdef TARGDEBUG
+	disp_ports(shpnt);
+#endif
+
+targdone:
+	SETPORT(SXFRCTL0, CH1);
+	SETPORT(SCSISIG, 0);
+	SETPORT(SCSISEQ, 0);
+
+	if (CURRENT_SC) {
+#ifdef TARGDEBUG
+		printk("Requeueing CURRENT_SC (%p, %p)\n", CURRENT_SC, ISSUE_SC);
+#endif
+		CURRENT_SC->SCp.phase = not_issued;
+		CURRENT_SC->host_scribble = ISSUE_SC;
+		ISSUE_SC = CURRENT_SC;  
+		CURRENT_SC = NULL;
+	}
+	SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+	SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+	SETBITS(SIMODE1, ENATNTARG); 
+	SETBITS(SCSISEQ, ENSELI);
+	SETPORT(SSTAT0, CLRSELDI);
+	SETBITS(DMACNTRL0, INTEN);
+	HOSTDATA(shpnt)->target = 0;
+	if (!ISSUE_SC)
+		return;
+  }
   /* disconnected target is trying to reconnect.
      Only possible, if we have disconnected nexuses and
      nothing is occupying the bus.
@@ -1669,7 +1954,10 @@
   /* Check, if we aren't busy with a command */
   if(!CURRENT_SC) {
     /* bus is free to issue a queued command */
-    if(TESTHI(SSTAT1, BUSFREE) && ISSUE_SC) {
+    if(TESTHI(SSTAT1, BUSFREE) && ISSUE_SC && HOSTDATA(shpnt)->target)
+	printk("HOSTDATA(shpnt)->target = %d\n", HOSTDATA(shpnt)->target);
+
+    if(TESTHI(SSTAT1, BUSFREE) && ISSUE_SC && !HOSTDATA(shpnt)->target) {
       save_flags(flags);
       cli();
 #if defined(DEBUG_QUEUES)
@@ -1696,7 +1984,7 @@
 
       /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
       SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
-      SETPORT(SIMODE1, ENSELTIMO);
+      SETPORT(SIMODE1, ENATNTARG | ENSELTIMO);
 
       /* Enable SELECTION OUT sequence */
       SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
@@ -1957,6 +2245,7 @@
         make_acklow(shpnt);
         getphase(shpnt);
       }
+  
 
       if(i<CURRENT_SC->cmd_len && TESTHI(SSTAT1, PHASEMIS))
         aha152x_panic(shpnt, "target left COMMAND");
@@ -2226,7 +2515,6 @@
         /* wait for PHASEMIS or full FIFO */
         while(TESTLO(DMASTAT, DFIFOFULL|INTSTAT))
           barrier();
-
 #if defined(DEBUG_DATAI)
         if(HOSTDATA(shpnt)->debug & debug_datai)
           printk("ok, ");
@@ -2408,7 +2696,13 @@
         if(HOSTDATA(shpnt)->debug & debug_datao)
           printk("data_count=%d, ", data_count);
 #endif
-  
+#if 0
+	printk("aha152x:  Packet dump (%p):\n", CURRENT_SC->SCp.ptr);
+	for(i=0; i<data_count; i++)
+		printk("0x%02x ", CURRENT_SC->SCp.ptr[i] & 0xff);
+	printk("\n");
+#endif
+
         if(data_count&1) {
           /* put a single byte in byte mode */
           SETBITS(DMACNTRL0, _8BIT);
@@ -2813,7 +3107,7 @@
   printk("\naha152x: leaving %s() (%x)\n", func, jiffies);
   if(!in_driver) {
     printk("aha152x: %s already left.\n", should_leave);
-    panic("aha152x: %s already left driver.\n");
+    panic("aha152x: %s already left driver.\n", should_leave);
   }
 
   in_driver--;
diff -u -r -N -x *.o linux/drivers/scsi/hosts.c linux-scsi/drivers/scsi/hosts.c
--- linux/drivers/scsi/hosts.c	Sun Apr 19 13:19:48 1998
+++ linux-scsi/drivers/scsi/hosts.c	Fri Apr 24 22:33:15 1998
@@ -614,6 +614,9 @@
 #ifdef CONFIG_CHR_DEV_SG
     scsi_register_device(&sg_template);
 #endif
+#ifdef CONFIG_SCSINET
+    scsi_register_device(&sn_template);
+#endif
     
 #if 0      
     max_scsi_hosts = next_scsi_host;
diff -u -r -N -x *.o linux/drivers/scsi/hosts.h linux-scsi/drivers/scsi/hosts.h
--- linux/drivers/scsi/hosts.h	Sun Apr 19 13:19:48 1998
+++ linux-scsi/drivers/scsi/hosts.h	Fri Apr 24 22:33:15 1998
@@ -447,6 +447,7 @@
 extern struct Scsi_Device_Template st_template;
 extern struct Scsi_Device_Template sr_template;
 extern struct Scsi_Device_Template sg_template;
+extern struct Scsi_Device_Template sn_template;
 
 int scsi_register_device(struct Scsi_Device_Template * sdpnt);
 
diff -u -r -N -x *.o linux/drivers/scsi/scsi.c linux-scsi/drivers/scsi/scsi.c
--- linux/drivers/scsi/scsi.c	Sun Apr 19 13:19:01 1998
+++ linux-scsi/drivers/scsi/scsi.c	Fri Apr 24 22:33:15 1998
@@ -1207,6 +1207,7 @@
     SCpnt->old_use_sg  = 0;
     SCpnt->transfersize = 0;      /* No default transfer size */
     SCpnt->cmd_len = 0;
+    SCpnt->network = 0;		  /* not a network packet command */
 
     SCpnt->underflow = 0;         /* Do not flag underflow conditions */
 
@@ -1396,7 +1397,7 @@
  * drivers go for the same host at the same time.
  */
 
-void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
+int scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
 		  void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
 		  int timeout, int retries)
 {
@@ -1434,6 +1435,9 @@
      */
 
     SCpnt->pid = scsi_pid++;
+ 
+    if (SCpnt->network && SCSI_BLOCK(host))
+	return -EBUSY;
 
     while (SCSI_BLOCK((Scsi_Device *) NULL, host)) {
     	spin_unlock(&io_request_lock);	/* FIXME!!! */
@@ -2541,6 +2545,8 @@
     printk("resize_dma_pool: dma sectors        = %d\n", dma_sectors);
     printk("resize_dma_pool: need isa buffers   = %d\n", scsi_need_isa_buffer);
 #endif
+
+    return 0;
 }
 
 #ifdef CONFIG_MODULES		/* a big #ifdef block... */
diff -u -r -N -x *.o linux/drivers/scsi/scsi.h linux-scsi/drivers/scsi/scsi.h
--- linux/drivers/scsi/scsi.h	Mon Apr 13 21:05:13 1998
+++ linux-scsi/drivers/scsi/scsi.h	Fri Apr 24 22:37:45 1998
@@ -376,7 +376,7 @@
  *  DID_ABORT is returned in the hostbyte.
  */
 
-extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
+extern int scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
 			 void *buffer, unsigned bufflen, 
 			 void (*done)(struct scsi_cmnd *),
 			 int timeout, int retries);
@@ -609,6 +609,8 @@
     
     unsigned char      tag;	   /* SCSI-II queued command tag */
     unsigned long      pid;	   /* Process ID, starts at 0 */
+
+    unsigned char      network;
 };
 
 
diff -u -r -N -x *.o linux/drivers/scsi/sn.c linux-scsi/drivers/scsi/sn.c
--- linux/drivers/scsi/sn.c	Wed Dec 31 18:00:00 1969
+++ linux-scsi/drivers/scsi/sn.c	Fri Apr 24 22:33:15 1998
@@ -0,0 +1,617 @@
+/*
+ *      sn.c - SCSI half of IP encapculation in SCSI driver
+ *
+ *      Authors:  Randy Scott (scottr@bork.com)
+ *		  Chris Frantz (frantzc@bork.com)
+ *
+ *      Bork Systems Group 
+ *	http://www.bork.com
+ *
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include <linux/scsinet.h>
+
+extern volatile struct Scsi_Host *host_active;
+
+#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
+		  || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
+
+extern void sn_rx_packet (struct device *dev, void *pkt_data, 
+		unsigned int pkt_len);
+
+int sn_tx_packet (int bus, char *packet, int len);
+int sn_net_init (struct device *net_device);
+int sn_net_down (struct sn_info *sni);
+unsigned char sn_get_my_id (int bus);
+
+static int sn_init(void);
+static int sn_attach(Scsi_Device *);
+static int sn_do_transmit (int bus, int dest, char *packet, unsigned short len);
+
+/*
+ * Initialize our Scsi_Device_Template.
+ *
+ * Make sure that the dev_noticed is greater than 0
+ * to insure that our sn_init() function is called.
+ */
+struct Scsi_Device_Template sn_template =
+	{NULL, NULL, "sn", NULL, 0xff, 0, 0, 1, 0, 0,
+	 NULL, sn_init, NULL, sn_attach, NULL};
+
+static int sn_hosts = 0;	/* set to number of SCSI busses in system
+				   when driver is initialized. */
+
+/*
+ * Data structure used to store information about a particular
+ * device on the SCSI bus.  An array of these structures is
+ * created.  The array is index by SCSI ID number on a particular
+ * SCSI bus.
+ */
+struct scsi_net_devs {
+	int our_device;
+	int usable;
+
+	Scsi_Device *device;
+};
+
+/*
+ * Data structure used to store information about a particular
+ * SCSI bus.  An array of these structures is created.  There
+ * will be exactly "sn_hosts" number of these structures in
+ * the array (indexed as 0 to sn_hosts-1).
+ */
+struct scsi_networks {
+	int inuse;		/* flag set when this SCSI bus is in use as
+				   as a network device. */
+
+	int channel_id;		/* Channel of the host adapter this bus
+				   is on. */
+
+	int devices;		/* Number of devices that can be connected
+				   to this SCSI bus.  Typically 8. */
+
+	struct Scsi_Host *host; /* pointer to the host adapter's data 
+				   structure. */
+
+	struct device *net_device; /* points to the entry in the network
+				      device table that corresponds to
+				      this SCSI bus. */
+
+	struct scsi_net_devs *devs; /* array of SCSI devices that can be
+				       connected to this bus. */
+};
+
+static struct scsi_networks *scsi_nets = NULL;
+
+/*
+ * sn_create_device() 
+ *	this function allocates memory for, and fills in all of the 
+ *	generic fields in a Scsi_Device structure.  The caller must
+ *	fill in the remaining fields (id, host adapter, etc.).
+ *
+ *	If the function fails to allocate enough memory for the
+ * 	structure, it returns a NULL value.
+ */
+Scsi_Device *sn_create_device (void)
+{
+	Scsi_Device *sdptr = NULL;
+
+	sdptr = (Scsi_Device *)kmalloc(sizeof(Scsi_Device), GFP_KERNEL);
+
+	if (sdptr != NULL) {
+		memset(sdptr, 0, sizeof(Scsi_Device));
+
+		sdptr->next = NULL;
+		sdptr->lun = 0;
+		sdptr->manufacturer = 0;
+		sdptr->attached = 1;
+		sdptr->access_count = 0;
+		sdptr->device_wait = NULL;
+		sdptr->scsi_request_fn = NULL;
+		sdptr->device_queue = NULL;
+		sdptr->hostdata = NULL;
+		sdptr->type = -1;
+		sdptr->scsi_level = 2;  /* assume SCSI-II ? */
+		sdptr->vendor[0] = 0;
+		sdptr->model[0] = 0;
+		sdptr->rev[0] = 0;
+		sdptr->current_tag = 0;
+		sdptr->sync_min_period = 0;
+		sdptr->sync_max_offset = 0;
+		sdptr->queue_depth = 0;
+		sdptr->writeable = 1;
+		sdptr->removable = 0;
+		sdptr->random = 0;
+		sdptr->has_cmdblocks = 1;
+		sdptr->changed = 0;
+		sdptr->busy = 0;
+		sdptr->lockable = 0;
+		sdptr->borken = 0;
+		sdptr->tagged_supported = 0;
+		sdptr->tagged_queue = 0;
+		sdptr->disconnect = 1;
+		sdptr->soft_reset = 0;
+		sdptr->sync = 0;
+		sdptr->single_lun = 0;
+		sdptr->was_reset = 0;
+		sdptr->expecting_cc_ua = 0;
+	} else 
+		printk("Cannot allocate Scsi_Device structure.\n");
+
+	return sdptr;
+}
+
+/*
+ * sn_init()
+ *	Used to initialize the SCSI portion of the driver.  It sets up
+ *	all of the necessary data structures used by the driver.  Additional
+ *	initialization is done when the sn_attach() function is called by
+ *	the SCSI mid-level driver and the sn_net_init() is called by the
+ *	network portion of the driver when the network interface is
+ *	"brought up."
+ *
+ *	Returns 0 if successful, and error number otherwise.
+ */
+static int sn_init(void)
+{
+	struct Scsi_Host *shptr;
+	int i,j;
+
+	/* 
+	 * Return if for some reason we are being called for a 
+ 	 * second time.
+	 */
+	if (scsi_nets)
+		return 0;
+
+	printk("sn: init SCSI network device. (SCSI portion)\n");
+
+	/*
+	 * Loop through all of the SCSI host adapters in the system
+	 * and add up all of their "max_channel" values to determine
+	 * the maximum number of possible SCSI network devices that
+	 * can ever be present in the system.
+	 */
+	for (shptr = scsi_hostlist; shptr; shptr = shptr->next)
+		sn_hosts += shptr->max_channel+1;
+
+	printk("sn: using %d SCSI bus%s.\n", sn_hosts,
+		(sn_hosts > 1 || sn_hosts == 0)?"ses":"");
+
+	scsi_nets = (struct scsi_networks *)
+	    scsi_init_malloc(sn_hosts * sizeof(struct scsi_networks), GFP_ATOMIC);
+	if (scsi_nets == NULL)
+		printk("sn: sn_init: cannot allocate scsi_nets!\n");
+
+	memset(scsi_nets, 0, sn_hosts * sizeof(struct scsi_networks));
+
+	/*
+	 * Run through the list of SCSI host adapters again.
+	 * This time, create the entry in the scsi_nets table
+	 * for each SCSI bus (channels of every adapter) on the
+	 * system.
+	 * 
+	 * Then, for each of the channels, make up fake Scsi_Device
+	 * structure for each of the possible devices (IDs) on that
+	 * bus.
+	 */
+	sn_hosts = 0;
+	for (shptr = scsi_hostlist; shptr; shptr = shptr->next)
+		for (i = 0; i <= shptr->max_channel; i++) {
+			scsi_nets[sn_hosts].inuse = 0;
+			scsi_nets[sn_hosts].host = shptr;
+			scsi_nets[sn_hosts].channel_id = i;
+			scsi_nets[sn_hosts].devices = shptr->max_id;
+
+			/*
+			 * Allocate memory for enough of our device
+			 * entries.
+			 */
+			scsi_nets[sn_hosts].devs = (struct scsi_net_devs *)
+				scsi_init_malloc(shptr->max_id * sizeof(struct scsi_net_devs), GFP_ATOMIC);
+
+			/*
+			 * Initialize the device structures for this SCSI bus
+			 * to indicate that all of the devices are usable and
+			 * are currently using fake Scsi_Device structures
+			 * (which will be allocated later).
+			 */
+			for (j = 0; j < shptr->max_id; j++) {
+				scsi_nets[sn_hosts].devs[j].our_device = 
+					scsi_nets[sn_hosts].devs[j].usable = 
+					(shptr->this_id == j) ? 0 : 1;
+				scsi_nets[sn_hosts].devs[j].device = NULL;
+			}
+
+			sn_hosts++;
+		}
+
+	return 0;
+}
+
+/*
+ * sn_attach()
+ *	Called by the mid-level SCSI driver to "attach" a SCSI device
+ *	to our driver.  We will "attach" any unattached devices that
+ *	we are given, otherwise, we will exclude the device from 
+ *	having network packets sent at it.  This should prevent us
+ *	from sending packets to disk drives, tape drives, etc.
+ *
+ *	Returns 0 on success, an error code otherwise.
+ */
+static int sn_attach(Scsi_Device * sdp)
+{
+	int i, j;
+
+	if (sdp == NULL) {
+		printk("sn: sn_attach: passed a NULL sdp.\n");
+		return 0;
+	}
+
+	if (scsi_nets == NULL) {
+		printk("sn: sn_attach: scsi_nets is NULL!?\n");
+		return 0;
+	}
+
+	/*
+ 	 * Loop through all of our SCSI busses and devices to find
+	 * the entry that is associated with the device that we
+	 * were passed.
+	 */
+	for (i = 0; i < sn_hosts; i++) {
+		/*
+		 * A little error checking.. Should only happen if 
+		 * sn_init() fails miserably.
+		 */
+		if (scsi_nets[i].devs == NULL) {
+			printk("sn: sn_attach: scsi_nets[%d].devs is NULL\n", i);
+			continue;
+		}
+
+		for (j = 0; j < scsi_nets[i].devices; j++) {
+			if (scsi_nets[i].devs[j].our_device == 1 &&
+			    j == sdp->id &&
+			    scsi_nets[i].channel_id == sdp->channel &&
+			    scsi_nets[i].host == sdp->host) {
+
+				if (sdp->attached == 0) {
+					scsi_nets[i].devs[j].usable = 1;
+					sdp->attached++;
+				} else scsi_nets[i].devs[j].usable = 0;
+
+				scsi_nets[i].devs[j].our_device = 0;
+				scsi_nets[i].devs[j].device = sdp;
+
+				break;
+			}
+		}
+	}
+				
+	return 0;
+}
+
+/*
+ * sn_get_my_id()
+ *	Used by the network driver to get the host adapters ID on a
+ * 	particular SCSI bus.  
+ *
+ * 	Returns 255 if the bus does not exist in the system, the
+ *	SCSI ID of the host adapter otherwise.
+ */
+unsigned char sn_get_my_id (int bus)
+{
+	return (bus >= sn_hosts) ? 255 : scsi_nets[bus].host->this_id;
+}
+
+/*
+ * sn_net_init() - called by network side of the driver to start up
+ *		   communications on a given SCSI bus.
+ */
+int sn_net_init (struct device *net_device)
+{
+	struct sn_info *sni = (struct sn_info *)net_device->priv;
+	int bus;
+	int i,j;
+	Scsi_Device *sdptr;
+
+	if (net_device == NULL || sni == NULL) {
+		printk("sn: sn_net_init passed a NULL sn_info block?\n");
+		return -EINVAL;
+	}
+
+	if (sni->scsi_bus_id < 0 || sni->scsi_bus_id > sn_hosts) {
+		printk("sn: sn_net_init sn_info block contains bad scsi_bus_id!\n");
+		return -EINVAL;
+	}
+
+	bus = sni->scsi_bus_id;
+	
+	if (sni->scsi_dont_use_len > scsi_nets[bus].devices) {
+		printk("sn: scsi_dont_use_len is greater than num. of devices!\n");
+		return -EINVAL;
+	}
+	
+	/*
+	 * Take the list of devices from scsi_dont_use and exclude
+	 * the listed devices from the list of devices available
+	 * to be used for transmission.
+	 */
+	for (i = 0; i < sni->scsi_dont_use_len; i++) {
+		j = sni->scsi_dont_use[i];
+
+		if (j > scsi_nets[bus].devices) {
+			printk("sn: dont_use id is too large! (%d > %d)\n",
+				j, scsi_nets[bus].devices);
+			return -EINVAL;
+		}
+		if (scsi_nets[bus].devs[j].usable) {
+			scsi_nets[bus].devs[j].usable = 0;
+			if (scsi_nets[bus].devs[j].our_device && 
+			    scsi_nets[bus].devs[j].device != NULL) {
+				scsi_nets[bus].devs[j].our_device=0;
+				scsi_nets[bus].devs[j].device = NULL;
+
+				/* 
+				 * This should never happen under normal
+				 * circumstances.
+				 */
+				printk("sn: sn_net_init: remove ourdevice at line %d\n", __LINE__);
+			}
+		}
+	}
+
+	printk("sn: sn_net_init: bus %d is using SCSI IDs: ", bus);
+	for (i = 0; i < scsi_nets[bus].devices; i++) 
+		if (scsi_nets[bus].devs[i].usable == 1)
+			printk("%d ",i);
+	printk("\n");
+
+
+	/*
+	 * Here we actually create all of the face Scsi_Device
+	 * structures that we need later to transmit packets.
+	 */
+	for (j = 0; j < scsi_nets[bus].devices; j++)
+		if (scsi_nets[bus].devs[j].our_device &&
+		    scsi_nets[bus].devs[j].usable) {
+			/*
+			 * If we are unable to allocate the fake
+			 * device structure, mark this device
+			 * as unusable.  A message is printed
+			 * in the sn_create_device function
+			 * if the malloc fails.
+			 */
+			sdptr = sn_create_device();
+			if (sdptr != NULL) {
+				scsi_nets[bus].devs[j].device = sdptr;
+
+				sdptr->id = j;
+				sdptr->channel = scsi_nets[bus].channel_id;
+				sdptr->host = scsi_nets[bus].host;
+			} else scsi_nets[bus].devs[j].usable = 0;
+		}
+
+	scsi_nets[bus].inuse = 1;
+	scsi_nets[bus].net_device = net_device;
+
+	return 0;
+}
+
+/*
+ * sn_net_down()
+ *	Called by the network device with the network interface
+ * 	is being taken "down."  It frees the fake Scsi_Device
+ *	structures that we allocated earlier and places the
+ *	data structure in a state where it can be easily 
+ * 	re-enabled with sn_net_init().
+ */
+int sn_net_down (struct sn_info *sni)
+{
+	int i;
+	int bus;
+
+	if (sni == NULL) {
+		printk("sn: sn_net_init passed a NULL sn_info block?\n");
+		return -EINVAL;
+	}
+
+	if (sni->scsi_bus_id < 0 || sni->scsi_bus_id > sn_hosts) {
+		printk("sn: sn_net_init sn_info block contains bad scsi_bus_id!\n");
+		return -EINVAL;
+	}
+
+	bus = sni->scsi_bus_id;
+
+	for (i = 0; i < scsi_nets[bus].devices; i++)
+		if (scsi_nets[bus].devs[i].our_device) {
+			if (scsi_nets[bus].devs[i].device != NULL) {
+				kfree(scsi_nets[bus].devs[i].device);
+				scsi_nets[bus].devs[i].device = NULL;
+			}
+
+			if (scsi_nets[bus].devs[i].usable == 0);
+				scsi_nets[bus].devs[i].usable = 1;
+		}
+
+	scsi_nets[bus].inuse = 0;
+
+	return 0;
+}
+
+/*
+ * sn_tx_packet() - used by network driver to send packets through the
+ *    		    SCSI network interface.
+ */
+int sn_tx_packet (int bus, char *packet, int len)
+{
+	struct scsihdr *scsi = (struct scsihdr *)packet;
+	int i, retval = 0;
+	unsigned char dest = scsi->h_dest[0];
+
+	if (scsi_nets[bus].inuse == 0) {
+		printk("sn: sn_tx_packet: tried to send packet on not-inuse bus\n");
+		return -EINVAL;
+ 	}	
+
+	if (packet == NULL || len == 0) {
+		printk("sn: sn_tx_packet: empty packet?!\n");
+		return -EINVAL;
+	}
+
+#if 0
+	printk ("sn: sn_tx_packet: packet dump (%p):\n", packet);
+	for (i = 0; i<len; i++) 
+		printk ("0x%02x ", packet[i] & 0xff);
+	printk ("\n");
+#endif
+
+	/*
+	 * If the destination address of the packet is the 
+	 * broadcast address, send the packet to all of the
+	 * usable IDs on the SCSI bus.
+	 */
+	if (dest == SN_DEST_BCAST) {
+		for (i = 0; i < scsi_nets[bus].devices; i++)
+			if (scsi_nets[bus].devs[i].usable) {
+				retval = sn_do_transmit (bus, i, packet, len);
+				if (retval != 0) break;
+			}
+	} else {
+		if (dest > scsi_nets[bus].devices || 
+		    scsi_nets[bus].devs[dest].usable == 0) {
+			printk("sn: sn_tx_packet: passed bad dest. (%d)\n",
+				dest);
+			return -EINVAL;
+		}
+		retval = sn_do_transmit (bus, dest, packet, len);
+	}
+	
+	return retval;
+}
+
+/*
+ * Called by the mid-level SCSI driver when a transmission attempt
+ * has completed.
+ */
+static void sn_tx_done(Scsi_Cmnd *cmnd) {
+	kfree(cmnd);
+}
+
+/*
+ * sn_do_transmit - called by sn_tx_packet to actually do the work
+ *		    of transmitting a packet.  sn_tx_packet handles
+ *		    all necessary error checking and handling, so
+ *		    we are sure that we are passed valid information.
+ */
+static int sn_do_transmit (int bus, int dest, char *packet, unsigned short len)
+{
+	Scsi_Cmnd *SCpnt;
+	unsigned char cmnd[MAX_COMMAND_SIZE];
+	Scsi_Device *SDpnt = scsi_nets[bus].devs[dest].device;
+	int result;
+
+#if 0
+	printk("sn: sn_do_transmit: txing %d bytes to bus %d, id %d\n", 
+		len, bus, dest);
+#endif
+
+	SCpnt = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+	if (SCpnt == NULL) {
+		printk("sn: sn_do_transmit: allocate_device failed!\n");
+		return -ENOMEM;
+	}
+	
+	SCpnt->host = SDpnt->host;
+	SCpnt->device = SDpnt;
+	SCpnt->target = SDpnt->id;
+	SCpnt->lun = SDpnt->lun;
+	SCpnt->channel = SDpnt->channel;
+	SCpnt->use_sg = 0; 
+	SCpnt->network = 1;
+
+	SCpnt->sense_buffer[0] = 0;
+	SCpnt->cmd_len = SCSI_HW_HLEN;
+
+	memset (cmnd, 0, MAX_COMMAND_SIZE);
+	cmnd[0] = 0x2A;
+	cmnd[7] = (len & 0xff00) >> 8;
+	cmnd[8] = len & 0xff;
+
+#if 0
+	printk("Issuing command SCpnt=%p\n", cmnd);
+#endif
+	result = scsi_do_cmd (SCpnt, (void *)cmnd, 
+		(void *)packet, len, sn_tx_done, 0, 0);
+
+	if (result != 0) {
+		kfree(SCpnt);
+		return result;
+	}
+
+	return 0;
+}
+
+/*
+ * sn_recv_msg()
+ *	Called by the low level SCSI driver when it has received
+ *	a valid packet from another host adapter on the SCSI bus.
+ *
+ * 	The function finds our data structure that corresponds
+ *	to the SCSI bus that the message came from and calls
+ *	the sn_rx_packet() network driver function to pass
+ *	the packet into the network stack.
+ */
+void sn_recv_msg (struct Scsi_Host *host, int chan, char *packet, int len)
+{
+	int i, found = 0;
+
+	for (i = 0; i < sn_hosts; i++) 
+		if (scsi_nets[i].host == host &&
+		    scsi_nets[i].channel_id == chan) {
+			found++;
+			if (scsi_nets[i].inuse)
+				sn_rx_packet(scsi_nets[i].net_device, packet, len);
+			else
+				printk("sn_recv_msg: unexpected packet received on bus %d.\n", i);
+			break;
+		}
+
+	if (!found)
+		printk("sn: sn_recv_msg: weirdo message from host %p chan %d\n",
+			host, chan);
+}
diff -u -r -N -x *.o linux/include/linux/if_arp.h linux-scsi/include/linux/if_arp.h
--- linux/include/linux/if_arp.h	Sun Apr 19 16:48:09 1998
+++ linux-scsi/include/linux/if_arp.h	Fri Apr 24 22:33:15 1998
@@ -36,6 +36,7 @@
 #define	ARPHRD_APPLETLK	8		/* APPLEtalk			*/
 #define ARPHRD_DLCI	15		/* Frame Relay DLCI		*/
 #define ARPHRD_METRICOM	23		/* Metricom STRIP (new IANA id)	*/
+#define ARPHRD_SCSI	69		/* SCSI... (real number is?)	*/
 
 /* Dummy types for non ARP hardware */
 #define ARPHRD_SLIP	256
diff -u -r -N -x *.o linux/include/linux/scsinet.h linux-scsi/include/linux/scsinet.h
--- linux/include/linux/scsinet.h	Wed Dec 31 18:00:00 1969
+++ linux-scsi/include/linux/scsinet.h	Fri Apr 24 22:33:15 1998
@@ -0,0 +1,73 @@
+/*
+ *
+ *	scsinet.h -- IP over SCSI implementation
+ *
+ *	Authors:  Chris Frantz (frantzc@bork.com)
+ *		  Randy Scott (scottr@bork.com)
+ *		  Alan Bork (borka@bork.com)
+ *
+ *	Bork Systems Group
+ *	http://www.bork.com
+ *
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef SCSINET_H
+#define SCSINET_H
+
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/sockios.h>
+
+#define SN_DEST_BCAST		255
+#define SN_MAX_DEV		32
+
+struct sn_info {
+	int	scsi_bus_id;
+
+	int 	scsi_dont_use_len;
+	int	scsi_dont_use[SN_MAX_DEV];
+
+	struct net_device_stats	stats;
+};
+
+/* ioctl info */
+#define SIOCSSCSIADD	(SIOCDEVPRIVATE + 1)
+#define SIOCSSCSIDEL	(SIOCDEVPRIVATE + 2)
+
+#define SCSI_HW_HLEN	10
+#define SCSI_HLEN	4
+#define SCSI_ALEN	1
+
+struct scsihdr {
+/*	unsigned char sh_type;	 Type 0x2A for SEND_MESSAGE(10) */
+/*	unsigned char sh_lun;	 LUN 0x00 */
+/*	unsigned char sh_resv1;	 Reserved 0x00 */
+/*	unsigned char sh_resv2;	 Reserved 0x00 */
+/*	unsigned char sh_ssm;	 Stream Select MSB = 0x00 */
+/*	unsigned char sh_ssl;	 Stream Select LSB = 0x00 */
+/*	unsigned char sh_resv3;	 Reserved 0x00 */
+/*	unsigned short sh_len;	 Transfer length (MSB first) */
+/*	unsigned char sh_cntl;	 Control 0x00 */
+
+	unsigned char h_dest[SCSI_ALEN];
+	unsigned char h_source[SCSI_ALEN];
+	unsigned short h_proto;
+};
+
+#endif SCSINET_H

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.rutgers.edu

home help back first fref pref prev next nref lref last post