/*
* 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>
#include <linux/sched.h>
#include <linux/slab.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))
/*
* Fusion MPT LAN private structures
*/
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 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 ;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* 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_array(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_array(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));
dma_unmap_single(&mpt_dev->pcidev->dev,
priv->RcvCtl[i].dma,
priv->RcvCtl[i].len, DMA_FROM_DEVICE);
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) {
dma_unmap_single(&mpt_dev->pcidev->dev,
priv->SendCtl[i].dma,
priv->SendCtl[i].len, DMA_TO_DEVICE);
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 ;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/* Tx timeout handler. */
static void
mpt_lan_tx_timeout(struct net_device *dev, unsigned int txqueue)
{
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;
dev->stats.tx_packets++;
dev->stats.tx_bytes += sent->len;
dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n" ,
IOC_AND_NETDEV_NAMES_s_s(dev),
__func__, sent));
priv->SendCtl[ctx].skb = NULL;
dma_unmap_single(&mpt_dev->pcidev->dev, priv->SendCtl[ctx].dma,
priv->SendCtl[ctx].len, DMA_TO_DEVICE);
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:
dev->stats.tx_packets += count;
break ;
case MPI_IOCSTATUS_LAN_CANCELED:
case MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED:
break ;
case MPI_IOCSTATUS_INVALID_SGL:
dev->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 :
dev->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;
dev->stats.tx_bytes += sent->len;
dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n" ,
IOC_AND_NETDEV_NAMES_s_s(dev),
__func__, sent));
priv->SendCtl[ctx].skb = NULL;
dma_unmap_single(&mpt_dev->pcidev->dev,
priv->SendCtl[ctx].dma,
priv->SendCtl[ctx].len, DMA_TO_DEVICE);
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 netdev_tx_t
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 = 0 x1000;
dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n" ,
__func__, 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" ,
__func__, priv->mpt_txfidx_tail);
return NETDEV_TX_BUSY;
}
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" ,
__func__);
return NETDEV_TX_BUSY;
}
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 = dma_map_single(&mpt_dev->pcidev->dev, skb->data, skb->len,
DMA_TO_DEVICE);
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 = 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);
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);
netif_trans_update(dev);
dioprintk((KERN_INFO MYNAM ": %s/%s: Sending packet. FlagsLength = %08x.\n" ,
IOC_AND_NETDEV_NAMES_s_s(dev),
le32_to_cpu(pSimple->FlagsLength)));
return NETDEV_TX_OK;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
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 = netdev_priv(dev);
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 = netdev_priv(dev);
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));
dev->stats.rx_bytes += skb->len;
dev->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 = netdev_priv(dev);
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;
}
dma_sync_single_for_cpu(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
dma_sync_single_for_device(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
goto out;
}
skb_put(skb, len);
priv->RcvCtl[ctx].skb = NULL;
dma_unmap_single(&mpt_dev->pcidev->dev, priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len, DMA_FROM_DEVICE);
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 = netdev_priv(dev);
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;
dma_unmap_single(&mpt_dev->pcidev->dev, priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len, DMA_FROM_DEVICE);
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 = netdev_priv(dev);
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));
dma_sync_single_for_cpu(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
skb_copy_from_linear_data(old_skb, skb_put(skb, l), l);
dma_sync_single_for_device(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
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;
}
dma_sync_single_for_cpu(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
skb_copy_from_linear_data(old_skb, skb_put(skb, len), len);
dma_sync_single_for_device(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
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;
dma_unmap_single(&mpt_dev->pcidev->dev, priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len, DMA_FROM_DEVICE);
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),
__func__, buckets, curr));
max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) /
(sizeof (SGETransaction32_t) + 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" ,
__func__);
dioprintk((KERN_ERR "%s: %u buckets remaining\n" ,
__func__, 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" ,
__func__);
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)) {
dma_unmap_single(&mpt_dev->pcidev->dev,
priv->RcvCtl[ctx].dma,
priv->RcvCtl[ctx].len,
DMA_FROM_DEVICE);
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" ,
__func__);
priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
break ;
}
dma = dma_map_single(&mpt_dev->pcidev->dev,
skb->data, len,
DMA_FROM_DEVICE);
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 = 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",
/**/ __func__);
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 ( " % 08 x " , 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" ,
__func__, buckets, atomic_read(&priv->buckets_out)));
dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n" ,
__func__, 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 const struct net_device_ops mpt_netdev_ops = {
.ndo_open = mpt_lan_open,
.ndo_stop = mpt_lan_close,
.ndo_start_xmit = mpt_lan_sdu_send,
.ndo_tx_timeout = mpt_lan_tx_timeout,
};
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
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;
dev_addr_set(dev, HWaddr);
memset(dev->broadcast, 0 xff, 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->netdev_ops = &mpt_netdev_ops;
dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT;
/* MTU range: 96 - 65280 */
dev->min_mtu = MPT_LAN_MIN_MTU;
dev->max_mtu = MPT_LAN_MAX_MTU;
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)
{
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 = %pM\n" ,
IOC_AND_NETDEV_NAMES_s_s(dev),
dev->dev_addr);
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;
struct mpt_lan_priv *priv = netdev_priv(dev);
cancel_delayed_work_sync(&priv->post_buckets_task);
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);
LanCtx = mpt_register(lan_reply, MPTLAN_DRIVER,
"lan_reply" );
if (LanCtx <= 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(0 xffff)) {
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 = %pM\n" ,
fch->saddr);
}
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;
/* 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);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Messung V0.5 in Prozent C=91 H=91 G=90
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland