| /* | 
 |  *  linux/drivers/message/fusion/mptlan.c | 
 |  *      IP Over Fibre Channel device driver. | 
 |  *      For use with LSI Fibre Channel PCI chip/adapters | 
 |  *      running LSI Fusion MPT (Message Passing Technology) firmware. | 
 |  * | 
 |  *  Copyright (c) 2000-2008 LSI Corporation | 
 |  *  (mailto:DL-MPTFusionLinux@lsi.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; 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 my_VERSION	MPT_LINUX_VERSION_COMMON | 
 | #define MYNAM		"mptlan" | 
 |  | 
 | MODULE_LICENSE("GPL"); | 
 | MODULE_VERSION(my_VERSION); | 
 |  | 
 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
 | /* | 
 |  * 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 delayed_work post_buckets_task; | 
 | 	struct net_device *dev; | 
 | 	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(struct mpt_lan_priv *priv); | 
 | 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 u8 LanCtx = MPT_MAX_PROTOCOL_DRIVERS; | 
 |  | 
 | 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 | 
 |  | 
 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
 | /** | 
 |  *	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! | 
 | #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? | 
 | 			//	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: | 
 | 		/*  _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; | 
 |  | 
 | 	if (dev == NULL) | 
 | 		return(1); | 
 | 	else | 
 | 		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(priv); | 
 | 		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 = kcalloc(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++) | 
 | 		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 = kcalloc(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++) | 
 | 		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(priv); | 
 | 	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 long 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 = jiffies + 2 * HZ; | 
 | 	while (atomic_read(&priv->buckets_out) && time_before(jiffies, timeout)) | 
 | 		schedule_timeout_interruptible(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; | 
 | 	const unsigned char *mac; | 
 | 	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_reset_mac_header(skb); | 
 | 	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)); | 
 |  | 
 | 	mac = skb_mac_header(skb); | 
 | #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] == mac[0]) && | 
 | 		    (nh->ieee[1] == mac[1]) && | 
 | 		    (nh->ieee[2] == mac[2]) && | 
 | 		    (nh->ieee[3] == mac[3]) && | 
 | 		    (nh->ieee[4] == mac[4]) && | 
 | 		    (nh->ieee[5] == mac[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) | | 
 | 						    (mac[0] <<  8) | | 
 | 						    (mac[1] <<  0)); | 
 | 	pTrans->TransactionDetails[1] = cpu_to_le32((mac[2] << 24) | | 
 | 						    (mac[3] << 16) | | 
 | 						    (mac[4] <<  8) | | 
 | 						    (mac[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 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_delayed_work(&priv->post_buckets_task, 0); | 
 | 		} 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 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); | 
 |  | 
 | 		skb_copy_from_linear_data(old_skb, skb_put(skb, len), 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); | 
 | 			skb_copy_from_linear_data(old_skb, skb_put(skb, l), 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); | 
 |  | 
 | 		skb_copy_from_linear_data(old_skb, skb_put(skb, len), 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); | 
 |  | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	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(struct mpt_lan_priv *priv) | 
 | { | 
 | 	struct net_device *dev = priv->dev; | 
 | 	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; | 
 |  | 
 | 		i = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); | 
 | 		mpt_dev->RequestNB[i] = 0; | 
 | 		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 void | 
 | mpt_lan_post_receive_buckets_work(struct work_struct *work) | 
 | { | 
 | 	mpt_lan_post_receive_buckets(container_of(work, struct mpt_lan_priv, | 
 | 						  post_buckets_task.work)); | 
 | } | 
 |  | 
 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | 
 | static struct net_device * | 
 | mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum) | 
 | { | 
 | 	struct net_device *dev; | 
 | 	struct mpt_lan_priv *priv; | 
 | 	u8 HWaddr[FC_ALEN], *a; | 
 |  | 
 | 	dev = alloc_fcdev(sizeof(struct mpt_lan_priv)); | 
 | 	if (!dev) | 
 | 		return NULL; | 
 |  | 
 | 	dev->mtu = MPT_LAN_MTU; | 
 |  | 
 | 	priv = netdev_priv(dev); | 
 |  | 
 | 	priv->dev = dev; | 
 | 	priv->mpt_dev = mpt_dev; | 
 | 	priv->pnum = pnum; | 
 |  | 
 | 	INIT_DELAYED_WORK(&priv->post_buckets_task, | 
 | 			  mpt_lan_post_receive_buckets_work); | 
 | 	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); | 
 |  | 
 | 	/*  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")); | 
 |  | 
 | 	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; | 
 | 	} | 
 |  | 
 | 	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")); | 
 | 	 | 
 | 	mpt_device_driver_register(&mptlan_driver, MPTLAN_DRIVER); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __exit mpt_lan_exit(void) | 
 | { | 
 | 	mpt_device_driver_deregister(MPTLAN_DRIVER); | 
 | 	mpt_reset_deregister(LanCtx); | 
 |  | 
 | 	if (LanCtx) { | 
 | 		mpt_deregister(LanCtx); | 
 | 		LanCtx = MPT_MAX_PROTOCOL_DRIVERS; | 
 | 	} | 
 | } | 
 |  | 
 | 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_reset_mac_header(skb); | 
 | 	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); | 
 | } | 
 |  | 
 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ |