162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/drivers/message/fusion/mptlan.c
362306a36Sopenharmony_ci *      IP Over Fibre Channel device driver.
462306a36Sopenharmony_ci *      For use with LSI Fibre Channel PCI chip/adapters
562306a36Sopenharmony_ci *      running LSI Fusion MPT (Message Passing Technology) firmware.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (c) 2000-2008 LSI Corporation
862306a36Sopenharmony_ci *  (mailto:DL-MPTFusionLinux@lsi.com)
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci    This program is free software; you can redistribute it and/or modify
1462306a36Sopenharmony_ci    it under the terms of the GNU General Public License as published by
1562306a36Sopenharmony_ci    the Free Software Foundation; version 2 of the License.
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci    This program is distributed in the hope that it will be useful,
1862306a36Sopenharmony_ci    but WITHOUT ANY WARRANTY; without even the implied warranty of
1962306a36Sopenharmony_ci    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2062306a36Sopenharmony_ci    GNU General Public License for more details.
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci    NO WARRANTY
2362306a36Sopenharmony_ci    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2462306a36Sopenharmony_ci    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2562306a36Sopenharmony_ci    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2662306a36Sopenharmony_ci    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2762306a36Sopenharmony_ci    solely responsible for determining the appropriateness of using and
2862306a36Sopenharmony_ci    distributing the Program and assumes all risks associated with its
2962306a36Sopenharmony_ci    exercise of rights under this Agreement, including but not limited to
3062306a36Sopenharmony_ci    the risks and costs of program errors, damage to or loss of data,
3162306a36Sopenharmony_ci    programs or equipment, and unavailability or interruption of operations.
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci    DISCLAIMER OF LIABILITY
3462306a36Sopenharmony_ci    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3562306a36Sopenharmony_ci    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3662306a36Sopenharmony_ci    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3762306a36Sopenharmony_ci    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3862306a36Sopenharmony_ci    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3962306a36Sopenharmony_ci    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
4062306a36Sopenharmony_ci    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci    You should have received a copy of the GNU General Public License
4362306a36Sopenharmony_ci    along with this program; if not, write to the Free Software
4462306a36Sopenharmony_ci    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4562306a36Sopenharmony_ci*/
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * Define statements used for debugging
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci//#define MPT_LAN_IO_DEBUG
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#include "mptlan.h"
5662306a36Sopenharmony_ci#include <linux/init.h>
5762306a36Sopenharmony_ci#include <linux/module.h>
5862306a36Sopenharmony_ci#include <linux/fs.h>
5962306a36Sopenharmony_ci#include <linux/sched.h>
6062306a36Sopenharmony_ci#include <linux/slab.h>
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define my_VERSION	MPT_LINUX_VERSION_COMMON
6362306a36Sopenharmony_ci#define MYNAM		"mptlan"
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6662306a36Sopenharmony_ciMODULE_VERSION(my_VERSION);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * MPT LAN message sizes without variable part.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_ci#define MPT_LAN_RECEIVE_POST_REQUEST_SIZE \
7362306a36Sopenharmony_ci	(sizeof(LANReceivePostRequest_t) - sizeof(SGE_MPI_UNION))
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci *  Fusion MPT LAN private structures
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct BufferControl {
8062306a36Sopenharmony_ci	struct sk_buff	*skb;
8162306a36Sopenharmony_ci	dma_addr_t	dma;
8262306a36Sopenharmony_ci	unsigned int	len;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct mpt_lan_priv {
8662306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev;
8762306a36Sopenharmony_ci	u8 pnum; /* Port number in the IOC. This is not a Unix network port! */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	atomic_t buckets_out;		/* number of unused buckets on IOC */
9062306a36Sopenharmony_ci	int bucketthresh;		/* Send more when this many left */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	int *mpt_txfidx; /* Free Tx Context list */
9362306a36Sopenharmony_ci	int mpt_txfidx_tail;
9462306a36Sopenharmony_ci	spinlock_t txfidx_lock;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	int *mpt_rxfidx; /* Free Rx Context list */
9762306a36Sopenharmony_ci	int mpt_rxfidx_tail;
9862306a36Sopenharmony_ci	spinlock_t rxfidx_lock;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	struct BufferControl *RcvCtl;	/* Receive BufferControl structs */
10162306a36Sopenharmony_ci	struct BufferControl *SendCtl;	/* Send BufferControl structs */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	int max_buckets_out;		/* Max buckets to send to IOC */
10462306a36Sopenharmony_ci	int tx_max_out;			/* IOC's Tx queue len */
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	u32 total_posted;
10762306a36Sopenharmony_ci	u32 total_received;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	struct delayed_work post_buckets_task;
11062306a36Sopenharmony_ci	struct net_device *dev;
11162306a36Sopenharmony_ci	unsigned long post_buckets_active;
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistruct mpt_lan_ohdr {
11562306a36Sopenharmony_ci	u16	dtype;
11662306a36Sopenharmony_ci	u8	daddr[FC_ALEN];
11762306a36Sopenharmony_ci	u16	stype;
11862306a36Sopenharmony_ci	u8	saddr[FC_ALEN];
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci *  Forward protos...
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_cistatic int  lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf,
12762306a36Sopenharmony_ci		       MPT_FRAME_HDR *reply);
12862306a36Sopenharmony_cistatic int  mpt_lan_open(struct net_device *dev);
12962306a36Sopenharmony_cistatic int  mpt_lan_reset(struct net_device *dev);
13062306a36Sopenharmony_cistatic int  mpt_lan_close(struct net_device *dev);
13162306a36Sopenharmony_cistatic void mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv);
13262306a36Sopenharmony_cistatic void mpt_lan_wake_post_buckets_task(struct net_device *dev,
13362306a36Sopenharmony_ci					   int priority);
13462306a36Sopenharmony_cistatic int  mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg);
13562306a36Sopenharmony_cistatic int  mpt_lan_receive_post_reply(struct net_device *dev,
13662306a36Sopenharmony_ci				       LANReceivePostReply_t *pRecvRep);
13762306a36Sopenharmony_cistatic int  mpt_lan_send_turbo(struct net_device *dev, u32 tmsg);
13862306a36Sopenharmony_cistatic int  mpt_lan_send_reply(struct net_device *dev,
13962306a36Sopenharmony_ci			       LANSendReply_t *pSendRep);
14062306a36Sopenharmony_cistatic int  mpt_lan_ioc_reset(MPT_ADAPTER *ioc, int reset_phase);
14162306a36Sopenharmony_cistatic int  mpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
14262306a36Sopenharmony_cistatic unsigned short mpt_lan_type_trans(struct sk_buff *skb,
14362306a36Sopenharmony_ci					 struct net_device *dev);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
14662306a36Sopenharmony_ci/*
14762306a36Sopenharmony_ci *  Fusion MPT LAN private data
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic u8 LanCtx = MPT_MAX_PROTOCOL_DRIVERS;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic u32 max_buckets_out = 127;
15262306a36Sopenharmony_cistatic u32 tx_max_out_p = 127 - 16;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci *	lan_reply - Handle all data sent from the hardware.
15762306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
15862306a36Sopenharmony_ci *	@mf: Pointer to original MPT request frame (NULL if TurboReply)
15962306a36Sopenharmony_ci *	@reply: Pointer to MPT reply frame
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci *	Returns 1 indicating original alloc'd request frame ptr
16262306a36Sopenharmony_ci *	should be freed, or 0 if it shouldn't.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cistatic int
16562306a36Sopenharmony_cilan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct net_device *dev = ioc->netdev;
16862306a36Sopenharmony_ci	int FreeReqFrame = 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s/%s: Got reply.\n",
17162306a36Sopenharmony_ci		  IOC_AND_NETDEV_NAMES_s_s(dev)));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci//	dioprintk((KERN_INFO MYNAM "@lan_reply: mf = %p, reply = %p\n",
17462306a36Sopenharmony_ci//			mf, reply));
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (mf == NULL) {
17762306a36Sopenharmony_ci		u32 tmsg = CAST_PTR_TO_U32(reply);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		dioprintk((KERN_INFO MYNAM ": %s/%s: @lan_reply, tmsg %08x\n",
18062306a36Sopenharmony_ci				IOC_AND_NETDEV_NAMES_s_s(dev),
18162306a36Sopenharmony_ci				tmsg));
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		switch (GET_LAN_FORM(tmsg)) {
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		// NOTE!  (Optimization) First case here is now caught in
18662306a36Sopenharmony_ci		//  mptbase.c::mpt_interrupt() routine and callcack here
18762306a36Sopenharmony_ci		//  is now skipped for this case!
18862306a36Sopenharmony_ci#if 0
18962306a36Sopenharmony_ci		case LAN_REPLY_FORM_MESSAGE_CONTEXT:
19062306a36Sopenharmony_ci//			dioprintk((KERN_INFO MYNAM "/lan_reply: "
19162306a36Sopenharmony_ci//				  "MessageContext turbo reply received\n"));
19262306a36Sopenharmony_ci			FreeReqFrame = 1;
19362306a36Sopenharmony_ci			break;
19462306a36Sopenharmony_ci#endif
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		case LAN_REPLY_FORM_SEND_SINGLE:
19762306a36Sopenharmony_ci//			dioprintk((MYNAM "/lan_reply: "
19862306a36Sopenharmony_ci//				  "calling mpt_lan_send_reply (turbo)\n"));
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci			// Potential BUG here?
20162306a36Sopenharmony_ci			//	FreeReqFrame = mpt_lan_send_turbo(dev, tmsg);
20262306a36Sopenharmony_ci			//  If/when mpt_lan_send_turbo would return 1 here,
20362306a36Sopenharmony_ci			//  calling routine (mptbase.c|mpt_interrupt)
20462306a36Sopenharmony_ci			//  would Oops because mf has already been set
20562306a36Sopenharmony_ci			//  to NULL.  So after return from this func,
20662306a36Sopenharmony_ci			//  mpt_interrupt() will attempt to put (NULL) mf ptr
20762306a36Sopenharmony_ci			//  item back onto its adapter FreeQ - Oops!:-(
20862306a36Sopenharmony_ci			//  It's Ok, since mpt_lan_send_turbo() *currently*
20962306a36Sopenharmony_ci			//  always returns 0, but..., just in case:
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci			(void) mpt_lan_send_turbo(dev, tmsg);
21262306a36Sopenharmony_ci			FreeReqFrame = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci			break;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		case LAN_REPLY_FORM_RECEIVE_SINGLE:
21762306a36Sopenharmony_ci//			dioprintk((KERN_INFO MYNAM "@lan_reply: "
21862306a36Sopenharmony_ci//				  "rcv-Turbo = %08x\n", tmsg));
21962306a36Sopenharmony_ci			mpt_lan_receive_post_turbo(dev, tmsg);
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		default:
22362306a36Sopenharmony_ci			printk (KERN_ERR MYNAM "/lan_reply: Got a turbo reply "
22462306a36Sopenharmony_ci				"that I don't know what to do with\n");
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci			/* CHECKME!  Hmmm...  FreeReqFrame is 0 here; is that right? */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci			break;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		return FreeReqFrame;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci//	msg = (u32 *) reply;
23562306a36Sopenharmony_ci//	dioprintk((KERN_INFO MYNAM "@lan_reply: msg = %08x %08x %08x %08x\n",
23662306a36Sopenharmony_ci//		  le32_to_cpu(msg[0]), le32_to_cpu(msg[1]),
23762306a36Sopenharmony_ci//		  le32_to_cpu(msg[2]), le32_to_cpu(msg[3])));
23862306a36Sopenharmony_ci//	dioprintk((KERN_INFO MYNAM "@lan_reply: Function = %02xh\n",
23962306a36Sopenharmony_ci//		  reply->u.hdr.Function));
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	switch (reply->u.hdr.Function) {
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	case MPI_FUNCTION_LAN_SEND:
24462306a36Sopenharmony_ci	{
24562306a36Sopenharmony_ci		LANSendReply_t *pSendRep;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		pSendRep = (LANSendReply_t *) reply;
24862306a36Sopenharmony_ci		FreeReqFrame = mpt_lan_send_reply(dev, pSendRep);
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	case MPI_FUNCTION_LAN_RECEIVE:
25362306a36Sopenharmony_ci	{
25462306a36Sopenharmony_ci		LANReceivePostReply_t *pRecvRep;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		pRecvRep = (LANReceivePostReply_t *) reply;
25762306a36Sopenharmony_ci		if (pRecvRep->NumberOfContexts) {
25862306a36Sopenharmony_ci			mpt_lan_receive_post_reply(dev, pRecvRep);
25962306a36Sopenharmony_ci			if (!(pRecvRep->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY))
26062306a36Sopenharmony_ci				FreeReqFrame = 1;
26162306a36Sopenharmony_ci		} else
26262306a36Sopenharmony_ci			dioprintk((KERN_INFO MYNAM "@lan_reply: zero context "
26362306a36Sopenharmony_ci				  "ReceivePostReply received.\n"));
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	case MPI_FUNCTION_LAN_RESET:
26862306a36Sopenharmony_ci		/* Just a default reply. Might want to check it to
26962306a36Sopenharmony_ci		 * make sure that everything went ok.
27062306a36Sopenharmony_ci		 */
27162306a36Sopenharmony_ci		FreeReqFrame = 1;
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	case MPI_FUNCTION_EVENT_NOTIFICATION:
27562306a36Sopenharmony_ci	case MPI_FUNCTION_EVENT_ACK:
27662306a36Sopenharmony_ci		/*  _EVENT_NOTIFICATION should NOT come down this path any more.
27762306a36Sopenharmony_ci		 *  Should be routed to mpt_lan_event_process(), but just in case...
27862306a36Sopenharmony_ci		 */
27962306a36Sopenharmony_ci		FreeReqFrame = 1;
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	default:
28362306a36Sopenharmony_ci		printk (KERN_ERR MYNAM "/lan_reply: Got a non-turbo "
28462306a36Sopenharmony_ci			"reply that I don't know what to do with\n");
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		/* CHECKME!  Hmmm...  FreeReqFrame is 0 here; is that right? */
28762306a36Sopenharmony_ci		FreeReqFrame = 1;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return FreeReqFrame;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
29662306a36Sopenharmony_cistatic int
29762306a36Sopenharmony_cimpt_lan_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct net_device *dev = ioc->netdev;
30062306a36Sopenharmony_ci	struct mpt_lan_priv *priv;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (dev == NULL)
30362306a36Sopenharmony_ci		return(1);
30462306a36Sopenharmony_ci	else
30562306a36Sopenharmony_ci		priv = netdev_priv(dev);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n",
30862306a36Sopenharmony_ci			reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
30962306a36Sopenharmony_ci			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (priv->mpt_rxfidx == NULL)
31262306a36Sopenharmony_ci		return (1);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (reset_phase == MPT_IOC_SETUP_RESET) {
31562306a36Sopenharmony_ci		;
31662306a36Sopenharmony_ci	} else if (reset_phase == MPT_IOC_PRE_RESET) {
31762306a36Sopenharmony_ci		int i;
31862306a36Sopenharmony_ci		unsigned long flags;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		netif_stop_queue(dev);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		dlprintk ((KERN_INFO "mptlan/ioc_reset: called netif_stop_queue for %s.\n", dev->name));
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		atomic_set(&priv->buckets_out, 0);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		/* Reset Rx Free Tail index and re-populate the queue. */
32762306a36Sopenharmony_ci		spin_lock_irqsave(&priv->rxfidx_lock, flags);
32862306a36Sopenharmony_ci		priv->mpt_rxfidx_tail = -1;
32962306a36Sopenharmony_ci		for (i = 0; i < priv->max_buckets_out; i++)
33062306a36Sopenharmony_ci			priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = i;
33162306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
33262306a36Sopenharmony_ci	} else {
33362306a36Sopenharmony_ci		mpt_lan_post_receive_buckets(priv);
33462306a36Sopenharmony_ci		netif_wake_queue(dev);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 1;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
34162306a36Sopenharmony_cistatic int
34262306a36Sopenharmony_cimpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ": MPT event routed to LAN driver!\n"));
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	switch (le32_to_cpu(pEvReply->Event)) {
34762306a36Sopenharmony_ci	case MPI_EVENT_NONE:				/* 00 */
34862306a36Sopenharmony_ci	case MPI_EVENT_LOG_DATA:			/* 01 */
34962306a36Sopenharmony_ci	case MPI_EVENT_STATE_CHANGE:			/* 02 */
35062306a36Sopenharmony_ci	case MPI_EVENT_UNIT_ATTENTION:			/* 03 */
35162306a36Sopenharmony_ci	case MPI_EVENT_IOC_BUS_RESET:			/* 04 */
35262306a36Sopenharmony_ci	case MPI_EVENT_EXT_BUS_RESET:			/* 05 */
35362306a36Sopenharmony_ci	case MPI_EVENT_RESCAN:				/* 06 */
35462306a36Sopenharmony_ci		/* Ok, do we need to do anything here? As far as
35562306a36Sopenharmony_ci		   I can tell, this is when a new device gets added
35662306a36Sopenharmony_ci		   to the loop. */
35762306a36Sopenharmony_ci	case MPI_EVENT_LINK_STATUS_CHANGE:		/* 07 */
35862306a36Sopenharmony_ci	case MPI_EVENT_LOOP_STATE_CHANGE:		/* 08 */
35962306a36Sopenharmony_ci	case MPI_EVENT_LOGOUT:				/* 09 */
36062306a36Sopenharmony_ci	case MPI_EVENT_EVENT_CHANGE:			/* 0A */
36162306a36Sopenharmony_ci	default:
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/*
36662306a36Sopenharmony_ci	 *  NOTE: pEvent->AckRequired handling now done in mptbase.c;
36762306a36Sopenharmony_ci	 *  Do NOT do it here now!
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return 1;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
37462306a36Sopenharmony_cistatic int
37562306a36Sopenharmony_cimpt_lan_open(struct net_device *dev)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
37862306a36Sopenharmony_ci	int i;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (mpt_lan_reset(dev) != 0) {
38162306a36Sopenharmony_ci		MPT_ADAPTER *mpt_dev = priv->mpt_dev;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		printk (KERN_WARNING MYNAM "/lan_open: lan_reset failed.");
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (mpt_dev->active)
38662306a36Sopenharmony_ci			printk ("The ioc is active. Perhaps it needs to be"
38762306a36Sopenharmony_ci				" reset?\n");
38862306a36Sopenharmony_ci		else
38962306a36Sopenharmony_ci			printk ("The ioc in inactive, most likely in the "
39062306a36Sopenharmony_ci				"process of being reset. Please try again in "
39162306a36Sopenharmony_ci				"a moment.\n");
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	priv->mpt_txfidx = kmalloc_array(priv->tx_max_out, sizeof(int),
39562306a36Sopenharmony_ci					 GFP_KERNEL);
39662306a36Sopenharmony_ci	if (priv->mpt_txfidx == NULL)
39762306a36Sopenharmony_ci		goto out;
39862306a36Sopenharmony_ci	priv->mpt_txfidx_tail = -1;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	priv->SendCtl = kcalloc(priv->tx_max_out, sizeof(struct BufferControl),
40162306a36Sopenharmony_ci				GFP_KERNEL);
40262306a36Sopenharmony_ci	if (priv->SendCtl == NULL)
40362306a36Sopenharmony_ci		goto out_mpt_txfidx;
40462306a36Sopenharmony_ci	for (i = 0; i < priv->tx_max_out; i++)
40562306a36Sopenharmony_ci		priv->mpt_txfidx[++priv->mpt_txfidx_tail] = i;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM "@lo: Finished initializing SendCtl\n"));
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	priv->mpt_rxfidx = kmalloc_array(priv->max_buckets_out, sizeof(int),
41062306a36Sopenharmony_ci					 GFP_KERNEL);
41162306a36Sopenharmony_ci	if (priv->mpt_rxfidx == NULL)
41262306a36Sopenharmony_ci		goto out_SendCtl;
41362306a36Sopenharmony_ci	priv->mpt_rxfidx_tail = -1;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	priv->RcvCtl = kcalloc(priv->max_buckets_out,
41662306a36Sopenharmony_ci			       sizeof(struct BufferControl),
41762306a36Sopenharmony_ci			       GFP_KERNEL);
41862306a36Sopenharmony_ci	if (priv->RcvCtl == NULL)
41962306a36Sopenharmony_ci		goto out_mpt_rxfidx;
42062306a36Sopenharmony_ci	for (i = 0; i < priv->max_buckets_out; i++)
42162306a36Sopenharmony_ci		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = i;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/**/	dlprintk((KERN_INFO MYNAM "/lo: txfidx contains - "));
42462306a36Sopenharmony_ci/**/	for (i = 0; i < priv->tx_max_out; i++)
42562306a36Sopenharmony_ci/**/		dlprintk((" %xh", priv->mpt_txfidx[i]));
42662306a36Sopenharmony_ci/**/	dlprintk(("\n"));
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM "/lo: Finished initializing RcvCtl\n"));
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	mpt_lan_post_receive_buckets(priv);
43162306a36Sopenharmony_ci	printk(KERN_INFO MYNAM ": %s/%s: interface up & active\n",
43262306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev));
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (mpt_event_register(LanCtx, mpt_lan_event_process) != 0) {
43562306a36Sopenharmony_ci		printk (KERN_WARNING MYNAM "/lo: Unable to register for Event"
43662306a36Sopenharmony_ci			" Notifications. This is a bad thing! We're not going "
43762306a36Sopenharmony_ci			"to go ahead, but I'd be leery of system stability at "
43862306a36Sopenharmony_ci			"this point.\n");
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	netif_start_queue(dev);
44262306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM "/lo: Done.\n"));
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return 0;
44562306a36Sopenharmony_ciout_mpt_rxfidx:
44662306a36Sopenharmony_ci	kfree(priv->mpt_rxfidx);
44762306a36Sopenharmony_ci	priv->mpt_rxfidx = NULL;
44862306a36Sopenharmony_ciout_SendCtl:
44962306a36Sopenharmony_ci	kfree(priv->SendCtl);
45062306a36Sopenharmony_ci	priv->SendCtl = NULL;
45162306a36Sopenharmony_ciout_mpt_txfidx:
45262306a36Sopenharmony_ci	kfree(priv->mpt_txfidx);
45362306a36Sopenharmony_ci	priv->mpt_txfidx = NULL;
45462306a36Sopenharmony_ciout:	return -ENOMEM;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
45862306a36Sopenharmony_ci/* Send a LanReset message to the FW. This should result in the FW returning
45962306a36Sopenharmony_ci   any buckets it still has. */
46062306a36Sopenharmony_cistatic int
46162306a36Sopenharmony_cimpt_lan_reset(struct net_device *dev)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	MPT_FRAME_HDR *mf;
46462306a36Sopenharmony_ci	LANResetRequest_t *pResetReq;
46562306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	mf = mpt_get_msg_frame(LanCtx, priv->mpt_dev);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (mf == NULL) {
47062306a36Sopenharmony_ci/*		dlprintk((KERN_ERR MYNAM "/reset: Evil funkiness abounds! "
47162306a36Sopenharmony_ci		"Unable to allocate a request frame.\n"));
47262306a36Sopenharmony_ci*/
47362306a36Sopenharmony_ci		return -1;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	pResetReq = (LANResetRequest_t *) mf;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	pResetReq->Function	= MPI_FUNCTION_LAN_RESET;
47962306a36Sopenharmony_ci	pResetReq->ChainOffset	= 0;
48062306a36Sopenharmony_ci	pResetReq->Reserved	= 0;
48162306a36Sopenharmony_ci	pResetReq->PortNumber	= priv->pnum;
48262306a36Sopenharmony_ci	pResetReq->MsgFlags	= 0;
48362306a36Sopenharmony_ci	pResetReq->Reserved2	= 0;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	mpt_put_msg_frame(LanCtx, priv->mpt_dev, mf);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	return 0;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
49162306a36Sopenharmony_cistatic int
49262306a36Sopenharmony_cimpt_lan_close(struct net_device *dev)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
49562306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
49662306a36Sopenharmony_ci	unsigned long timeout;
49762306a36Sopenharmony_ci	int i;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ": mpt_lan_close called\n"));
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	mpt_event_deregister(LanCtx);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ":lan_close: Posted %d buckets "
50462306a36Sopenharmony_ci		  "since driver was loaded, %d still out\n",
50562306a36Sopenharmony_ci		  priv->total_posted,atomic_read(&priv->buckets_out)));
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	netif_stop_queue(dev);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	mpt_lan_reset(dev);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	timeout = jiffies + 2 * HZ;
51262306a36Sopenharmony_ci	while (atomic_read(&priv->buckets_out) && time_before(jiffies, timeout))
51362306a36Sopenharmony_ci		schedule_timeout_interruptible(1);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	for (i = 0; i < priv->max_buckets_out; i++) {
51662306a36Sopenharmony_ci		if (priv->RcvCtl[i].skb != NULL) {
51762306a36Sopenharmony_ci/**/			dlprintk((KERN_INFO MYNAM "/lan_close: bucket %05x "
51862306a36Sopenharmony_ci/**/				  "is still out\n", i));
51962306a36Sopenharmony_ci			dma_unmap_single(&mpt_dev->pcidev->dev,
52062306a36Sopenharmony_ci					 priv->RcvCtl[i].dma,
52162306a36Sopenharmony_ci					 priv->RcvCtl[i].len, DMA_FROM_DEVICE);
52262306a36Sopenharmony_ci			dev_kfree_skb(priv->RcvCtl[i].skb);
52362306a36Sopenharmony_ci		}
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	kfree(priv->RcvCtl);
52762306a36Sopenharmony_ci	kfree(priv->mpt_rxfidx);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	for (i = 0; i < priv->tx_max_out; i++) {
53062306a36Sopenharmony_ci		if (priv->SendCtl[i].skb != NULL) {
53162306a36Sopenharmony_ci			dma_unmap_single(&mpt_dev->pcidev->dev,
53262306a36Sopenharmony_ci					 priv->SendCtl[i].dma,
53362306a36Sopenharmony_ci					 priv->SendCtl[i].len, DMA_TO_DEVICE);
53462306a36Sopenharmony_ci			dev_kfree_skb(priv->SendCtl[i].skb);
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	kfree(priv->SendCtl);
53962306a36Sopenharmony_ci	kfree(priv->mpt_txfidx);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	atomic_set(&priv->buckets_out, 0);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	printk(KERN_INFO MYNAM ": %s/%s: interface down & inactive\n",
54462306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev));
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
55062306a36Sopenharmony_ci/* Tx timeout handler. */
55162306a36Sopenharmony_cistatic void
55262306a36Sopenharmony_cimpt_lan_tx_timeout(struct net_device *dev, unsigned int txqueue)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
55562306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (mpt_dev->active) {
55862306a36Sopenharmony_ci		dlprintk (("mptlan/tx_timeout: calling netif_wake_queue for %s.\n", dev->name));
55962306a36Sopenharmony_ci		netif_wake_queue(dev);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
56462306a36Sopenharmony_ci//static inline int
56562306a36Sopenharmony_cistatic int
56662306a36Sopenharmony_cimpt_lan_send_turbo(struct net_device *dev, u32 tmsg)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
56962306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
57062306a36Sopenharmony_ci	struct sk_buff *sent;
57162306a36Sopenharmony_ci	unsigned long flags;
57262306a36Sopenharmony_ci	u32 ctx;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	ctx = GET_LAN_BUFFER_CONTEXT(tmsg);
57562306a36Sopenharmony_ci	sent = priv->SendCtl[ctx].skb;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	dev->stats.tx_packets++;
57862306a36Sopenharmony_ci	dev->stats.tx_bytes += sent->len;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n",
58162306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev),
58262306a36Sopenharmony_ci			__func__, sent));
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	priv->SendCtl[ctx].skb = NULL;
58562306a36Sopenharmony_ci	dma_unmap_single(&mpt_dev->pcidev->dev, priv->SendCtl[ctx].dma,
58662306a36Sopenharmony_ci			 priv->SendCtl[ctx].len, DMA_TO_DEVICE);
58762306a36Sopenharmony_ci	dev_kfree_skb_irq(sent);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->txfidx_lock, flags);
59062306a36Sopenharmony_ci	priv->mpt_txfidx[++priv->mpt_txfidx_tail] = ctx;
59162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->txfidx_lock, flags);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	netif_wake_queue(dev);
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
59862306a36Sopenharmony_cistatic int
59962306a36Sopenharmony_cimpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
60262306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
60362306a36Sopenharmony_ci	struct sk_buff *sent;
60462306a36Sopenharmony_ci	unsigned long flags;
60562306a36Sopenharmony_ci	int FreeReqFrame = 0;
60662306a36Sopenharmony_ci	u32 *pContext;
60762306a36Sopenharmony_ci	u32 ctx;
60862306a36Sopenharmony_ci	u8 count;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	count = pSendRep->NumberOfContexts;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": send_reply: IOCStatus: %04x\n",
61362306a36Sopenharmony_ci		 le16_to_cpu(pSendRep->IOCStatus)));
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* Add check for Loginfo Flag in IOCStatus */
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	switch (le16_to_cpu(pSendRep->IOCStatus) & MPI_IOCSTATUS_MASK) {
61862306a36Sopenharmony_ci	case MPI_IOCSTATUS_SUCCESS:
61962306a36Sopenharmony_ci		dev->stats.tx_packets += count;
62062306a36Sopenharmony_ci		break;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	case MPI_IOCSTATUS_LAN_CANCELED:
62362306a36Sopenharmony_ci	case MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED:
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	case MPI_IOCSTATUS_INVALID_SGL:
62762306a36Sopenharmony_ci		dev->stats.tx_errors += count;
62862306a36Sopenharmony_ci		printk (KERN_ERR MYNAM ": %s/%s: ERROR - Invalid SGL sent to IOC!\n",
62962306a36Sopenharmony_ci				IOC_AND_NETDEV_NAMES_s_s(dev));
63062306a36Sopenharmony_ci		goto out;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	default:
63362306a36Sopenharmony_ci		dev->stats.tx_errors += count;
63462306a36Sopenharmony_ci		break;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	pContext = &pSendRep->BufferContext;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->txfidx_lock, flags);
64062306a36Sopenharmony_ci	while (count > 0) {
64162306a36Sopenharmony_ci		ctx = GET_LAN_BUFFER_CONTEXT(le32_to_cpu(*pContext));
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		sent = priv->SendCtl[ctx].skb;
64462306a36Sopenharmony_ci		dev->stats.tx_bytes += sent->len;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n",
64762306a36Sopenharmony_ci				IOC_AND_NETDEV_NAMES_s_s(dev),
64862306a36Sopenharmony_ci				__func__, sent));
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		priv->SendCtl[ctx].skb = NULL;
65162306a36Sopenharmony_ci		dma_unmap_single(&mpt_dev->pcidev->dev,
65262306a36Sopenharmony_ci				 priv->SendCtl[ctx].dma,
65362306a36Sopenharmony_ci				 priv->SendCtl[ctx].len, DMA_TO_DEVICE);
65462306a36Sopenharmony_ci		dev_kfree_skb_irq(sent);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		priv->mpt_txfidx[++priv->mpt_txfidx_tail] = ctx;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		pContext++;
65962306a36Sopenharmony_ci		count--;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->txfidx_lock, flags);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ciout:
66462306a36Sopenharmony_ci	if (!(pSendRep->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY))
66562306a36Sopenharmony_ci		FreeReqFrame = 1;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	netif_wake_queue(dev);
66862306a36Sopenharmony_ci	return FreeReqFrame;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
67262306a36Sopenharmony_cistatic netdev_tx_t
67362306a36Sopenharmony_cimpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
67662306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
67762306a36Sopenharmony_ci	MPT_FRAME_HDR *mf;
67862306a36Sopenharmony_ci	LANSendRequest_t *pSendReq;
67962306a36Sopenharmony_ci	SGETransaction32_t *pTrans;
68062306a36Sopenharmony_ci	SGESimple64_t *pSimple;
68162306a36Sopenharmony_ci	const unsigned char *mac;
68262306a36Sopenharmony_ci	dma_addr_t dma;
68362306a36Sopenharmony_ci	unsigned long flags;
68462306a36Sopenharmony_ci	int ctx;
68562306a36Sopenharmony_ci	u16 cur_naa = 0x1000;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n",
68862306a36Sopenharmony_ci			__func__, skb));
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	spin_lock_irqsave(&priv->txfidx_lock, flags);
69162306a36Sopenharmony_ci	if (priv->mpt_txfidx_tail < 0) {
69262306a36Sopenharmony_ci		netif_stop_queue(dev);
69362306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->txfidx_lock, flags);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		printk (KERN_ERR "%s: no tx context available: %u\n",
69662306a36Sopenharmony_ci			__func__, priv->mpt_txfidx_tail);
69762306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	mf = mpt_get_msg_frame(LanCtx, mpt_dev);
70162306a36Sopenharmony_ci	if (mf == NULL) {
70262306a36Sopenharmony_ci		netif_stop_queue(dev);
70362306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->txfidx_lock, flags);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		printk (KERN_ERR "%s: Unable to alloc request frame\n",
70662306a36Sopenharmony_ci			__func__);
70762306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	ctx = priv->mpt_txfidx[priv->mpt_txfidx_tail--];
71162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->txfidx_lock, flags);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci//	dioprintk((KERN_INFO MYNAM ": %s/%s: Creating new msg frame (send).\n",
71462306a36Sopenharmony_ci//			IOC_AND_NETDEV_NAMES_s_s(dev)));
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	pSendReq = (LANSendRequest_t *) mf;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Set the mac.raw pointer, since this apparently isn't getting
71962306a36Sopenharmony_ci	 * done before we get the skb. Pull the data pointer past the mac data.
72062306a36Sopenharmony_ci	 */
72162306a36Sopenharmony_ci	skb_reset_mac_header(skb);
72262306a36Sopenharmony_ci	skb_pull(skb, 12);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	dma = dma_map_single(&mpt_dev->pcidev->dev, skb->data, skb->len,
72562306a36Sopenharmony_ci			     DMA_TO_DEVICE);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	priv->SendCtl[ctx].skb = skb;
72862306a36Sopenharmony_ci	priv->SendCtl[ctx].dma = dma;
72962306a36Sopenharmony_ci	priv->SendCtl[ctx].len = skb->len;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* Message Header */
73262306a36Sopenharmony_ci	pSendReq->Reserved    = 0;
73362306a36Sopenharmony_ci	pSendReq->Function    = MPI_FUNCTION_LAN_SEND;
73462306a36Sopenharmony_ci	pSendReq->ChainOffset = 0;
73562306a36Sopenharmony_ci	pSendReq->Reserved2   = 0;
73662306a36Sopenharmony_ci	pSendReq->MsgFlags    = 0;
73762306a36Sopenharmony_ci	pSendReq->PortNumber  = priv->pnum;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* Transaction Context Element */
74062306a36Sopenharmony_ci	pTrans = (SGETransaction32_t *) pSendReq->SG_List;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/* No Flags, 8 bytes of Details, 32bit Context (bloody turbo replies) */
74362306a36Sopenharmony_ci	pTrans->ContextSize   = sizeof(u32);
74462306a36Sopenharmony_ci	pTrans->DetailsLength = 2 * sizeof(u32);
74562306a36Sopenharmony_ci	pTrans->Flags         = 0;
74662306a36Sopenharmony_ci	pTrans->TransactionContext = cpu_to_le32(ctx);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci//	dioprintk((KERN_INFO MYNAM ": %s/%s: BC = %08x, skb = %p, buff = %p\n",
74962306a36Sopenharmony_ci//			IOC_AND_NETDEV_NAMES_s_s(dev),
75062306a36Sopenharmony_ci//			ctx, skb, skb->data));
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	mac = skb_mac_header(skb);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa         << 16) |
75562306a36Sopenharmony_ci						    (mac[0] <<  8) |
75662306a36Sopenharmony_ci						    (mac[1] <<  0));
75762306a36Sopenharmony_ci	pTrans->TransactionDetails[1] = cpu_to_le32((mac[2] << 24) |
75862306a36Sopenharmony_ci						    (mac[3] << 16) |
75962306a36Sopenharmony_ci						    (mac[4] <<  8) |
76062306a36Sopenharmony_ci						    (mac[5] <<  0));
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2];
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	/* If we ever decide to send more than one Simple SGE per LANSend, then
76562306a36Sopenharmony_ci	   we will need to make sure that LAST_ELEMENT only gets set on the
76662306a36Sopenharmony_ci	   last one. Otherwise, bad voodoo and evil funkiness will commence. */
76762306a36Sopenharmony_ci	pSimple->FlagsLength = cpu_to_le32(
76862306a36Sopenharmony_ci			((MPI_SGE_FLAGS_LAST_ELEMENT |
76962306a36Sopenharmony_ci			  MPI_SGE_FLAGS_END_OF_BUFFER |
77062306a36Sopenharmony_ci			  MPI_SGE_FLAGS_SIMPLE_ELEMENT |
77162306a36Sopenharmony_ci			  MPI_SGE_FLAGS_SYSTEM_ADDRESS |
77262306a36Sopenharmony_ci			  MPI_SGE_FLAGS_HOST_TO_IOC |
77362306a36Sopenharmony_ci			  MPI_SGE_FLAGS_64_BIT_ADDRESSING |
77462306a36Sopenharmony_ci			  MPI_SGE_FLAGS_END_OF_LIST) << MPI_SGE_FLAGS_SHIFT) |
77562306a36Sopenharmony_ci			skb->len);
77662306a36Sopenharmony_ci	pSimple->Address.Low = cpu_to_le32((u32) dma);
77762306a36Sopenharmony_ci	if (sizeof(dma_addr_t) > sizeof(u32))
77862306a36Sopenharmony_ci		pSimple->Address.High = cpu_to_le32((u32) ((u64) dma >> 32));
77962306a36Sopenharmony_ci	else
78062306a36Sopenharmony_ci		pSimple->Address.High = 0;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	mpt_put_msg_frame (LanCtx, mpt_dev, mf);
78362306a36Sopenharmony_ci	netif_trans_update(dev);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s/%s: Sending packet. FlagsLength = %08x.\n",
78662306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev),
78762306a36Sopenharmony_ci			le32_to_cpu(pSimple->FlagsLength)));
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	return NETDEV_TX_OK;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
79362306a36Sopenharmony_cistatic void
79462306a36Sopenharmony_cimpt_lan_wake_post_buckets_task(struct net_device *dev, int priority)
79562306a36Sopenharmony_ci/*
79662306a36Sopenharmony_ci * @priority: 0 = put it on the timer queue, 1 = put it on the immediate queue
79762306a36Sopenharmony_ci */
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (test_and_set_bit(0, &priv->post_buckets_active) == 0) {
80262306a36Sopenharmony_ci		if (priority) {
80362306a36Sopenharmony_ci			schedule_delayed_work(&priv->post_buckets_task, 0);
80462306a36Sopenharmony_ci		} else {
80562306a36Sopenharmony_ci			schedule_delayed_work(&priv->post_buckets_task, 1);
80662306a36Sopenharmony_ci			dioprintk((KERN_INFO MYNAM ": post_buckets queued on "
80762306a36Sopenharmony_ci				   "timer.\n"));
80862306a36Sopenharmony_ci		}
80962306a36Sopenharmony_ci	        dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n",
81062306a36Sopenharmony_ci			   IOC_AND_NETDEV_NAMES_s_s(dev) ));
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
81562306a36Sopenharmony_cistatic int
81662306a36Sopenharmony_cimpt_lan_receive_skb(struct net_device *dev, struct sk_buff *skb)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	skb->protocol = mpt_lan_type_trans(skb, dev);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s/%s: Incoming packet (%d bytes) "
82362306a36Sopenharmony_ci		 "delivered to upper level.\n",
82462306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev), skb->len));
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	dev->stats.rx_bytes += skb->len;
82762306a36Sopenharmony_ci	dev->stats.rx_packets++;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	skb->dev = dev;
83062306a36Sopenharmony_ci	netif_rx(skb);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	dioprintk((MYNAM "/receive_skb: %d buckets remaining\n",
83362306a36Sopenharmony_ci		 atomic_read(&priv->buckets_out)));
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (atomic_read(&priv->buckets_out) < priv->bucketthresh)
83662306a36Sopenharmony_ci		mpt_lan_wake_post_buckets_task(dev, 1);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM "/receive_post_reply: %d buckets "
83962306a36Sopenharmony_ci		  "remaining, %d received back since sod\n",
84062306a36Sopenharmony_ci		  atomic_read(&priv->buckets_out), priv->total_received));
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	return 0;
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
84662306a36Sopenharmony_ci//static inline int
84762306a36Sopenharmony_cistatic int
84862306a36Sopenharmony_cimpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
85162306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
85262306a36Sopenharmony_ci	struct sk_buff *skb, *old_skb;
85362306a36Sopenharmony_ci	unsigned long flags;
85462306a36Sopenharmony_ci	u32 ctx, len;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	ctx = GET_LAN_BUCKET_CONTEXT(tmsg);
85762306a36Sopenharmony_ci	skb = priv->RcvCtl[ctx].skb;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	len = GET_LAN_PACKET_LENGTH(tmsg);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (len < MPT_LAN_RX_COPYBREAK) {
86262306a36Sopenharmony_ci		old_skb = skb;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		skb = (struct sk_buff *)dev_alloc_skb(len);
86562306a36Sopenharmony_ci		if (!skb) {
86662306a36Sopenharmony_ci			printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
86762306a36Sopenharmony_ci					IOC_AND_NETDEV_NAMES_s_s(dev),
86862306a36Sopenharmony_ci					__FILE__, __LINE__);
86962306a36Sopenharmony_ci			return -ENOMEM;
87062306a36Sopenharmony_ci		}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		dma_sync_single_for_cpu(&mpt_dev->pcidev->dev,
87362306a36Sopenharmony_ci					priv->RcvCtl[ctx].dma,
87462306a36Sopenharmony_ci					priv->RcvCtl[ctx].len,
87562306a36Sopenharmony_ci					DMA_FROM_DEVICE);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci		skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		dma_sync_single_for_device(&mpt_dev->pcidev->dev,
88062306a36Sopenharmony_ci					   priv->RcvCtl[ctx].dma,
88162306a36Sopenharmony_ci					   priv->RcvCtl[ctx].len,
88262306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
88362306a36Sopenharmony_ci		goto out;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	skb_put(skb, len);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	priv->RcvCtl[ctx].skb = NULL;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	dma_unmap_single(&mpt_dev->pcidev->dev, priv->RcvCtl[ctx].dma,
89162306a36Sopenharmony_ci			 priv->RcvCtl[ctx].len, DMA_FROM_DEVICE);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ciout:
89462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->rxfidx_lock, flags);
89562306a36Sopenharmony_ci	priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
89662306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	atomic_dec(&priv->buckets_out);
89962306a36Sopenharmony_ci	priv->total_received++;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	return mpt_lan_receive_skb(dev, skb);
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
90562306a36Sopenharmony_cistatic int
90662306a36Sopenharmony_cimpt_lan_receive_post_free(struct net_device *dev,
90762306a36Sopenharmony_ci			  LANReceivePostReply_t *pRecvRep)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
91062306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
91162306a36Sopenharmony_ci	unsigned long flags;
91262306a36Sopenharmony_ci	struct sk_buff *skb;
91362306a36Sopenharmony_ci	u32 ctx;
91462306a36Sopenharmony_ci	int count;
91562306a36Sopenharmony_ci	int i;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	count = pRecvRep->NumberOfContexts;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci/**/	dlprintk((KERN_INFO MYNAM "/receive_post_reply: "
92062306a36Sopenharmony_ci		  "IOC returned %d buckets, freeing them...\n", count));
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->rxfidx_lock, flags);
92362306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
92462306a36Sopenharmony_ci		ctx = le32_to_cpu(pRecvRep->BucketContext[i]);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		skb = priv->RcvCtl[ctx].skb;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci//		dlprintk((KERN_INFO MYNAM ": %s: dev_name = %s\n",
92962306a36Sopenharmony_ci//				IOC_AND_NETDEV_NAMES_s_s(dev)));
93062306a36Sopenharmony_ci//		dlprintk((KERN_INFO MYNAM "@rpr[2], priv = %p, buckets_out addr = %p",
93162306a36Sopenharmony_ci//				priv, &(priv->buckets_out)));
93262306a36Sopenharmony_ci//		dlprintk((KERN_INFO MYNAM "@rpr[2] TC + 3\n"));
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		priv->RcvCtl[ctx].skb = NULL;
93562306a36Sopenharmony_ci		dma_unmap_single(&mpt_dev->pcidev->dev, priv->RcvCtl[ctx].dma,
93662306a36Sopenharmony_ci				 priv->RcvCtl[ctx].len, DMA_FROM_DEVICE);
93762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	atomic_sub(count, &priv->buckets_out);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci//	for (i = 0; i < priv->max_buckets_out; i++)
94662306a36Sopenharmony_ci//		if (priv->RcvCtl[i].skb != NULL)
94762306a36Sopenharmony_ci//			dlprintk((KERN_INFO MYNAM "@rpr: bucket %03x "
94862306a36Sopenharmony_ci//				  "is still out\n", i));
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci/*	dlprintk((KERN_INFO MYNAM "/receive_post_reply: freed %d buckets\n",
95162306a36Sopenharmony_ci		  count));
95262306a36Sopenharmony_ci*/
95362306a36Sopenharmony_ci/**/	dlprintk((KERN_INFO MYNAM "@receive_post_reply: %d buckets "
95462306a36Sopenharmony_ci/**/		  "remaining, %d received back since sod.\n",
95562306a36Sopenharmony_ci/**/		  atomic_read(&priv->buckets_out), priv->total_received));
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
96062306a36Sopenharmony_cistatic int
96162306a36Sopenharmony_cimpt_lan_receive_post_reply(struct net_device *dev,
96262306a36Sopenharmony_ci			   LANReceivePostReply_t *pRecvRep)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
96562306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
96662306a36Sopenharmony_ci	struct sk_buff *skb, *old_skb;
96762306a36Sopenharmony_ci	unsigned long flags;
96862306a36Sopenharmony_ci	u32 len, ctx, offset;
96962306a36Sopenharmony_ci	u32 remaining = le32_to_cpu(pRecvRep->BucketsRemaining);
97062306a36Sopenharmony_ci	int count;
97162306a36Sopenharmony_ci	int i, l;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": mpt_lan_receive_post_reply called\n"));
97462306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": receive_post_reply: IOCStatus: %04x\n",
97562306a36Sopenharmony_ci		 le16_to_cpu(pRecvRep->IOCStatus)));
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if ((le16_to_cpu(pRecvRep->IOCStatus) & MPI_IOCSTATUS_MASK) ==
97862306a36Sopenharmony_ci						MPI_IOCSTATUS_LAN_CANCELED)
97962306a36Sopenharmony_ci		return mpt_lan_receive_post_free(dev, pRecvRep);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	len = le32_to_cpu(pRecvRep->PacketLength);
98262306a36Sopenharmony_ci	if (len == 0) {
98362306a36Sopenharmony_ci		printk (KERN_ERR MYNAM ": %s/%s: ERROR - Got a non-TURBO "
98462306a36Sopenharmony_ci			"ReceivePostReply w/ PacketLength zero!\n",
98562306a36Sopenharmony_ci				IOC_AND_NETDEV_NAMES_s_s(dev));
98662306a36Sopenharmony_ci		printk (KERN_ERR MYNAM ": MsgFlags = %02x, IOCStatus = %04x\n",
98762306a36Sopenharmony_ci				pRecvRep->MsgFlags, le16_to_cpu(pRecvRep->IOCStatus));
98862306a36Sopenharmony_ci		return -1;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	ctx    = le32_to_cpu(pRecvRep->BucketContext[0]);
99262306a36Sopenharmony_ci	count  = pRecvRep->NumberOfContexts;
99362306a36Sopenharmony_ci	skb    = priv->RcvCtl[ctx].skb;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	offset = le32_to_cpu(pRecvRep->PacketOffset);
99662306a36Sopenharmony_ci//	if (offset != 0) {
99762306a36Sopenharmony_ci//		printk (KERN_INFO MYNAM ": %s/%s: Got a ReceivePostReply "
99862306a36Sopenharmony_ci//			"w/ PacketOffset %u\n",
99962306a36Sopenharmony_ci//				IOC_AND_NETDEV_NAMES_s_s(dev),
100062306a36Sopenharmony_ci//				offset);
100162306a36Sopenharmony_ci//	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s/%s: @rpr, offset = %d, len = %d\n",
100462306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev),
100562306a36Sopenharmony_ci			offset, len));
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (count > 1) {
100862306a36Sopenharmony_ci		int szrem = len;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci//		dioprintk((KERN_INFO MYNAM ": %s/%s: Multiple buckets returned "
101162306a36Sopenharmony_ci//			"for single packet, concatenating...\n",
101262306a36Sopenharmony_ci//				IOC_AND_NETDEV_NAMES_s_s(dev)));
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		skb = (struct sk_buff *)dev_alloc_skb(len);
101562306a36Sopenharmony_ci		if (!skb) {
101662306a36Sopenharmony_ci			printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
101762306a36Sopenharmony_ci					IOC_AND_NETDEV_NAMES_s_s(dev),
101862306a36Sopenharmony_ci					__FILE__, __LINE__);
101962306a36Sopenharmony_ci			return -ENOMEM;
102062306a36Sopenharmony_ci		}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		spin_lock_irqsave(&priv->rxfidx_lock, flags);
102362306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci			ctx = le32_to_cpu(pRecvRep->BucketContext[i]);
102662306a36Sopenharmony_ci			old_skb = priv->RcvCtl[ctx].skb;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci			l = priv->RcvCtl[ctx].len;
102962306a36Sopenharmony_ci			if (szrem < l)
103062306a36Sopenharmony_ci				l = szrem;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci//			dioprintk((KERN_INFO MYNAM ": %s/%s: Buckets = %d, len = %u\n",
103362306a36Sopenharmony_ci//					IOC_AND_NETDEV_NAMES_s_s(dev),
103462306a36Sopenharmony_ci//					i, l));
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci			dma_sync_single_for_cpu(&mpt_dev->pcidev->dev,
103762306a36Sopenharmony_ci						priv->RcvCtl[ctx].dma,
103862306a36Sopenharmony_ci						priv->RcvCtl[ctx].len,
103962306a36Sopenharmony_ci						DMA_FROM_DEVICE);
104062306a36Sopenharmony_ci			skb_copy_from_linear_data(old_skb, skb_put(skb, l), l);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci			dma_sync_single_for_device(&mpt_dev->pcidev->dev,
104362306a36Sopenharmony_ci						   priv->RcvCtl[ctx].dma,
104462306a36Sopenharmony_ci						   priv->RcvCtl[ctx].len,
104562306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci			priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
104862306a36Sopenharmony_ci			szrem -= l;
104962306a36Sopenharmony_ci		}
105062306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	} else if (len < MPT_LAN_RX_COPYBREAK) {
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		old_skb = skb;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		skb = (struct sk_buff *)dev_alloc_skb(len);
105762306a36Sopenharmony_ci		if (!skb) {
105862306a36Sopenharmony_ci			printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
105962306a36Sopenharmony_ci					IOC_AND_NETDEV_NAMES_s_s(dev),
106062306a36Sopenharmony_ci					__FILE__, __LINE__);
106162306a36Sopenharmony_ci			return -ENOMEM;
106262306a36Sopenharmony_ci		}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		dma_sync_single_for_cpu(&mpt_dev->pcidev->dev,
106562306a36Sopenharmony_ci					priv->RcvCtl[ctx].dma,
106662306a36Sopenharmony_ci					priv->RcvCtl[ctx].len,
106762306a36Sopenharmony_ci					DMA_FROM_DEVICE);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci		skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		dma_sync_single_for_device(&mpt_dev->pcidev->dev,
107262306a36Sopenharmony_ci					   priv->RcvCtl[ctx].dma,
107362306a36Sopenharmony_ci					   priv->RcvCtl[ctx].len,
107462306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		spin_lock_irqsave(&priv->rxfidx_lock, flags);
107762306a36Sopenharmony_ci		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
107862306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	} else {
108162306a36Sopenharmony_ci		spin_lock_irqsave(&priv->rxfidx_lock, flags);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci		priv->RcvCtl[ctx].skb = NULL;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		dma_unmap_single(&mpt_dev->pcidev->dev, priv->RcvCtl[ctx].dma,
108662306a36Sopenharmony_ci				 priv->RcvCtl[ctx].len, DMA_FROM_DEVICE);
108762306a36Sopenharmony_ci		priv->RcvCtl[ctx].dma = 0;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
109062306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		skb_put(skb,len);
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	atomic_sub(count, &priv->buckets_out);
109662306a36Sopenharmony_ci	priv->total_received += count;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (priv->mpt_rxfidx_tail >= MPT_LAN_MAX_BUCKETS_OUT) {
109962306a36Sopenharmony_ci		printk (KERN_ERR MYNAM ": %s/%s: Yoohoo! mpt_rxfidx_tail = %d, "
110062306a36Sopenharmony_ci			"MPT_LAN_MAX_BUCKETS_OUT = %d\n",
110162306a36Sopenharmony_ci				IOC_AND_NETDEV_NAMES_s_s(dev),
110262306a36Sopenharmony_ci				priv->mpt_rxfidx_tail,
110362306a36Sopenharmony_ci				MPT_LAN_MAX_BUCKETS_OUT);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		return -1;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (remaining == 0)
110962306a36Sopenharmony_ci		printk (KERN_WARNING MYNAM ": %s/%s: WARNING - IOC out of buckets! "
111062306a36Sopenharmony_ci			"(priv->buckets_out = %d)\n",
111162306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev),
111262306a36Sopenharmony_ci			atomic_read(&priv->buckets_out));
111362306a36Sopenharmony_ci	else if (remaining < 10)
111462306a36Sopenharmony_ci		printk (KERN_INFO MYNAM ": %s/%s: IOC says %d buckets left. "
111562306a36Sopenharmony_ci			"(priv->buckets_out = %d)\n",
111662306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev),
111762306a36Sopenharmony_ci			remaining, atomic_read(&priv->buckets_out));
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if ((remaining < priv->bucketthresh) &&
112062306a36Sopenharmony_ci	    ((atomic_read(&priv->buckets_out) - remaining) >
112162306a36Sopenharmony_ci	     MPT_LAN_BUCKETS_REMAIN_MISMATCH_THRESH)) {
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		printk (KERN_WARNING MYNAM " Mismatch between driver's "
112462306a36Sopenharmony_ci			"buckets_out count and fw's BucketsRemaining "
112562306a36Sopenharmony_ci			"count has crossed the threshold, issuing a "
112662306a36Sopenharmony_ci			"LanReset to clear the fw's hashtable. You may "
112762306a36Sopenharmony_ci			"want to check your /var/log/messages for \"CRC "
112862306a36Sopenharmony_ci			"error\" event notifications.\n");
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci		mpt_lan_reset(dev);
113162306a36Sopenharmony_ci		mpt_lan_wake_post_buckets_task(dev, 0);
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	return mpt_lan_receive_skb(dev, skb);
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
113862306a36Sopenharmony_ci/* Simple SGE's only at the moment */
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic void
114162306a36Sopenharmony_cimpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	struct net_device *dev = priv->dev;
114462306a36Sopenharmony_ci	MPT_ADAPTER *mpt_dev = priv->mpt_dev;
114562306a36Sopenharmony_ci	MPT_FRAME_HDR *mf;
114662306a36Sopenharmony_ci	LANReceivePostRequest_t *pRecvReq;
114762306a36Sopenharmony_ci	SGETransaction32_t *pTrans;
114862306a36Sopenharmony_ci	SGESimple64_t *pSimple;
114962306a36Sopenharmony_ci	struct sk_buff *skb;
115062306a36Sopenharmony_ci	dma_addr_t dma;
115162306a36Sopenharmony_ci	u32 curr, buckets, count, max;
115262306a36Sopenharmony_ci	u32 len = (dev->mtu + dev->hard_header_len + 4);
115362306a36Sopenharmony_ci	unsigned long flags;
115462306a36Sopenharmony_ci	int i;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	curr = atomic_read(&priv->buckets_out);
115762306a36Sopenharmony_ci	buckets = (priv->max_buckets_out - curr);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, Start_buckets = %u, buckets_out = %u\n",
116062306a36Sopenharmony_ci			IOC_AND_NETDEV_NAMES_s_s(dev),
116162306a36Sopenharmony_ci			__func__, buckets, curr));
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) /
116462306a36Sopenharmony_ci			(sizeof(SGETransaction32_t) + sizeof(SGESimple64_t));
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	while (buckets) {
116762306a36Sopenharmony_ci		mf = mpt_get_msg_frame(LanCtx, mpt_dev);
116862306a36Sopenharmony_ci		if (mf == NULL) {
116962306a36Sopenharmony_ci			printk (KERN_ERR "%s: Unable to alloc request frame\n",
117062306a36Sopenharmony_ci				__func__);
117162306a36Sopenharmony_ci			dioprintk((KERN_ERR "%s: %u buckets remaining\n",
117262306a36Sopenharmony_ci				 __func__, buckets));
117362306a36Sopenharmony_ci			goto out;
117462306a36Sopenharmony_ci		}
117562306a36Sopenharmony_ci		pRecvReq = (LANReceivePostRequest_t *) mf;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		i = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
117862306a36Sopenharmony_ci		mpt_dev->RequestNB[i] = 0;
117962306a36Sopenharmony_ci		count = buckets;
118062306a36Sopenharmony_ci		if (count > max)
118162306a36Sopenharmony_ci			count = max;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci		pRecvReq->Function    = MPI_FUNCTION_LAN_RECEIVE;
118462306a36Sopenharmony_ci		pRecvReq->ChainOffset = 0;
118562306a36Sopenharmony_ci		pRecvReq->MsgFlags    = 0;
118662306a36Sopenharmony_ci		pRecvReq->PortNumber  = priv->pnum;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		pTrans = (SGETransaction32_t *) pRecvReq->SG_List;
118962306a36Sopenharmony_ci		pSimple = NULL;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
119262306a36Sopenharmony_ci			int ctx;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci			spin_lock_irqsave(&priv->rxfidx_lock, flags);
119562306a36Sopenharmony_ci			if (priv->mpt_rxfidx_tail < 0) {
119662306a36Sopenharmony_ci				printk (KERN_ERR "%s: Can't alloc context\n",
119762306a36Sopenharmony_ci					__func__);
119862306a36Sopenharmony_ci				spin_unlock_irqrestore(&priv->rxfidx_lock,
119962306a36Sopenharmony_ci						       flags);
120062306a36Sopenharmony_ci				break;
120162306a36Sopenharmony_ci			}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci			ctx = priv->mpt_rxfidx[priv->mpt_rxfidx_tail--];
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci			skb = priv->RcvCtl[ctx].skb;
120662306a36Sopenharmony_ci			if (skb && (priv->RcvCtl[ctx].len != len)) {
120762306a36Sopenharmony_ci				dma_unmap_single(&mpt_dev->pcidev->dev,
120862306a36Sopenharmony_ci						 priv->RcvCtl[ctx].dma,
120962306a36Sopenharmony_ci						 priv->RcvCtl[ctx].len,
121062306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
121162306a36Sopenharmony_ci				dev_kfree_skb(priv->RcvCtl[ctx].skb);
121262306a36Sopenharmony_ci				skb = priv->RcvCtl[ctx].skb = NULL;
121362306a36Sopenharmony_ci			}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci			if (skb == NULL) {
121662306a36Sopenharmony_ci				skb = dev_alloc_skb(len);
121762306a36Sopenharmony_ci				if (skb == NULL) {
121862306a36Sopenharmony_ci					printk (KERN_WARNING
121962306a36Sopenharmony_ci						MYNAM "/%s: Can't alloc skb\n",
122062306a36Sopenharmony_ci						__func__);
122162306a36Sopenharmony_ci					priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
122262306a36Sopenharmony_ci					spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
122362306a36Sopenharmony_ci					break;
122462306a36Sopenharmony_ci				}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci				dma = dma_map_single(&mpt_dev->pcidev->dev,
122762306a36Sopenharmony_ci						     skb->data, len,
122862306a36Sopenharmony_ci						     DMA_FROM_DEVICE);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci				priv->RcvCtl[ctx].skb = skb;
123162306a36Sopenharmony_ci				priv->RcvCtl[ctx].dma = dma;
123262306a36Sopenharmony_ci				priv->RcvCtl[ctx].len = len;
123362306a36Sopenharmony_ci			}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci			spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci			pTrans->ContextSize   = sizeof(u32);
123862306a36Sopenharmony_ci			pTrans->DetailsLength = 0;
123962306a36Sopenharmony_ci			pTrans->Flags         = 0;
124062306a36Sopenharmony_ci			pTrans->TransactionContext = cpu_to_le32(ctx);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci			pSimple = (SGESimple64_t *) pTrans->TransactionDetails;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci			pSimple->FlagsLength = cpu_to_le32(
124562306a36Sopenharmony_ci				((MPI_SGE_FLAGS_END_OF_BUFFER |
124662306a36Sopenharmony_ci				  MPI_SGE_FLAGS_SIMPLE_ELEMENT |
124762306a36Sopenharmony_ci				  MPI_SGE_FLAGS_64_BIT_ADDRESSING) << MPI_SGE_FLAGS_SHIFT) | len);
124862306a36Sopenharmony_ci			pSimple->Address.Low = cpu_to_le32((u32) priv->RcvCtl[ctx].dma);
124962306a36Sopenharmony_ci			if (sizeof(dma_addr_t) > sizeof(u32))
125062306a36Sopenharmony_ci				pSimple->Address.High = cpu_to_le32((u32) ((u64) priv->RcvCtl[ctx].dma >> 32));
125162306a36Sopenharmony_ci			else
125262306a36Sopenharmony_ci				pSimple->Address.High = 0;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci			pTrans = (SGETransaction32_t *) (pSimple + 1);
125562306a36Sopenharmony_ci		}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		if (pSimple == NULL) {
125862306a36Sopenharmony_ci/**/			printk (KERN_WARNING MYNAM "/%s: No buckets posted\n",
125962306a36Sopenharmony_ci/**/				__func__);
126062306a36Sopenharmony_ci			mpt_free_msg_frame(mpt_dev, mf);
126162306a36Sopenharmony_ci			goto out;
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci		pSimple->FlagsLength |= cpu_to_le32(MPI_SGE_FLAGS_END_OF_LIST << MPI_SGE_FLAGS_SHIFT);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci		pRecvReq->BucketCount = cpu_to_le32(i);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci/*	printk(KERN_INFO MYNAM ": posting buckets\n   ");
126962306a36Sopenharmony_ci *	for (i = 0; i < j + 2; i ++)
127062306a36Sopenharmony_ci *	    printk (" %08x", le32_to_cpu(msg[i]));
127162306a36Sopenharmony_ci *	printk ("\n");
127262306a36Sopenharmony_ci */
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci		mpt_put_msg_frame(LanCtx, mpt_dev, mf);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		priv->total_posted += i;
127762306a36Sopenharmony_ci		buckets -= i;
127862306a36Sopenharmony_ci		atomic_add(i, &priv->buckets_out);
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ciout:
128262306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n",
128362306a36Sopenharmony_ci		  __func__, buckets, atomic_read(&priv->buckets_out)));
128462306a36Sopenharmony_ci	dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n",
128562306a36Sopenharmony_ci	__func__, priv->total_posted, priv->total_received));
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	clear_bit(0, &priv->post_buckets_active);
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic void
129162306a36Sopenharmony_cimpt_lan_post_receive_buckets_work(struct work_struct *work)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	mpt_lan_post_receive_buckets(container_of(work, struct mpt_lan_priv,
129462306a36Sopenharmony_ci						  post_buckets_task.work));
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_cistatic const struct net_device_ops mpt_netdev_ops = {
129862306a36Sopenharmony_ci	.ndo_open       = mpt_lan_open,
129962306a36Sopenharmony_ci	.ndo_stop       = mpt_lan_close,
130062306a36Sopenharmony_ci	.ndo_start_xmit = mpt_lan_sdu_send,
130162306a36Sopenharmony_ci	.ndo_tx_timeout = mpt_lan_tx_timeout,
130262306a36Sopenharmony_ci};
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
130562306a36Sopenharmony_cistatic struct net_device *
130662306a36Sopenharmony_cimpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	struct net_device *dev;
130962306a36Sopenharmony_ci	struct mpt_lan_priv *priv;
131062306a36Sopenharmony_ci	u8 HWaddr[FC_ALEN], *a;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	dev = alloc_fcdev(sizeof(struct mpt_lan_priv));
131362306a36Sopenharmony_ci	if (!dev)
131462306a36Sopenharmony_ci		return NULL;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	dev->mtu = MPT_LAN_MTU;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	priv = netdev_priv(dev);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	priv->dev = dev;
132162306a36Sopenharmony_ci	priv->mpt_dev = mpt_dev;
132262306a36Sopenharmony_ci	priv->pnum = pnum;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->post_buckets_task,
132562306a36Sopenharmony_ci			  mpt_lan_post_receive_buckets_work);
132662306a36Sopenharmony_ci	priv->post_buckets_active = 0;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n",
132962306a36Sopenharmony_ci			__LINE__, dev->mtu + dev->hard_header_len + 4));
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	atomic_set(&priv->buckets_out, 0);
133262306a36Sopenharmony_ci	priv->total_posted = 0;
133362306a36Sopenharmony_ci	priv->total_received = 0;
133462306a36Sopenharmony_ci	priv->max_buckets_out = max_buckets_out;
133562306a36Sopenharmony_ci	if (mpt_dev->pfacts[0].MaxLanBuckets < max_buckets_out)
133662306a36Sopenharmony_ci		priv->max_buckets_out = mpt_dev->pfacts[0].MaxLanBuckets;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM "@%d: MaxLanBuckets=%d, max_buckets_out/priv=%d/%d\n",
133962306a36Sopenharmony_ci			__LINE__,
134062306a36Sopenharmony_ci			mpt_dev->pfacts[0].MaxLanBuckets,
134162306a36Sopenharmony_ci			max_buckets_out,
134262306a36Sopenharmony_ci			priv->max_buckets_out));
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	priv->bucketthresh = priv->max_buckets_out * 2 / 3;
134562306a36Sopenharmony_ci	spin_lock_init(&priv->txfidx_lock);
134662306a36Sopenharmony_ci	spin_lock_init(&priv->rxfidx_lock);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	/*  Grab pre-fetched LANPage1 stuff. :-) */
134962306a36Sopenharmony_ci	a = (u8 *) &mpt_dev->lan_cnfg_page1.HardwareAddressLow;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	HWaddr[0] = a[5];
135262306a36Sopenharmony_ci	HWaddr[1] = a[4];
135362306a36Sopenharmony_ci	HWaddr[2] = a[3];
135462306a36Sopenharmony_ci	HWaddr[3] = a[2];
135562306a36Sopenharmony_ci	HWaddr[4] = a[1];
135662306a36Sopenharmony_ci	HWaddr[5] = a[0];
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	dev->addr_len = FC_ALEN;
135962306a36Sopenharmony_ci	dev_addr_set(dev, HWaddr);
136062306a36Sopenharmony_ci	memset(dev->broadcast, 0xff, FC_ALEN);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	/* The Tx queue is 127 deep on the 909.
136362306a36Sopenharmony_ci	 * Give ourselves some breathing room.
136462306a36Sopenharmony_ci	 */
136562306a36Sopenharmony_ci	priv->tx_max_out = (tx_max_out_p <= MPT_TX_MAX_OUT_LIM) ?
136662306a36Sopenharmony_ci			    tx_max_out_p : MPT_TX_MAX_OUT_LIM;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	dev->netdev_ops = &mpt_netdev_ops;
136962306a36Sopenharmony_ci	dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* MTU range: 96 - 65280 */
137262306a36Sopenharmony_ci	dev->min_mtu = MPT_LAN_MIN_MTU;
137362306a36Sopenharmony_ci	dev->max_mtu = MPT_LAN_MAX_MTU;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ": Finished registering dev "
137662306a36Sopenharmony_ci		"and setting initial values\n"));
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	if (register_netdev(dev) != 0) {
137962306a36Sopenharmony_ci		free_netdev(dev);
138062306a36Sopenharmony_ci		dev = NULL;
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci	return dev;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic int
138662306a36Sopenharmony_cimptlan_probe(struct pci_dev *pdev)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev);
138962306a36Sopenharmony_ci	struct net_device	*dev;
139062306a36Sopenharmony_ci	int			i;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	for (i = 0; i < ioc->facts.NumberOfPorts; i++) {
139362306a36Sopenharmony_ci		printk(KERN_INFO MYNAM ": %s: PortNum=%x, "
139462306a36Sopenharmony_ci		       "ProtocolFlags=%02Xh (%c%c%c%c)\n",
139562306a36Sopenharmony_ci		       ioc->name, ioc->pfacts[i].PortNumber,
139662306a36Sopenharmony_ci		       ioc->pfacts[i].ProtocolFlags,
139762306a36Sopenharmony_ci		       MPT_PROTOCOL_FLAGS_c_c_c_c(
139862306a36Sopenharmony_ci			       ioc->pfacts[i].ProtocolFlags));
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		if (!(ioc->pfacts[i].ProtocolFlags &
140162306a36Sopenharmony_ci					MPI_PORTFACTS_PROTOCOL_LAN)) {
140262306a36Sopenharmony_ci			printk(KERN_INFO MYNAM ": %s: Hmmm... LAN protocol "
140362306a36Sopenharmony_ci			       "seems to be disabled on this adapter port!\n",
140462306a36Sopenharmony_ci			       ioc->name);
140562306a36Sopenharmony_ci			continue;
140662306a36Sopenharmony_ci		}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		dev = mpt_register_lan_device(ioc, i);
140962306a36Sopenharmony_ci		if (!dev) {
141062306a36Sopenharmony_ci			printk(KERN_ERR MYNAM ": %s: Unable to register "
141162306a36Sopenharmony_ci			       "port%d as a LAN device\n", ioc->name,
141262306a36Sopenharmony_ci			       ioc->pfacts[i].PortNumber);
141362306a36Sopenharmony_ci			continue;
141462306a36Sopenharmony_ci		}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci		printk(KERN_INFO MYNAM ": %s: Fusion MPT LAN device "
141762306a36Sopenharmony_ci		       "registered as '%s'\n", ioc->name, dev->name);
141862306a36Sopenharmony_ci		printk(KERN_INFO MYNAM ": %s/%s: "
141962306a36Sopenharmony_ci		       "LanAddr = %pM\n",
142062306a36Sopenharmony_ci		       IOC_AND_NETDEV_NAMES_s_s(dev),
142162306a36Sopenharmony_ci		       dev->dev_addr);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		ioc->netdev = dev;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci		return 0;
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	return -ENODEV;
142962306a36Sopenharmony_ci}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic void
143262306a36Sopenharmony_cimptlan_remove(struct pci_dev *pdev)
143362306a36Sopenharmony_ci{
143462306a36Sopenharmony_ci	MPT_ADAPTER 		*ioc = pci_get_drvdata(pdev);
143562306a36Sopenharmony_ci	struct net_device	*dev = ioc->netdev;
143662306a36Sopenharmony_ci	struct mpt_lan_priv *priv = netdev_priv(dev);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->post_buckets_task);
143962306a36Sopenharmony_ci	if(dev != NULL) {
144062306a36Sopenharmony_ci		unregister_netdev(dev);
144162306a36Sopenharmony_ci		free_netdev(dev);
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_cistatic struct mpt_pci_driver mptlan_driver = {
144662306a36Sopenharmony_ci	.probe		= mptlan_probe,
144762306a36Sopenharmony_ci	.remove		= mptlan_remove,
144862306a36Sopenharmony_ci};
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_cistatic int __init mpt_lan_init (void)
145162306a36Sopenharmony_ci{
145262306a36Sopenharmony_ci	show_mptmod_ver(LANAME, LANVER);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	LanCtx = mpt_register(lan_reply, MPTLAN_DRIVER,
145562306a36Sopenharmony_ci				"lan_reply");
145662306a36Sopenharmony_ci	if (LanCtx <= 0) {
145762306a36Sopenharmony_ci		printk (KERN_ERR MYNAM ": Failed to register with MPT base driver\n");
145862306a36Sopenharmony_ci		return -EBUSY;
145962306a36Sopenharmony_ci	}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ": assigned context of %d\n", LanCtx));
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (mpt_reset_register(LanCtx, mpt_lan_ioc_reset)) {
146462306a36Sopenharmony_ci		printk(KERN_ERR MYNAM ": Eieee! unable to register a reset "
146562306a36Sopenharmony_ci		       "handler with mptbase! The world is at an end! "
146662306a36Sopenharmony_ci		       "Everything is fading to black! Goodbye.\n");
146762306a36Sopenharmony_ci		return -EBUSY;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	dlprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	mpt_device_driver_register(&mptlan_driver, MPTLAN_DRIVER);
147362306a36Sopenharmony_ci	return 0;
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_cistatic void __exit mpt_lan_exit(void)
147762306a36Sopenharmony_ci{
147862306a36Sopenharmony_ci	mpt_device_driver_deregister(MPTLAN_DRIVER);
147962306a36Sopenharmony_ci	mpt_reset_deregister(LanCtx);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (LanCtx) {
148262306a36Sopenharmony_ci		mpt_deregister(LanCtx);
148362306a36Sopenharmony_ci		LanCtx = MPT_MAX_PROTOCOL_DRIVERS;
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cimodule_init(mpt_lan_init);
148862306a36Sopenharmony_cimodule_exit(mpt_lan_exit);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
149162306a36Sopenharmony_cistatic unsigned short
149262306a36Sopenharmony_cimpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data;
149562306a36Sopenharmony_ci	struct fcllc *fcllc;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	skb_reset_mac_header(skb);
149862306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct mpt_lan_ohdr));
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (fch->dtype == htons(0xffff)) {
150162306a36Sopenharmony_ci		u32 *p = (u32 *) fch;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci		swab32s(p + 0);
150462306a36Sopenharmony_ci		swab32s(p + 1);
150562306a36Sopenharmony_ci		swab32s(p + 2);
150662306a36Sopenharmony_ci		swab32s(p + 3);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci		printk (KERN_WARNING MYNAM ": %s: WARNING - Broadcast swap F/W bug detected!\n",
150962306a36Sopenharmony_ci				NETDEV_PTR_TO_IOC_NAME_s(dev));
151062306a36Sopenharmony_ci		printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %pM\n",
151162306a36Sopenharmony_ci				fch->saddr);
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	if (*fch->daddr & 1) {
151562306a36Sopenharmony_ci		if (!memcmp(fch->daddr, dev->broadcast, FC_ALEN)) {
151662306a36Sopenharmony_ci			skb->pkt_type = PACKET_BROADCAST;
151762306a36Sopenharmony_ci		} else {
151862306a36Sopenharmony_ci			skb->pkt_type = PACKET_MULTICAST;
151962306a36Sopenharmony_ci		}
152062306a36Sopenharmony_ci	} else {
152162306a36Sopenharmony_ci		if (memcmp(fch->daddr, dev->dev_addr, FC_ALEN)) {
152262306a36Sopenharmony_ci			skb->pkt_type = PACKET_OTHERHOST;
152362306a36Sopenharmony_ci		} else {
152462306a36Sopenharmony_ci			skb->pkt_type = PACKET_HOST;
152562306a36Sopenharmony_ci		}
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	fcllc = (struct fcllc *)skb->data;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/* Strip the SNAP header from ARP packets since we don't
153162306a36Sopenharmony_ci	 * pass them through to the 802.2/SNAP layers.
153262306a36Sopenharmony_ci	 */
153362306a36Sopenharmony_ci	if (fcllc->dsap == EXTENDED_SAP &&
153462306a36Sopenharmony_ci		(fcllc->ethertype == htons(ETH_P_IP) ||
153562306a36Sopenharmony_ci		 fcllc->ethertype == htons(ETH_P_ARP))) {
153662306a36Sopenharmony_ci		skb_pull(skb, sizeof(struct fcllc));
153762306a36Sopenharmony_ci		return fcllc->ethertype;
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	return htons(ETH_P_802_2);
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1544