IP over SCSI

Randy D. Scott (scottr@wwa.com)
Fri, 24 Apr 1998 23:46:54 -0500 (CDT)


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-kernel" in
the body of a message to majordomo@vger.rutgers.edu