Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
new file mode 100644
index 0000000..ef2713b
--- /dev/null
+++ b/drivers/message/fusion/mptlan.c
@@ -0,0 +1,1688 @@
+/*
+ *  linux/drivers/message/fusion/mptlan.c
+ *      IP Over Fibre Channel device driver.
+ *      For use with PCI chip/adapter(s):
+ *          LSIFC9xx/LSI409xx Fibre Channel
+ *      running LSI Logic Fusion MPT (Message Passing Technology) firmware.
+ *
+ *  Credits:
+ *      This driver would not exist if not for Alan Cox's development
+ *      of the linux i2o driver.
+ *
+ *      Special thanks goes to the I2O LAN driver people at the
+ *      University of Helsinki, who, unbeknownst to them, provided
+ *      the inspiration and initial structure for this driver.
+ *
+ *      A huge debt of gratitude is owed to David S. Miller (DaveM)
+ *      for fixing much of the stupid and broken stuff in the early
+ *      driver while porting to sparc64 platform.  THANK YOU!
+ *
+ *      A really huge debt of gratitude is owed to Eddie C. Dost
+ *      for gobs of hard work fixing and optimizing LAN code.
+ *      THANK YOU!
+ *
+ *      (see also mptbase.c)
+ *
+ *  Copyright (c) 2000-2004 LSI Logic Corporation
+ *  Originally By: Noah Romer
+ *  (mailto:mpt_linux_developer@lsil.com)
+ *
+ *  $Id: mptlan.c,v 1.53 2002/10/17 20:15:58 pdelaney Exp $
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; version 2 of the License.
+
+    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.
+
+    NO WARRANTY
+    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+    solely responsible for determining the appropriateness of using and
+    distributing the Program and assumes all risks associated with its
+    exercise of rights under this Agreement, including but not limited to
+    the risks and costs of program errors, damage to or loss of data,
+    programs or equipment, and unavailability or interruption of operations.
+
+    DISCLAIMER OF LIABILITY
+    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Define statements used for debugging
+ */
+//#define MPT_LAN_IO_DEBUG
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include "mptlan.h"
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#define MYNAM		"mptlan"
+
+MODULE_LICENSE("GPL");
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * MPT LAN message sizes without variable part.
+ */
+#define MPT_LAN_RECEIVE_POST_REQUEST_SIZE \
+	(sizeof(LANReceivePostRequest_t) - sizeof(SGE_MPI_UNION))
+
+#define MPT_LAN_TRANSACTION32_SIZE \
+	(sizeof(SGETransaction32_t) - sizeof(u32))
+
+/*
+ *  Fusion MPT LAN private structures
+ */
+
+struct NAA_Hosed {
+	u16 NAA;
+	u8 ieee[FC_ALEN];
+	struct NAA_Hosed *next;
+};
+
+struct BufferControl {
+	struct sk_buff	*skb;
+	dma_addr_t	dma;
+	unsigned int	len;
+};
+
+struct mpt_lan_priv {
+	MPT_ADAPTER *mpt_dev;
+	u8 pnum; /* Port number in the IOC. This is not a Unix network port! */
+
+	atomic_t buckets_out;		/* number of unused buckets on IOC */
+	int bucketthresh;		/* Send more when this many left */
+
+	int *mpt_txfidx; /* Free Tx Context list */
+	int mpt_txfidx_tail;
+	spinlock_t txfidx_lock;
+
+	int *mpt_rxfidx; /* Free Rx Context list */
+	int mpt_rxfidx_tail;
+	spinlock_t rxfidx_lock;
+
+	struct BufferControl *RcvCtl;	/* Receive BufferControl structs */
+	struct BufferControl *SendCtl;	/* Send BufferControl structs */
+
+	int max_buckets_out;		/* Max buckets to send to IOC */
+	int tx_max_out;			/* IOC's Tx queue len */
+
+	u32 total_posted;
+	u32 total_received;
+	struct net_device_stats stats;	/* Per device statistics */
+
+	struct work_struct post_buckets_task;
+	unsigned long post_buckets_active;
+};
+
+struct mpt_lan_ohdr {
+	u16	dtype;
+	u8	daddr[FC_ALEN];
+	u16	stype;
+	u8	saddr[FC_ALEN];
+};
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/*
+ *  Forward protos...
+ */
+static int  lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf,
+		       MPT_FRAME_HDR *reply);
+static int  mpt_lan_open(struct net_device *dev);
+static int  mpt_lan_reset(struct net_device *dev);
+static int  mpt_lan_close(struct net_device *dev);
+static void mpt_lan_post_receive_buckets(void *dev_id);
+static void mpt_lan_wake_post_buckets_task(struct net_device *dev,
+					   int priority);
+static int  mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg);
+static int  mpt_lan_receive_post_reply(struct net_device *dev,
+				       LANReceivePostReply_t *pRecvRep);
+static int  mpt_lan_send_turbo(struct net_device *dev, u32 tmsg);
+static int  mpt_lan_send_reply(struct net_device *dev,
+			       LANSendReply_t *pSendRep);
+static int  mpt_lan_ioc_reset(MPT_ADAPTER *ioc, int reset_phase);
+static int  mpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
+static unsigned short mpt_lan_type_trans(struct sk_buff *skb,
+					 struct net_device *dev);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *  Fusion MPT LAN private data
+ */
+static int LanCtx = -1;
+
+static u32 max_buckets_out = 127;
+static u32 tx_max_out_p = 127 - 16;
+
+#ifdef QLOGIC_NAA_WORKAROUND
+static struct NAA_Hosed *mpt_bad_naa = NULL;
+DEFINE_RWLOCK(bad_naa_lock);
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Fusion MPT LAN external data
+ */
+extern int mpt_lan_index;
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	lan_reply - Handle all data sent from the hardware.
+ *	@ioc: Pointer to MPT_ADAPTER structure
+ *	@mf: Pointer to original MPT request frame (NULL if TurboReply)
+ *	@reply: Pointer to MPT reply frame
+ *
+ *	Returns 1 indicating original alloc'd request frame ptr
+ *	should be freed, or 0 if it shouldn't.
+ */
+static int
+lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
+{
+	struct net_device *dev = ioc->netdev;
+	int FreeReqFrame = 0;
+
+	dioprintk((KERN_INFO MYNAM ": %s/%s: Got reply.\n",
+		  IOC_AND_NETDEV_NAMES_s_s(dev)));
+
+//	dioprintk((KERN_INFO MYNAM "@lan_reply: mf = %p, reply = %p\n",
+//			mf, reply));
+
+	if (mf == NULL) {
+		u32 tmsg = CAST_PTR_TO_U32(reply);
+
+		dioprintk((KERN_INFO MYNAM ": %s/%s: @lan_reply, tmsg %08x\n",
+				IOC_AND_NETDEV_NAMES_s_s(dev),
+				tmsg));
+
+		switch (GET_LAN_FORM(tmsg)) {
+
+		// NOTE!  (Optimization) First case here is now caught in
+		//  mptbase.c::mpt_interrupt() routine and callcack here
+		//  is now skipped for this case!  20001218 -sralston
+#if 0
+		case LAN_REPLY_FORM_MESSAGE_CONTEXT:
+//			dioprintk((KERN_INFO MYNAM "/lan_reply: "
+//				  "MessageContext turbo reply received\n"));
+			FreeReqFrame = 1;
+			break;
+#endif
+
+		case LAN_REPLY_FORM_SEND_SINGLE:
+//			dioprintk((MYNAM "/lan_reply: "
+//				  "calling mpt_lan_send_reply (turbo)\n"));
+
+			// Potential BUG here?  -sralston
+			//	FreeReqFrame = mpt_lan_send_turbo(dev, tmsg);
+			//  If/when mpt_lan_send_turbo would return 1 here,
+			//  calling routine (mptbase.c|mpt_interrupt)
+			//  would Oops because mf has already been set
+			//  to NULL.  So after return from this func,
+			//  mpt_interrupt() will attempt to put (NULL) mf ptr
+			//  item back onto its adapter FreeQ - Oops!:-(
+			//  It's Ok, since mpt_lan_send_turbo() *currently*
+			//  always returns 0, but..., just in case:
+
+			(void) mpt_lan_send_turbo(dev, tmsg);
+			FreeReqFrame = 0;
+
+			break;
+
+		case LAN_REPLY_FORM_RECEIVE_SINGLE:
+//			dioprintk((KERN_INFO MYNAM "@lan_reply: "
+//				  "rcv-Turbo = %08x\n", tmsg));
+			mpt_lan_receive_post_turbo(dev, tmsg);
+			break;
+
+		default:
+			printk (KERN_ERR MYNAM "/lan_reply: Got a turbo reply "
+				"that I don't know what to do with\n");
+
+			/* CHECKME!  Hmmm...  FreeReqFrame is 0 here; is that right? */
+
+			break;
+		}
+
+		return FreeReqFrame;
+	}
+
+//	msg = (u32 *) reply;
+//	dioprintk((KERN_INFO MYNAM "@lan_reply: msg = %08x %08x %08x %08x\n",
+//		  le32_to_cpu(msg[0]), le32_to_cpu(msg[1]),
+//		  le32_to_cpu(msg[2]), le32_to_cpu(msg[3])));
+//	dioprintk((KERN_INFO MYNAM "@lan_reply: Function = %02xh\n",
+//		  reply->u.hdr.Function));
+
+	switch (reply->u.hdr.Function) {
+
+	case MPI_FUNCTION_LAN_SEND:
+	{
+		LANSendReply_t *pSendRep;
+
+		pSendRep = (LANSendReply_t *) reply;
+		FreeReqFrame = mpt_lan_send_reply(dev, pSendRep);
+		break;
+	}
+
+	case MPI_FUNCTION_LAN_RECEIVE:
+	{
+		LANReceivePostReply_t *pRecvRep;
+
+		pRecvRep = (LANReceivePostReply_t *) reply;
+		if (pRecvRep->NumberOfContexts) {
+			mpt_lan_receive_post_reply(dev, pRecvRep);
+			if (!(pRecvRep->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY))
+				FreeReqFrame = 1;
+		} else
+			dioprintk((KERN_INFO MYNAM "@lan_reply: zero context "
+				  "ReceivePostReply received.\n"));
+		break;
+	}
+
+	case MPI_FUNCTION_LAN_RESET:
+		/* Just a default reply. Might want to check it to
+		 * make sure that everything went ok.
+		 */
+		FreeReqFrame = 1;
+		break;
+
+	case MPI_FUNCTION_EVENT_NOTIFICATION:
+	case MPI_FUNCTION_EVENT_ACK:
+		/* UPDATE!  20010120 -sralston
+		 *  _EVENT_NOTIFICATION should NOT come down this path any more.
+		 *  Should be routed to mpt_lan_event_process(), but just in case...
+		 */
+		FreeReqFrame = 1;
+		break;
+
+	default:
+		printk (KERN_ERR MYNAM "/lan_reply: Got a non-turbo "
+			"reply that I don't know what to do with\n");
+
+		/* CHECKME!  Hmmm...  FreeReqFrame is 0 here; is that right? */
+		FreeReqFrame = 1;
+
+		break;
+	}
+
+	return FreeReqFrame;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
+{
+	struct net_device *dev = ioc->netdev;
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+
+	dlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n",
+			reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
+			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
+
+	if (priv->mpt_rxfidx == NULL)
+		return (1);
+
+	if (reset_phase == MPT_IOC_SETUP_RESET) {
+		;
+	} else if (reset_phase == MPT_IOC_PRE_RESET) {
+		int i;
+		unsigned long flags;
+
+		netif_stop_queue(dev);
+
+		dlprintk ((KERN_INFO "mptlan/ioc_reset: called netif_stop_queue for %s.\n", dev->name));
+
+		atomic_set(&priv->buckets_out, 0);
+
+		/* Reset Rx Free Tail index and re-populate the queue. */
+		spin_lock_irqsave(&priv->rxfidx_lock, flags);
+		priv->mpt_rxfidx_tail = -1;
+		for (i = 0; i < priv->max_buckets_out; i++)
+			priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = i;
+		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+	} else {
+		mpt_lan_post_receive_buckets(dev);
+		netif_wake_queue(dev);
+	}
+
+	return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
+{
+	dlprintk((KERN_INFO MYNAM ": MPT event routed to LAN driver!\n"));
+
+	switch (le32_to_cpu(pEvReply->Event)) {
+	case MPI_EVENT_NONE:				/* 00 */
+	case MPI_EVENT_LOG_DATA:			/* 01 */
+	case MPI_EVENT_STATE_CHANGE:			/* 02 */
+	case MPI_EVENT_UNIT_ATTENTION:			/* 03 */
+	case MPI_EVENT_IOC_BUS_RESET:			/* 04 */
+	case MPI_EVENT_EXT_BUS_RESET:			/* 05 */
+	case MPI_EVENT_RESCAN:				/* 06 */
+		/* Ok, do we need to do anything here? As far as
+		   I can tell, this is when a new device gets added
+		   to the loop. */
+	case MPI_EVENT_LINK_STATUS_CHANGE:		/* 07 */
+	case MPI_EVENT_LOOP_STATE_CHANGE:		/* 08 */
+	case MPI_EVENT_LOGOUT:				/* 09 */
+	case MPI_EVENT_EVENT_CHANGE:			/* 0A */
+	default:
+		break;
+	}
+
+	/*
+	 *  NOTE: pEvent->AckRequired handling now done in mptbase.c;
+	 *  Do NOT do it here now!
+	 */
+
+	return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_open(struct net_device *dev)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+	int i;
+
+	if (mpt_lan_reset(dev) != 0) {
+		MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+
+		printk (KERN_WARNING MYNAM "/lan_open: lan_reset failed.");
+
+		if (mpt_dev->active)
+			printk ("The ioc is active. Perhaps it needs to be"
+				" reset?\n");
+		else
+			printk ("The ioc in inactive, most likely in the "
+				"process of being reset. Please try again in "
+				"a moment.\n");
+	}
+
+	priv->mpt_txfidx = kmalloc(priv->tx_max_out * sizeof(int), GFP_KERNEL);
+	if (priv->mpt_txfidx == NULL)
+		goto out;
+	priv->mpt_txfidx_tail = -1;
+
+	priv->SendCtl = kmalloc(priv->tx_max_out * sizeof(struct BufferControl),
+				GFP_KERNEL);
+	if (priv->SendCtl == NULL)
+		goto out_mpt_txfidx;
+	for (i = 0; i < priv->tx_max_out; i++) {
+		memset(&priv->SendCtl[i], 0, sizeof(struct BufferControl));
+		priv->mpt_txfidx[++priv->mpt_txfidx_tail] = i;
+	}
+
+	dlprintk((KERN_INFO MYNAM "@lo: Finished initializing SendCtl\n"));
+
+	priv->mpt_rxfidx = kmalloc(priv->max_buckets_out * sizeof(int),
+				   GFP_KERNEL);
+	if (priv->mpt_rxfidx == NULL)
+		goto out_SendCtl;
+	priv->mpt_rxfidx_tail = -1;
+
+	priv->RcvCtl = kmalloc(priv->max_buckets_out *
+						sizeof(struct BufferControl),
+			       GFP_KERNEL);
+	if (priv->RcvCtl == NULL)
+		goto out_mpt_rxfidx;
+	for (i = 0; i < priv->max_buckets_out; i++) {
+		memset(&priv->RcvCtl[i], 0, sizeof(struct BufferControl));
+		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = i;
+	}
+
+/**/	dlprintk((KERN_INFO MYNAM "/lo: txfidx contains - "));
+/**/	for (i = 0; i < priv->tx_max_out; i++)
+/**/		dlprintk((" %xh", priv->mpt_txfidx[i]));
+/**/	dlprintk(("\n"));
+
+	dlprintk((KERN_INFO MYNAM "/lo: Finished initializing RcvCtl\n"));
+
+	mpt_lan_post_receive_buckets(dev);
+	printk(KERN_INFO MYNAM ": %s/%s: interface up & active\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev));
+
+	if (mpt_event_register(LanCtx, mpt_lan_event_process) != 0) {
+		printk (KERN_WARNING MYNAM "/lo: Unable to register for Event"
+			" Notifications. This is a bad thing! We're not going "
+			"to go ahead, but I'd be leery of system stability at "
+			"this point.\n");
+	}
+
+	netif_start_queue(dev);
+	dlprintk((KERN_INFO MYNAM "/lo: Done.\n"));
+
+	return 0;
+out_mpt_rxfidx:
+	kfree(priv->mpt_rxfidx);
+	priv->mpt_rxfidx = NULL;
+out_SendCtl:
+	kfree(priv->SendCtl);
+	priv->SendCtl = NULL;
+out_mpt_txfidx:
+	kfree(priv->mpt_txfidx);
+	priv->mpt_txfidx = NULL;
+out:	return -ENOMEM;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Send a LanReset message to the FW. This should result in the FW returning
+   any buckets it still has. */
+static int
+mpt_lan_reset(struct net_device *dev)
+{
+	MPT_FRAME_HDR *mf;
+	LANResetRequest_t *pResetReq;
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+
+	mf = mpt_get_msg_frame(LanCtx, priv->mpt_dev);
+
+	if (mf == NULL) {
+/*		dlprintk((KERN_ERR MYNAM "/reset: Evil funkiness abounds! "
+		"Unable to allocate a request frame.\n"));
+*/
+		return -1;
+	}
+
+	pResetReq = (LANResetRequest_t *) mf;
+
+	pResetReq->Function	= MPI_FUNCTION_LAN_RESET;
+	pResetReq->ChainOffset	= 0;
+	pResetReq->Reserved	= 0;
+	pResetReq->PortNumber	= priv->pnum;
+	pResetReq->MsgFlags	= 0;
+	pResetReq->Reserved2	= 0;
+
+	mpt_put_msg_frame(LanCtx, priv->mpt_dev, mf);
+
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_close(struct net_device *dev)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	unsigned int timeout;
+	int i;
+
+	dlprintk((KERN_INFO MYNAM ": mpt_lan_close called\n"));
+
+	mpt_event_deregister(LanCtx);
+
+	dlprintk((KERN_INFO MYNAM ":lan_close: Posted %d buckets "
+		  "since driver was loaded, %d still out\n",
+		  priv->total_posted,atomic_read(&priv->buckets_out)));
+
+	netif_stop_queue(dev);
+
+	mpt_lan_reset(dev);
+
+	timeout = 2 * HZ;
+	while (atomic_read(&priv->buckets_out) && --timeout) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	for (i = 0; i < priv->max_buckets_out; i++) {
+		if (priv->RcvCtl[i].skb != NULL) {
+/**/			dlprintk((KERN_INFO MYNAM "/lan_close: bucket %05x "
+/**/				  "is still out\n", i));
+			pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[i].dma,
+					 priv->RcvCtl[i].len,
+					 PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(priv->RcvCtl[i].skb);
+		}
+	}
+
+	kfree (priv->RcvCtl);
+	kfree (priv->mpt_rxfidx);
+
+	for (i = 0; i < priv->tx_max_out; i++) {
+		if (priv->SendCtl[i].skb != NULL) {
+			pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[i].dma,
+					 priv->SendCtl[i].len,
+					 PCI_DMA_TODEVICE);
+			dev_kfree_skb(priv->SendCtl[i].skb);
+		}
+	}
+
+	kfree(priv->SendCtl);
+	kfree(priv->mpt_txfidx);
+
+	atomic_set(&priv->buckets_out, 0);
+
+	printk(KERN_INFO MYNAM ": %s/%s: interface down & inactive\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev));
+
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static struct net_device_stats *
+mpt_lan_get_stats(struct net_device *dev)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+
+	return (struct net_device_stats *) &priv->stats;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < MPT_LAN_MIN_MTU) || (new_mtu > MPT_LAN_MAX_MTU))
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Tx timeout handler. */
+static void
+mpt_lan_tx_timeout(struct net_device *dev)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+
+	if (mpt_dev->active) {
+		dlprintk (("mptlan/tx_timeout: calling netif_wake_queue for %s.\n", dev->name));
+		netif_wake_queue(dev);
+	}
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+//static inline int
+static int
+mpt_lan_send_turbo(struct net_device *dev, u32 tmsg)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	struct sk_buff *sent;
+	unsigned long flags;
+	u32 ctx;
+
+	ctx = GET_LAN_BUFFER_CONTEXT(tmsg);
+	sent = priv->SendCtl[ctx].skb;
+
+	priv->stats.tx_packets++;
+	priv->stats.tx_bytes += sent->len;
+
+	dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev),
+			__FUNCTION__, sent));
+
+	priv->SendCtl[ctx].skb = NULL;
+	pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma,
+			 priv->SendCtl[ctx].len, PCI_DMA_TODEVICE);
+	dev_kfree_skb_irq(sent);
+
+	spin_lock_irqsave(&priv->txfidx_lock, flags);
+	priv->mpt_txfidx[++priv->mpt_txfidx_tail] = ctx;
+	spin_unlock_irqrestore(&priv->txfidx_lock, flags);
+
+	netif_wake_queue(dev);
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	struct sk_buff *sent;
+	unsigned long flags;
+	int FreeReqFrame = 0;
+	u32 *pContext;
+	u32 ctx;
+	u8 count;
+
+	count = pSendRep->NumberOfContexts;
+
+	dioprintk((KERN_INFO MYNAM ": send_reply: IOCStatus: %04x\n",
+		 le16_to_cpu(pSendRep->IOCStatus)));
+
+	/* Add check for Loginfo Flag in IOCStatus */
+
+	switch (le16_to_cpu(pSendRep->IOCStatus) & MPI_IOCSTATUS_MASK) {
+	case MPI_IOCSTATUS_SUCCESS:
+		priv->stats.tx_packets += count;
+		break;
+
+	case MPI_IOCSTATUS_LAN_CANCELED:
+	case MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED:
+		break;
+
+	case MPI_IOCSTATUS_INVALID_SGL:
+		priv->stats.tx_errors += count;
+		printk (KERN_ERR MYNAM ": %s/%s: ERROR - Invalid SGL sent to IOC!\n",
+				IOC_AND_NETDEV_NAMES_s_s(dev));
+		goto out;
+
+	default:
+		priv->stats.tx_errors += count;
+		break;
+	}
+
+	pContext = &pSendRep->BufferContext;
+
+	spin_lock_irqsave(&priv->txfidx_lock, flags);
+	while (count > 0) {
+		ctx = GET_LAN_BUFFER_CONTEXT(le32_to_cpu(*pContext));
+
+		sent = priv->SendCtl[ctx].skb;
+		priv->stats.tx_bytes += sent->len;
+
+		dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n",
+				IOC_AND_NETDEV_NAMES_s_s(dev),
+				__FUNCTION__, sent));
+
+		priv->SendCtl[ctx].skb = NULL;
+		pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma,
+				 priv->SendCtl[ctx].len, PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq(sent);
+
+		priv->mpt_txfidx[++priv->mpt_txfidx_tail] = ctx;
+
+		pContext++;
+		count--;
+	}
+	spin_unlock_irqrestore(&priv->txfidx_lock, flags);
+
+out:
+	if (!(pSendRep->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY))
+		FreeReqFrame = 1;
+
+	netif_wake_queue(dev);
+	return FreeReqFrame;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
+{
+	struct mpt_lan_priv *priv = netdev_priv(dev);
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	MPT_FRAME_HDR *mf;
+	LANSendRequest_t *pSendReq;
+	SGETransaction32_t *pTrans;
+	SGESimple64_t *pSimple;
+	dma_addr_t dma;
+	unsigned long flags;
+	int ctx;
+	u16 cur_naa = 0x1000;
+
+	dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n",
+			__FUNCTION__, skb));
+
+	spin_lock_irqsave(&priv->txfidx_lock, flags);
+	if (priv->mpt_txfidx_tail < 0) {
+		netif_stop_queue(dev);
+		spin_unlock_irqrestore(&priv->txfidx_lock, flags);
+
+		printk (KERN_ERR "%s: no tx context available: %u\n",
+			__FUNCTION__, priv->mpt_txfidx_tail);
+		return 1;
+	}
+
+	mf = mpt_get_msg_frame(LanCtx, mpt_dev);
+	if (mf == NULL) {
+		netif_stop_queue(dev);
+		spin_unlock_irqrestore(&priv->txfidx_lock, flags);
+
+		printk (KERN_ERR "%s: Unable to alloc request frame\n",
+			__FUNCTION__);
+		return 1;
+	}
+
+	ctx = priv->mpt_txfidx[priv->mpt_txfidx_tail--];
+	spin_unlock_irqrestore(&priv->txfidx_lock, flags);
+
+//	dioprintk((KERN_INFO MYNAM ": %s/%s: Creating new msg frame (send).\n",
+//			IOC_AND_NETDEV_NAMES_s_s(dev)));
+
+	pSendReq = (LANSendRequest_t *) mf;
+
+	/* Set the mac.raw pointer, since this apparently isn't getting
+	 * done before we get the skb. Pull the data pointer past the mac data.
+	 */
+	skb->mac.raw = skb->data;
+	skb_pull(skb, 12);
+
+        dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len,
+			     PCI_DMA_TODEVICE);
+
+	priv->SendCtl[ctx].skb = skb;
+	priv->SendCtl[ctx].dma = dma;
+	priv->SendCtl[ctx].len = skb->len;
+
+	/* Message Header */
+	pSendReq->Reserved    = 0;
+	pSendReq->Function    = MPI_FUNCTION_LAN_SEND;
+	pSendReq->ChainOffset = 0;
+	pSendReq->Reserved2   = 0;
+	pSendReq->MsgFlags    = 0;
+	pSendReq->PortNumber  = priv->pnum;
+
+	/* Transaction Context Element */
+	pTrans = (SGETransaction32_t *) pSendReq->SG_List;
+
+	/* No Flags, 8 bytes of Details, 32bit Context (bloody turbo replies) */
+	pTrans->ContextSize   = sizeof(u32);
+	pTrans->DetailsLength = 2 * sizeof(u32);
+	pTrans->Flags         = 0;
+	pTrans->TransactionContext[0] = cpu_to_le32(ctx);
+
+//	dioprintk((KERN_INFO MYNAM ": %s/%s: BC = %08x, skb = %p, buff = %p\n",
+//			IOC_AND_NETDEV_NAMES_s_s(dev),
+//			ctx, skb, skb->data));
+
+#ifdef QLOGIC_NAA_WORKAROUND
+{
+	struct NAA_Hosed *nh;
+
+	/* Munge the NAA for Tx packets to QLogic boards, which don't follow
+	   RFC 2625. The longer I look at this, the more my opinion of Qlogic
+	   drops. */
+	read_lock_irq(&bad_naa_lock);
+	for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) {
+		if ((nh->ieee[0] == skb->mac.raw[0]) &&
+		    (nh->ieee[1] == skb->mac.raw[1]) &&
+		    (nh->ieee[2] == skb->mac.raw[2]) &&
+		    (nh->ieee[3] == skb->mac.raw[3]) &&
+		    (nh->ieee[4] == skb->mac.raw[4]) &&
+		    (nh->ieee[5] == skb->mac.raw[5])) {
+			cur_naa = nh->NAA;
+			dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value "
+				  "= %04x.\n", cur_naa));
+			break;
+		}
+	}
+	read_unlock_irq(&bad_naa_lock);
+}
+#endif
+
+	pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa         << 16) |
+						    (skb->mac.raw[0] <<  8) |
+						    (skb->mac.raw[1] <<  0));
+	pTrans->TransactionDetails[1] = cpu_to_le32((skb->mac.raw[2] << 24) |
+						    (skb->mac.raw[3] << 16) |
+						    (skb->mac.raw[4] <<  8) |
+						    (skb->mac.raw[5] <<  0));
+
+	pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2];
+
+	/* If we ever decide to send more than one Simple SGE per LANSend, then
+	   we will need to make sure that LAST_ELEMENT only gets set on the
+	   last one. Otherwise, bad voodoo and evil funkiness will commence. */
+	pSimple->FlagsLength = cpu_to_le32(
+			((MPI_SGE_FLAGS_LAST_ELEMENT |
+			  MPI_SGE_FLAGS_END_OF_BUFFER |
+			  MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+			  MPI_SGE_FLAGS_SYSTEM_ADDRESS |
+			  MPI_SGE_FLAGS_HOST_TO_IOC |
+			  MPI_SGE_FLAGS_64_BIT_ADDRESSING |
+			  MPI_SGE_FLAGS_END_OF_LIST) << MPI_SGE_FLAGS_SHIFT) |
+			skb->len);
+	pSimple->Address.Low = cpu_to_le32((u32) dma);
+	if (sizeof(dma_addr_t) > sizeof(u32))
+		pSimple->Address.High = cpu_to_le32((u32) ((u64) dma >> 32));
+	else
+		pSimple->Address.High = 0;
+
+	mpt_put_msg_frame (LanCtx, mpt_dev, mf);
+	dev->trans_start = jiffies;
+
+	dioprintk((KERN_INFO MYNAM ": %s/%s: Sending packet. FlagsLength = %08x.\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev),
+			le32_to_cpu(pSimple->FlagsLength)));
+
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static inline void
+mpt_lan_wake_post_buckets_task(struct net_device *dev, int priority)
+/*
+ * @priority: 0 = put it on the timer queue, 1 = put it on the immediate queue
+ */
+{
+	struct mpt_lan_priv *priv = dev->priv;
+	
+	if (test_and_set_bit(0, &priv->post_buckets_active) == 0) {
+		if (priority) {
+			schedule_work(&priv->post_buckets_task);
+		} else {
+			schedule_delayed_work(&priv->post_buckets_task, 1);
+			dioprintk((KERN_INFO MYNAM ": post_buckets queued on "
+				   "timer.\n"));
+		}
+	        dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n",
+			   IOC_AND_NETDEV_NAMES_s_s(dev) ));
+	}
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static inline int
+mpt_lan_receive_skb(struct net_device *dev, struct sk_buff *skb)
+{
+	struct mpt_lan_priv *priv = dev->priv;
+
+	skb->protocol = mpt_lan_type_trans(skb, dev);
+
+	dioprintk((KERN_INFO MYNAM ": %s/%s: Incoming packet (%d bytes) "
+		 "delivered to upper level.\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev), skb->len));
+
+	priv->stats.rx_bytes += skb->len;
+	priv->stats.rx_packets++;
+
+	skb->dev = dev;
+	netif_rx(skb);
+
+	dioprintk((MYNAM "/receive_skb: %d buckets remaining\n",
+		 atomic_read(&priv->buckets_out)));
+
+	if (atomic_read(&priv->buckets_out) < priv->bucketthresh)
+		mpt_lan_wake_post_buckets_task(dev, 1);
+
+	dioprintk((KERN_INFO MYNAM "/receive_post_reply: %d buckets "
+		  "remaining, %d received back since sod\n",
+		  atomic_read(&priv->buckets_out), priv->total_received));
+
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+//static inline int
+static int
+mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg)
+{
+	struct mpt_lan_priv *priv = dev->priv;
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	struct sk_buff *skb, *old_skb;
+	unsigned long flags;
+	u32 ctx, len;
+
+	ctx = GET_LAN_BUCKET_CONTEXT(tmsg);
+	skb = priv->RcvCtl[ctx].skb;
+
+	len = GET_LAN_PACKET_LENGTH(tmsg);
+
+	if (len < MPT_LAN_RX_COPYBREAK) {
+		old_skb = skb;
+
+		skb = (struct sk_buff *)dev_alloc_skb(len);
+		if (!skb) {
+			printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
+					IOC_AND_NETDEV_NAMES_s_s(dev),
+					__FILE__, __LINE__);
+			return -ENOMEM;
+		}
+
+		pci_dma_sync_single_for_cpu(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
+					    priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
+
+		memcpy(skb_put(skb, len), old_skb->data, len);
+
+		pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
+					       priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
+		goto out;
+	}
+
+	skb_put(skb, len);
+
+	priv->RcvCtl[ctx].skb = NULL;
+
+	pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
+			 priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
+
+out:
+	spin_lock_irqsave(&priv->rxfidx_lock, flags);
+	priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
+	spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+
+	atomic_dec(&priv->buckets_out);
+	priv->total_received++;
+
+	return mpt_lan_receive_skb(dev, skb);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_receive_post_free(struct net_device *dev,
+			  LANReceivePostReply_t *pRecvRep)
+{
+	struct mpt_lan_priv *priv = dev->priv;
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	unsigned long flags;
+	struct sk_buff *skb;
+	u32 ctx;
+	int count;
+	int i;
+
+	count = pRecvRep->NumberOfContexts;
+
+/**/	dlprintk((KERN_INFO MYNAM "/receive_post_reply: "
+		  "IOC returned %d buckets, freeing them...\n", count));
+
+	spin_lock_irqsave(&priv->rxfidx_lock, flags);
+	for (i = 0; i < count; i++) {
+		ctx = le32_to_cpu(pRecvRep->BucketContext[i]);
+
+		skb = priv->RcvCtl[ctx].skb;
+
+//		dlprintk((KERN_INFO MYNAM ": %s: dev_name = %s\n",
+//				IOC_AND_NETDEV_NAMES_s_s(dev)));
+//		dlprintk((KERN_INFO MYNAM "@rpr[2], priv = %p, buckets_out addr = %p",
+//				priv, &(priv->buckets_out)));
+//		dlprintk((KERN_INFO MYNAM "@rpr[2] TC + 3\n"));
+
+		priv->RcvCtl[ctx].skb = NULL;
+		pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
+				 priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
+		dev_kfree_skb_any(skb);
+
+		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
+	}
+	spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+
+	atomic_sub(count, &priv->buckets_out);
+
+//	for (i = 0; i < priv->max_buckets_out; i++)
+//		if (priv->RcvCtl[i].skb != NULL)
+//			dlprintk((KERN_INFO MYNAM "@rpr: bucket %03x "
+//				  "is still out\n", i));
+
+/*	dlprintk((KERN_INFO MYNAM "/receive_post_reply: freed %d buckets\n",
+		  count));
+*/
+/**/	dlprintk((KERN_INFO MYNAM "@receive_post_reply: %d buckets "
+/**/		  "remaining, %d received back since sod.\n",
+/**/		  atomic_read(&priv->buckets_out), priv->total_received));
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_lan_receive_post_reply(struct net_device *dev,
+			   LANReceivePostReply_t *pRecvRep)
+{
+	struct mpt_lan_priv *priv = dev->priv;
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	struct sk_buff *skb, *old_skb;
+	unsigned long flags;
+	u32 len, ctx, offset;
+	u32 remaining = le32_to_cpu(pRecvRep->BucketsRemaining);
+	int count;
+	int i, l;
+
+	dioprintk((KERN_INFO MYNAM ": mpt_lan_receive_post_reply called\n"));
+	dioprintk((KERN_INFO MYNAM ": receive_post_reply: IOCStatus: %04x\n",
+		 le16_to_cpu(pRecvRep->IOCStatus)));
+
+	if ((le16_to_cpu(pRecvRep->IOCStatus) & MPI_IOCSTATUS_MASK) ==
+						MPI_IOCSTATUS_LAN_CANCELED)
+		return mpt_lan_receive_post_free(dev, pRecvRep);
+
+	len = le32_to_cpu(pRecvRep->PacketLength);
+	if (len == 0) {
+		printk (KERN_ERR MYNAM ": %s/%s: ERROR - Got a non-TURBO "
+			"ReceivePostReply w/ PacketLength zero!\n",
+				IOC_AND_NETDEV_NAMES_s_s(dev));
+		printk (KERN_ERR MYNAM ": MsgFlags = %02x, IOCStatus = %04x\n",
+				pRecvRep->MsgFlags, le16_to_cpu(pRecvRep->IOCStatus));
+		return -1;
+	}
+
+	ctx    = le32_to_cpu(pRecvRep->BucketContext[0]);
+	count  = pRecvRep->NumberOfContexts;
+	skb    = priv->RcvCtl[ctx].skb;
+
+	offset = le32_to_cpu(pRecvRep->PacketOffset);
+//	if (offset != 0) {
+//		printk (KERN_INFO MYNAM ": %s/%s: Got a ReceivePostReply "
+//			"w/ PacketOffset %u\n",
+//				IOC_AND_NETDEV_NAMES_s_s(dev),
+//				offset);
+//	}
+
+	dioprintk((KERN_INFO MYNAM ": %s/%s: @rpr, offset = %d, len = %d\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev),
+			offset, len));
+
+	if (count > 1) {
+		int szrem = len;
+
+//		dioprintk((KERN_INFO MYNAM ": %s/%s: Multiple buckets returned "
+//			"for single packet, concatenating...\n",
+//				IOC_AND_NETDEV_NAMES_s_s(dev)));
+
+		skb = (struct sk_buff *)dev_alloc_skb(len);
+		if (!skb) {
+			printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
+					IOC_AND_NETDEV_NAMES_s_s(dev),
+					__FILE__, __LINE__);
+			return -ENOMEM;
+		}
+
+		spin_lock_irqsave(&priv->rxfidx_lock, flags);
+		for (i = 0; i < count; i++) {
+
+			ctx = le32_to_cpu(pRecvRep->BucketContext[i]);
+			old_skb = priv->RcvCtl[ctx].skb;
+
+			l = priv->RcvCtl[ctx].len;
+			if (szrem < l)
+				l = szrem;
+
+//			dioprintk((KERN_INFO MYNAM ": %s/%s: Buckets = %d, len = %u\n",
+//					IOC_AND_NETDEV_NAMES_s_s(dev),
+//					i, l));
+
+			pci_dma_sync_single_for_cpu(mpt_dev->pcidev,
+						    priv->RcvCtl[ctx].dma,
+						    priv->RcvCtl[ctx].len,
+						    PCI_DMA_FROMDEVICE);
+			memcpy(skb_put(skb, l), old_skb->data, l);
+
+			pci_dma_sync_single_for_device(mpt_dev->pcidev,
+						       priv->RcvCtl[ctx].dma,
+						       priv->RcvCtl[ctx].len,
+						       PCI_DMA_FROMDEVICE);
+
+			priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
+			szrem -= l;
+		}
+		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+
+	} else if (len < MPT_LAN_RX_COPYBREAK) {
+
+		old_skb = skb;
+
+		skb = (struct sk_buff *)dev_alloc_skb(len);
+		if (!skb) {
+			printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
+					IOC_AND_NETDEV_NAMES_s_s(dev),
+					__FILE__, __LINE__);
+			return -ENOMEM;
+		}
+
+		pci_dma_sync_single_for_cpu(mpt_dev->pcidev,
+					    priv->RcvCtl[ctx].dma,
+					    priv->RcvCtl[ctx].len,
+					    PCI_DMA_FROMDEVICE);
+
+		memcpy(skb_put(skb, len), old_skb->data, len);
+
+		pci_dma_sync_single_for_device(mpt_dev->pcidev,
+					       priv->RcvCtl[ctx].dma,
+					       priv->RcvCtl[ctx].len,
+					       PCI_DMA_FROMDEVICE);
+
+		spin_lock_irqsave(&priv->rxfidx_lock, flags);
+		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
+		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+
+	} else {
+		spin_lock_irqsave(&priv->rxfidx_lock, flags);
+
+		priv->RcvCtl[ctx].skb = NULL;
+
+		pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
+				 priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE);
+		priv->RcvCtl[ctx].dma = 0;
+
+		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
+		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+
+		skb_put(skb,len);
+	}
+
+	atomic_sub(count, &priv->buckets_out);
+	priv->total_received += count;
+
+	if (priv->mpt_rxfidx_tail >= MPT_LAN_MAX_BUCKETS_OUT) {
+		printk (KERN_ERR MYNAM ": %s/%s: Yoohoo! mpt_rxfidx_tail = %d, "
+			"MPT_LAN_MAX_BUCKETS_OUT = %d\n",
+				IOC_AND_NETDEV_NAMES_s_s(dev),
+				priv->mpt_rxfidx_tail,
+				MPT_LAN_MAX_BUCKETS_OUT);
+
+		panic("Damn it Jim! I'm a doctor, not a programmer! "
+				"Oh, wait a sec, I am a programmer. "
+				"And, who's Jim?!?!\n"
+				"Arrgghh! We've done it again!\n");
+	}
+
+	if (remaining == 0)
+		printk (KERN_WARNING MYNAM ": %s/%s: WARNING - IOC out of buckets! "
+			"(priv->buckets_out = %d)\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev),
+			atomic_read(&priv->buckets_out));
+	else if (remaining < 10)
+		printk (KERN_INFO MYNAM ": %s/%s: IOC says %d buckets left. "
+			"(priv->buckets_out = %d)\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev),
+			remaining, atomic_read(&priv->buckets_out));
+	
+	if ((remaining < priv->bucketthresh) &&
+	    ((atomic_read(&priv->buckets_out) - remaining) >
+	     MPT_LAN_BUCKETS_REMAIN_MISMATCH_THRESH)) {
+		
+		printk (KERN_WARNING MYNAM " Mismatch between driver's "
+			"buckets_out count and fw's BucketsRemaining "
+			"count has crossed the threshold, issuing a "
+			"LanReset to clear the fw's hashtable. You may "
+			"want to check your /var/log/messages for \"CRC "
+			"error\" event notifications.\n");
+		
+		mpt_lan_reset(dev);
+		mpt_lan_wake_post_buckets_task(dev, 0);
+	}
+	
+	return mpt_lan_receive_skb(dev, skb);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Simple SGE's only at the moment */
+
+static void
+mpt_lan_post_receive_buckets(void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mpt_lan_priv *priv = dev->priv;
+	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+	MPT_FRAME_HDR *mf;
+	LANReceivePostRequest_t *pRecvReq;
+	SGETransaction32_t *pTrans;
+	SGESimple64_t *pSimple;
+	struct sk_buff *skb;
+	dma_addr_t dma;
+	u32 curr, buckets, count, max;
+	u32 len = (dev->mtu + dev->hard_header_len + 4);
+	unsigned long flags;
+	int i;
+
+	curr = atomic_read(&priv->buckets_out);
+	buckets = (priv->max_buckets_out - curr);
+
+	dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, Start_buckets = %u, buckets_out = %u\n",
+			IOC_AND_NETDEV_NAMES_s_s(dev),
+			__FUNCTION__, buckets, curr));
+
+	max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) /
+			(MPT_LAN_TRANSACTION32_SIZE + sizeof(SGESimple64_t));
+
+	while (buckets) {
+		mf = mpt_get_msg_frame(LanCtx, mpt_dev);
+		if (mf == NULL) {
+			printk (KERN_ERR "%s: Unable to alloc request frame\n",
+				__FUNCTION__);
+			dioprintk((KERN_ERR "%s: %u buckets remaining\n",
+				 __FUNCTION__, buckets));
+			goto out;
+		}
+		pRecvReq = (LANReceivePostRequest_t *) mf;
+
+		count = buckets;
+		if (count > max)
+			count = max;
+
+		pRecvReq->Function    = MPI_FUNCTION_LAN_RECEIVE;
+		pRecvReq->ChainOffset = 0;
+		pRecvReq->MsgFlags    = 0;
+		pRecvReq->PortNumber  = priv->pnum;
+
+		pTrans = (SGETransaction32_t *) pRecvReq->SG_List;
+		pSimple = NULL;
+
+		for (i = 0; i < count; i++) {
+			int ctx;
+
+			spin_lock_irqsave(&priv->rxfidx_lock, flags);
+			if (priv->mpt_rxfidx_tail < 0) {
+				printk (KERN_ERR "%s: Can't alloc context\n",
+					__FUNCTION__);
+				spin_unlock_irqrestore(&priv->rxfidx_lock,
+						       flags);
+				break;
+			}
+
+			ctx = priv->mpt_rxfidx[priv->mpt_rxfidx_tail--];
+
+			skb = priv->RcvCtl[ctx].skb;
+			if (skb && (priv->RcvCtl[ctx].len != len)) {
+				pci_unmap_single(mpt_dev->pcidev,
+						 priv->RcvCtl[ctx].dma,
+						 priv->RcvCtl[ctx].len,
+						 PCI_DMA_FROMDEVICE);
+				dev_kfree_skb(priv->RcvCtl[ctx].skb);
+				skb = priv->RcvCtl[ctx].skb = NULL;
+			}
+
+			if (skb == NULL) {
+				skb = dev_alloc_skb(len);
+				if (skb == NULL) {
+					printk (KERN_WARNING
+						MYNAM "/%s: Can't alloc skb\n",
+						__FUNCTION__);
+					priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
+					spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+					break;
+				}
+
+				dma = pci_map_single(mpt_dev->pcidev, skb->data,
+						     len, PCI_DMA_FROMDEVICE);
+
+				priv->RcvCtl[ctx].skb = skb;
+				priv->RcvCtl[ctx].dma = dma;
+				priv->RcvCtl[ctx].len = len;
+			}
+
+			spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
+
+			pTrans->ContextSize   = sizeof(u32);
+			pTrans->DetailsLength = 0;
+			pTrans->Flags         = 0;
+			pTrans->TransactionContext[0] = cpu_to_le32(ctx);
+
+			pSimple = (SGESimple64_t *) pTrans->TransactionDetails;
+
+			pSimple->FlagsLength = cpu_to_le32(
+				((MPI_SGE_FLAGS_END_OF_BUFFER |
+				  MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+				  MPI_SGE_FLAGS_64_BIT_ADDRESSING) << MPI_SGE_FLAGS_SHIFT) | len);
+			pSimple->Address.Low = cpu_to_le32((u32) priv->RcvCtl[ctx].dma);
+			if (sizeof(dma_addr_t) > sizeof(u32))
+				pSimple->Address.High = cpu_to_le32((u32) ((u64) priv->RcvCtl[ctx].dma >> 32));
+			else
+				pSimple->Address.High = 0;
+
+			pTrans = (SGETransaction32_t *) (pSimple + 1);
+		}
+
+		if (pSimple == NULL) {
+/**/			printk (KERN_WARNING MYNAM "/%s: No buckets posted\n",
+/**/				__FUNCTION__);
+			mpt_free_msg_frame(mpt_dev, mf);
+			goto out;
+		}
+
+		pSimple->FlagsLength |= cpu_to_le32(MPI_SGE_FLAGS_END_OF_LIST << MPI_SGE_FLAGS_SHIFT);
+
+		pRecvReq->BucketCount = cpu_to_le32(i);
+
+/*	printk(KERN_INFO MYNAM ": posting buckets\n   ");
+ *	for (i = 0; i < j + 2; i ++)
+ *	    printk (" %08x", le32_to_cpu(msg[i]));
+ *	printk ("\n");
+ */
+
+		mpt_put_msg_frame(LanCtx, mpt_dev, mf);
+
+		priv->total_posted += i;
+		buckets -= i;
+		atomic_add(i, &priv->buckets_out);
+	}
+
+out:
+	dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n",
+		  __FUNCTION__, buckets, atomic_read(&priv->buckets_out)));
+	dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n",
+	__FUNCTION__, priv->total_posted, priv->total_received));
+
+	clear_bit(0, &priv->post_buckets_active);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static struct net_device *
+mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum)
+{
+	struct net_device *dev = alloc_fcdev(sizeof(struct mpt_lan_priv));
+	struct mpt_lan_priv *priv = NULL;
+	u8 HWaddr[FC_ALEN], *a;
+
+	if (!dev)
+		return NULL;
+
+	dev->mtu = MPT_LAN_MTU;
+
+	priv = netdev_priv(dev);
+
+	priv->mpt_dev = mpt_dev;
+	priv->pnum = pnum;
+
+	memset(&priv->post_buckets_task, 0, sizeof(struct work_struct));
+	INIT_WORK(&priv->post_buckets_task, mpt_lan_post_receive_buckets, dev);
+	priv->post_buckets_active = 0;
+
+	dlprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n",
+			__LINE__, dev->mtu + dev->hard_header_len + 4));
+
+	atomic_set(&priv->buckets_out, 0);
+	priv->total_posted = 0;
+	priv->total_received = 0;
+	priv->max_buckets_out = max_buckets_out;
+	if (mpt_dev->pfacts[0].MaxLanBuckets < max_buckets_out)
+		priv->max_buckets_out = mpt_dev->pfacts[0].MaxLanBuckets;
+
+	dlprintk((KERN_INFO MYNAM "@%d: MaxLanBuckets=%d, max_buckets_out/priv=%d/%d\n",
+			__LINE__,
+			mpt_dev->pfacts[0].MaxLanBuckets,
+			max_buckets_out,
+			priv->max_buckets_out));
+
+	priv->bucketthresh = priv->max_buckets_out * 2 / 3;
+	spin_lock_init(&priv->txfidx_lock);
+	spin_lock_init(&priv->rxfidx_lock);
+
+	memset(&priv->stats, 0, sizeof(priv->stats));
+
+	/*  Grab pre-fetched LANPage1 stuff. :-) */
+	a = (u8 *) &mpt_dev->lan_cnfg_page1.HardwareAddressLow;
+
+	HWaddr[0] = a[5];
+	HWaddr[1] = a[4];
+	HWaddr[2] = a[3];
+	HWaddr[3] = a[2];
+	HWaddr[4] = a[1];
+	HWaddr[5] = a[0];
+
+	dev->addr_len = FC_ALEN;
+	memcpy(dev->dev_addr, HWaddr, FC_ALEN);
+	memset(dev->broadcast, 0xff, FC_ALEN);
+
+	/* The Tx queue is 127 deep on the 909.
+	 * Give ourselves some breathing room.
+	 */
+	priv->tx_max_out = (tx_max_out_p <= MPT_TX_MAX_OUT_LIM) ?
+			    tx_max_out_p : MPT_TX_MAX_OUT_LIM;
+
+	dev->open = mpt_lan_open;
+	dev->stop = mpt_lan_close;
+	dev->get_stats = mpt_lan_get_stats;
+	dev->set_multicast_list = NULL;
+	dev->change_mtu = mpt_lan_change_mtu;
+	dev->hard_start_xmit = mpt_lan_sdu_send;
+
+/* Not in 2.3.42. Need 2.3.45+ */
+	dev->tx_timeout = mpt_lan_tx_timeout;
+	dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT;
+
+	dlprintk((KERN_INFO MYNAM ": Finished registering dev "
+		"and setting initial values\n"));
+
+	SET_MODULE_OWNER(dev);
+
+	if (register_netdev(dev) != 0) {
+		free_netdev(dev);
+		dev = NULL;
+	}
+	return dev;
+}
+
+static int
+mptlan_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev);
+	struct net_device	*dev;
+	int			i;
+
+	for (i = 0; i < ioc->facts.NumberOfPorts; i++) {
+		printk(KERN_INFO MYNAM ": %s: PortNum=%x, "
+		       "ProtocolFlags=%02Xh (%c%c%c%c)\n",
+		       ioc->name, ioc->pfacts[i].PortNumber,
+		       ioc->pfacts[i].ProtocolFlags,
+		       MPT_PROTOCOL_FLAGS_c_c_c_c(
+			       ioc->pfacts[i].ProtocolFlags));
+
+		if (!(ioc->pfacts[i].ProtocolFlags &
+					MPI_PORTFACTS_PROTOCOL_LAN)) {
+			printk(KERN_INFO MYNAM ": %s: Hmmm... LAN protocol "
+			       "seems to be disabled on this adapter port!\n",
+			       ioc->name);
+			continue;
+		}
+
+		dev = mpt_register_lan_device(ioc, i);
+		if (!dev) {
+			printk(KERN_ERR MYNAM ": %s: Unable to register "
+			       "port%d as a LAN device\n", ioc->name,
+			       ioc->pfacts[i].PortNumber);
+			continue;
+		}
+		
+		printk(KERN_INFO MYNAM ": %s: Fusion MPT LAN device "
+		       "registered as '%s'\n", ioc->name, dev->name);
+		printk(KERN_INFO MYNAM ": %s/%s: "
+		       "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+		       IOC_AND_NETDEV_NAMES_s_s(dev),
+		       dev->dev_addr[0], dev->dev_addr[1],
+		       dev->dev_addr[2], dev->dev_addr[3],
+		       dev->dev_addr[4], dev->dev_addr[5]);
+	
+		ioc->netdev = dev;
+
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static void
+mptlan_remove(struct pci_dev *pdev)
+{
+	MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev);
+	struct net_device	*dev = ioc->netdev;
+
+	if(dev != NULL) {
+		unregister_netdev(dev);
+		free_netdev(dev);
+	}
+}
+
+static struct mpt_pci_driver mptlan_driver = {
+	.probe		= mptlan_probe,
+	.remove		= mptlan_remove,
+};
+
+static int __init mpt_lan_init (void)
+{
+	show_mptmod_ver(LANAME, LANVER);
+
+	if ((LanCtx = mpt_register(lan_reply, MPTLAN_DRIVER)) <= 0) {
+		printk (KERN_ERR MYNAM ": Failed to register with MPT base driver\n");
+		return -EBUSY;
+	}
+
+	/* Set the callback index to be used by driver core for turbo replies */
+	mpt_lan_index = LanCtx;
+
+	dlprintk((KERN_INFO MYNAM ": assigned context of %d\n", LanCtx));
+
+	if (mpt_reset_register(LanCtx, mpt_lan_ioc_reset)) {
+		printk(KERN_ERR MYNAM ": Eieee! unable to register a reset "
+		       "handler with mptbase! The world is at an end! "
+		       "Everything is fading to black! Goodbye.\n");
+		return -EBUSY;
+	}
+
+	dlprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
+	
+	if (mpt_device_driver_register(&mptlan_driver, MPTLAN_DRIVER))
+		dprintk((KERN_INFO MYNAM ": failed to register dd callbacks\n"));
+	return 0;
+}
+
+static void __exit mpt_lan_exit(void)
+{
+	mpt_device_driver_deregister(MPTLAN_DRIVER);
+	mpt_reset_deregister(LanCtx);
+
+	if (LanCtx >= 0) {
+		mpt_deregister(LanCtx);
+		LanCtx = -1;
+		mpt_lan_index = 0;
+	}
+}
+
+module_init(mpt_lan_init);
+module_exit(mpt_lan_exit);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static unsigned short
+mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data;
+	struct fcllc *fcllc;
+
+	skb->mac.raw = skb->data;
+	skb_pull(skb, sizeof(struct mpt_lan_ohdr));
+
+	if (fch->dtype == htons(0xffff)) {
+		u32 *p = (u32 *) fch;
+
+		swab32s(p + 0);
+		swab32s(p + 1);
+		swab32s(p + 2);
+		swab32s(p + 3);
+
+		printk (KERN_WARNING MYNAM ": %s: WARNING - Broadcast swap F/W bug detected!\n",
+				NETDEV_PTR_TO_IOC_NAME_s(dev));
+		printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
+				fch->saddr[0], fch->saddr[1], fch->saddr[2],
+				fch->saddr[3], fch->saddr[4], fch->saddr[5]);
+	}
+
+	if (*fch->daddr & 1) {
+		if (!memcmp(fch->daddr, dev->broadcast, FC_ALEN)) {
+			skb->pkt_type = PACKET_BROADCAST;
+		} else {
+			skb->pkt_type = PACKET_MULTICAST;
+		}
+	} else {
+		if (memcmp(fch->daddr, dev->dev_addr, FC_ALEN)) {
+			skb->pkt_type = PACKET_OTHERHOST;
+		} else {
+			skb->pkt_type = PACKET_HOST;
+		}
+	}
+
+	fcllc = (struct fcllc *)skb->data;
+
+#ifdef QLOGIC_NAA_WORKAROUND
+{
+	u16 source_naa = fch->stype, found = 0;
+
+	/* Workaround for QLogic not following RFC 2625 in regards to the NAA
+	   value. */
+
+	if ((source_naa & 0xF000) == 0)
+		source_naa = swab16(source_naa);
+
+	if (fcllc->ethertype == htons(ETH_P_ARP))
+	    dlprintk ((KERN_INFO "mptlan/type_trans: got arp req/rep w/ naa of "
+		      "%04x.\n", source_naa));
+
+	if ((fcllc->ethertype == htons(ETH_P_ARP)) &&
+	   ((source_naa >> 12) !=  MPT_LAN_NAA_RFC2625)){
+		struct NAA_Hosed *nh, *prevnh;
+		int i;
+
+		dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep from "
+			  "system with non-RFC 2625 NAA value (%04x).\n",
+			  source_naa));
+
+		write_lock_irq(&bad_naa_lock);
+		for (prevnh = nh = mpt_bad_naa; nh != NULL;
+		     prevnh=nh, nh=nh->next) {
+			if ((nh->ieee[0] == fch->saddr[0]) &&
+			    (nh->ieee[1] == fch->saddr[1]) &&
+			    (nh->ieee[2] == fch->saddr[2]) &&
+			    (nh->ieee[3] == fch->saddr[3]) &&
+			    (nh->ieee[4] == fch->saddr[4]) &&
+			    (nh->ieee[5] == fch->saddr[5])) {
+				found = 1;
+				dlprintk ((KERN_INFO "mptlan/type_trans: ARP Re"
+					 "q/Rep w/ bad NAA from system already"
+					 " in DB.\n"));
+				break;
+			}
+		}
+
+		if ((!found) && (nh == NULL)) {
+
+			nh = kmalloc(sizeof(struct NAA_Hosed), GFP_KERNEL);
+			dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep w/"
+				 " bad NAA from system not yet in DB.\n"));
+
+			if (nh != NULL) {
+				nh->next = NULL;
+				if (!mpt_bad_naa)
+					mpt_bad_naa = nh;
+				if (prevnh)
+					prevnh->next = nh;
+
+				nh->NAA = source_naa; /* Set the S_NAA value. */
+				for (i = 0; i < FC_ALEN; i++)
+					nh->ieee[i] = fch->saddr[i];
+				dlprintk ((KERN_INFO "Got ARP from %02x:%02x:%02x:%02x:"
+					  "%02x:%02x with non-compliant S_NAA value.\n",
+					  fch->saddr[0], fch->saddr[1], fch->saddr[2],
+					  fch->saddr[3], fch->saddr[4],fch->saddr[5]));
+			} else {
+				printk (KERN_ERR "mptlan/type_trans: Unable to"
+					" kmalloc a NAA_Hosed struct.\n");
+			}
+		} else if (!found) {
+			printk (KERN_ERR "mptlan/type_trans: found not"
+				" set, but nh isn't null. Evil "
+				"funkiness abounds.\n");
+		}
+		write_unlock_irq(&bad_naa_lock);
+	}
+}
+#endif
+
+	/* Strip the SNAP header from ARP packets since we don't
+	 * pass them through to the 802.2/SNAP layers.
+	 */
+	if (fcllc->dsap == EXTENDED_SAP &&
+		(fcllc->ethertype == htons(ETH_P_IP) ||
+		 fcllc->ethertype == htons(ETH_P_ARP))) {
+		skb_pull(skb, sizeof(struct fcllc));
+		return fcllc->ethertype;
+	}
+
+	return htons(ETH_P_802_2);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/