/*******************************************************************
 * This file is part of the Emulex Linux Device Driver for         *
 * Enterprise Fibre Channel Host Bus Adapters.                     *
 * Refer to the README file included with this package for         *
 * driver version and adapter support.                             *
 * Copyright (C) 2003 Emulex Corporation.                          *
 * www.emulex.com                                                  *
 *                                                                 *
 * This program is free software; you can redistribute it and/or   *
 * modify it under the terms of the GNU General Public License     *
 * as published by the Free Software Foundation; either version 2  *
 * of the License, or (at your option) any later version.          *
 *                                                                 *
 * This program is distributed in the hope that it will be useful, *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
 * GNU General Public License for more details, a copy of which    *
 * can be found in the file COPYING included with this package.    *
 *******************************************************************/

#include "fc_os.h"

#include "fc_hw.h"
#include "fc.h"

#include "fcdiag.h"
#include "fcfgparm.h"
#include "fcmsg.h"
#include "fc_crtn.h"
#include "fc_ertn.h"

extern fc_dd_ctl_t DD_CTL;
extern iCfgParam icfgparam[];




/* Some timers in data structures are stored in seconds, some environments
 * timeout functions work in ticks, thus some conversion is required.
 * Other externs, as needed for environemt are defined here.
 */
extern uint32  fc_scsi_abort_timeout_ticks;
extern uint32  fc_ticks_per_second;




/* Routine Declaration - Local */
_local_ void       fc_deq_abort_bdr(dvi_t *dev_ptr);
_local_ void       fc_deq_wait(dvi_t *dev_ptr);
/* End Routine Declaration - Local */

/* AlpaArray for assignment of scsid for scan-down == 2 */
_static_ uchar AlpaArray[] = 
   {
   0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6,
   0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA,
   0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5,
   0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9,
   0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97,
   0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79,
   0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B,
   0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56,
   0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A,
   0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35,
   0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29,
   0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17,
   0x10, 0x0F, 0x08, 0x04, 0x02, 0x01
   };


_static_ dvi_t *
fc_fcp_abort(
   fc_dev_ctl_t * p_dev_ctl,
   int flag,
   int target,
   int lun)
{
   FC_BRD_INFO  * binfo;
   node_t       * node_ptr;
   dvi_t        * dev_ptr;
   dvi_t        * xmt_devp, * devp;
   RING         * rp;
   int            i;

   binfo = &BINFO;
   if(binfo->fc_flag & FC_ESTABLISH_LINK) 
      return(0);

   rp = &binfo->fc_ring[FC_FCP_RING];
   xmt_devp = 0;
   /* Clear the queues for one or more SCSI devices 
    * flag will indicate perform a Target Reset, Lun Reset, or Abort Task Set
    * if target = -1, all targets (bus reset).
    * if lun = -1 all luns on the target.
    */
   for (i = 0; i < MAX_FC_TARGETS; i++) {
      if ((node_ptr = binfo->device_queue_hash[i].node_ptr) != NULL) {
         if ((target != -1) && (node_ptr->scsi_id != target))
            continue;
         dev_ptr = node_ptr->lunlist;
         if((flag == TARGET_RESET) &&
            (dev_ptr != NULL)) {
            if((node_ptr->rpi != 0xFFFE) && (binfo->fc_ffstate == FC_READY)) {
               if(dev_ptr->flags & (SCSI_LUN_RESET | SCSI_ABORT_TSET)) {
                  /* check if we sent abort task set or reset lun */
                  for (devp = p_dev_ctl->ABORT_BDR_head; (devp != NULL); 
                     devp = devp->ABORT_BDR_fwd) {
                     if(devp == dev_ptr)
                        break;
                  }
                  if(devp) {
                     /* we found devp, its not sent yet,
                      * so change it to target reset.
                      */
                     dev_ptr->flags &= ~CHK_SCSI_ABDR;
                     dev_ptr->flags |= SCSI_TARGET_RESET;
                  }
                  else {
                     /* just Q another task mgmt cmd, target reset */
                     dev_ptr->flags |= SCSI_TARGET_RESET;
                     fc_enq_abort_bdr(dev_ptr);
                  }
                  xmt_devp = dev_ptr;
               }
               else if(!(dev_ptr->flags & SCSI_TARGET_RESET)) {
                  dev_ptr->flags |= SCSI_TARGET_RESET;
                  fc_enq_abort_bdr(dev_ptr);
                  fc_issue_cmd(p_dev_ctl);
                  xmt_devp = dev_ptr;
               }
            }
         }
         for (dev_ptr = node_ptr->lunlist; dev_ptr != NULL; 
             dev_ptr = dev_ptr->next) {
            if ((lun != -1) && (dev_ptr->lun_id != lun))
               continue;

            if(flag == TARGET_RESET) {
               if((node_ptr->rpi != 0xFFFE) && (binfo->fc_ffstate == FC_READY)) {
                  dev_ptr->flags |= SCSI_TARGET_RESET;
                  dev_ptr->queue_state = HALTED;
                  fc_fail_pendq(dev_ptr, (char) EIO, 0);
               }
               else {
                  /* First send ABTS on outstanding I/Os in txp queue */
                  fc_abort_fcp_txpq(binfo, dev_ptr);

                  fc_fail_pendq(dev_ptr, (char) EIO, 0);
                  fc_fail_cmd(dev_ptr, (char) EIO, 0);
               }
            }

            if((flag == LUN_RESET) &&
               !(dev_ptr->flags & CHK_SCSI_ABDR)) {

               if((node_ptr->rpi != 0xFFFE) && (binfo->fc_ffstate == FC_READY)) {
                  dev_ptr->flags |= SCSI_LUN_RESET;
                  fc_enq_abort_bdr(dev_ptr);
                  fc_issue_cmd(p_dev_ctl);
                  xmt_devp = dev_ptr;
                  dev_ptr->queue_state = HALTED;
                  fc_fail_pendq(dev_ptr, (char) EIO, 0);
               }
               else {
                  /* First send ABTS on outstanding I/Os in txp queue */
                  fc_abort_fcp_txpq(binfo, dev_ptr);

                  fc_fail_pendq(dev_ptr, (char) EIO, 0);
                  fc_fail_cmd(dev_ptr, (char) EIO, 0);
               }
            }

            if((flag == ABORT_TASK_SET) &&
               !(dev_ptr->flags & CHK_SCSI_ABDR)) {

               if((node_ptr->rpi != 0xFFFE) && (binfo->fc_ffstate == FC_READY)) {
                  dev_ptr->flags |= SCSI_ABORT_TSET;
                  fc_enq_abort_bdr(dev_ptr);
                  fc_issue_cmd(p_dev_ctl);
                  xmt_devp = dev_ptr;
                  dev_ptr->queue_state = HALTED;
                  fc_fail_pendq(dev_ptr, (char) EIO, 0);
               }
               else {
                  /* First send ABTS on outstanding I/Os in txp queue */
                  fc_abort_fcp_txpq(binfo, dev_ptr);

                  fc_fail_pendq(dev_ptr, (char) EIO, 0);
                  fc_fail_cmd(dev_ptr, (char) EIO, 0);
               }
            }
         }
      }
   }
   return(xmt_devp);
}

_static_ int
issue_abdr(
   fc_dev_ctl_t * ap,
   dvi_t * dev_ptr,
   RING * rp,
   fc_lun_t lun)
{
   FC_BRD_INFO   * binfo;
   iCfgParam     * clp;
   fc_buf_t      * fcptr;
   T_SCSIBUF     * sbp;
   IOCB          * cmd;
   IOCBQ         * temp;
   uint32        * lp;
   MATCHMAP      * bmp;
   ULP_BDE64     * bpl;
   int             i;

   binfo = &ap->info;
   if ((fcptr = fc_deq_fcbuf(dev_ptr)) == NULL) {
      return(EIO);
   }

   if ((temp = (IOCBQ * )fc_mem_get(binfo, MEM_IOCB)) == NULL) {
      fc_enq_fcbuf(fcptr);
      return(EIO);
   }

   {
      uint32 did;
      uint32 pan;
      uint32 sid;

      if ((dev_ptr->nodep) && (dev_ptr->nodep->nlp)) {
         did = dev_ptr->nodep->nlp->nlp_DID;
         pan = dev_ptr->nodep->nlp->id.nlp_pan;
         sid = dev_ptr->nodep->nlp->id.nlp_sid;
      } else {
         did = 0;
         pan = 0;
         sid = 0;
      }

      if (dev_ptr->flags & SCSI_ABORT_TSET) {
         /* Issue Abort Task Set I/O for LUN <num> */
         fc_log_printf_msg_vargs( binfo->fc_brd_no,
                &fc_msgBlk0701,                  /* ptr to msg structure */
                 fc_mes0701,                     /* ptr to msg */
                  fc_msgBlk0701.msgPreambleStr,  /* begin varargs */
                   (uint32)lun,
                    did,
                     FC_SCSID(pan, sid),
                      dev_ptr->flags);           /* end varargs */
      } else if (dev_ptr->flags & SCSI_TARGET_RESET) {
         /* Issue Target Reset I/O */
         fc_log_printf_msg_vargs( binfo->fc_brd_no,
                &fc_msgBlk0702,                  /* ptr to msg structure */
                 fc_mes0702,                     /* ptr to msg */
                  fc_msgBlk0702.msgPreambleStr,  /* begin varargs */
                   (uint32)lun,
                    did,
                     FC_SCSID(pan, sid),
                      dev_ptr->flags);           /* end varargs */
      } else if (dev_ptr->flags & SCSI_LUN_RESET) {
         /* Issue LUN Reset I/O for LUN <num> */
         fc_log_printf_msg_vargs( binfo->fc_brd_no,
                &fc_msgBlk0703,                  /* ptr to msg structure */
                 fc_mes0703,                     /* ptr to msg */
                  fc_msgBlk0703.msgPreambleStr,  /* begin varargs */
                   (uint32)lun,
                    did,
                     FC_SCSID(pan, sid),
                      dev_ptr->flags);           /* end varargs */
      }
   }

   sbp = &dev_ptr->scbuf;
   clp = DD_CTL.p_config[binfo->fc_brd_no];

   fc_bzero((void *)fcptr, sizeof(FCP_CMND) + sizeof(FCP_RSP));

   /* shift lun id into the right payload byte */
   fcptr->fcp_cmd.fcpLunMsl = lun << FC_LUN_SHIFT;
   fcptr->fcp_cmd.fcpLunLsl = 0;
   if (dev_ptr->nodep->addr_mode == VOLUME_SET_ADDRESSING) {
      fcptr->fcp_cmd.fcpLunMsl |= SWAP_DATA(0x40000000);
   }

   fcptr->sc_bufp = sbp;
   fcptr->flags = 0;
   sbp->bufstruct.b_flags = 0;
   sbp->bufstruct.b_error = 0;

   if (dev_ptr->flags & SCSI_ABORT_TSET) {
         /* Issue an Abort Task Set task management command */
         fcptr->fcp_cmd.fcpCntl2 = ABORT_TASK_SET;
   } else if (dev_ptr->flags & SCSI_TARGET_RESET) {
         /* Issue a Target Reset task management command */
     fcptr->fcp_cmd.fcpCntl2 = TARGET_RESET;
   } else if (dev_ptr->flags & SCSI_LUN_RESET) {
         /* Issue a Lun Reset task management command */
     fcptr->fcp_cmd.fcpCntl2 = LUN_RESET;
   }

   /* set up an iotag so we can match the completion iocb */
   for (i = 0; i < MAX_FCP_CMDS; i++) {
      fcptr->iotag = rp->fc_iotag++;
      if (rp->fc_iotag >= MAX_FCP_CMDS)
         rp->fc_iotag = 1;
      if (binfo->fc_table->fcp_array[fcptr->iotag] == 0)
         break;
   }

   if (i >= MAX_FCP_CMDS) {
      /* No more command slots available, retry later */
      fc_mem_put(binfo, MEM_IOCB, (uchar * )temp);
      fc_enq_fcbuf(fcptr);
      return(EIO);
   }

   fc_bzero((void *)temp, sizeof(IOCBQ));  /* zero the iocb entry */
   cmd = &temp->iocb;

   if (binfo->fc_flag & FC_SLI2) {
      /* Allocate buffer for Buffer ptr list */
      if ((bmp = (MATCHMAP * )fc_mem_get(binfo, MEM_BPL)) == 0) {
         fc_mem_put(binfo, MEM_IOCB, (uchar * )temp);
         fc_enq_fcbuf(fcptr);
         return(EIO);
      }
      bpl = (ULP_BDE64 * )bmp->virt;
      bpl->addrHigh = PCIMEM_LONG((uint32)putPaddrHigh(GET_PAYLOAD_PHYS_ADDR(fcptr)));
      bpl->addrLow = PCIMEM_LONG((uint32)putPaddrLow(GET_PAYLOAD_PHYS_ADDR(fcptr)));
      bpl->tus.f.bdeSize = sizeof(FCP_CMND);
      bpl->tus.f.bdeFlags = BUFF_USE_CMND;
      bpl->tus.w = PCIMEM_LONG(bpl->tus.w);

      bpl++;
      bpl->addrHigh = PCIMEM_LONG((uint32)putPaddrHigh(GET_PAYLOAD_PHYS_ADDR(fcptr)+sizeof(FCP_CMND)));
      bpl->addrLow = PCIMEM_LONG((uint32)putPaddrLow(GET_PAYLOAD_PHYS_ADDR(fcptr)+sizeof(FCP_CMND)));
      bpl->tus.f.bdeSize = sizeof(FCP_RSP);
      bpl->tus.f.bdeFlags = (BUFF_USE_CMND | BUFF_USE_RCV);
      bpl->tus.w = PCIMEM_LONG(bpl->tus.w);

      bpl++;
      cmd->un.fcpi64.bdl.ulpIoTag32 = (uint32)0;
      cmd->un.fcpi64.bdl.addrHigh = (uint32)putPaddrHigh(bmp->phys);
      cmd->un.fcpi64.bdl.addrLow = (uint32)putPaddrLow(bmp->phys);
      cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof(ULP_BDE64));
      cmd->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDL;

      cmd->ulpCommand = CMD_FCP_ICMND64_CR;
      cmd->ulpBdeCount = 1;
      fcptr->bmp = bmp;
      temp->bpl = (uchar *)0;
   } else {
      cmd->un.fcpi.fcpi_cmnd.bdeAddress = (uint32)putPaddrLow(GET_PAYLOAD_PHYS_ADDR(fcptr));
      cmd->un.fcpi.fcpi_cmnd.bdeSize = sizeof(FCP_CMND);
      cmd->un.fcpi.fcpi_rsp.bdeAddress = (uint32)(putPaddrLow((GET_PAYLOAD_PHYS_ADDR(fcptr) + sizeof(FCP_CMND))));
      cmd->un.fcpi.fcpi_rsp.bdeSize = sizeof(FCP_RSP);
      cmd->ulpCommand = CMD_FCP_ICMND_CR;
      cmd->ulpBdeCount = 2;
      temp->bpl = (uchar *)0;
   }
   cmd->ulpContext = dev_ptr->nodep->rpi;
   cmd->ulpIoTag = fcptr->iotag;
   cmd->ulpClass = (dev_ptr->nodep->nlp->id.nlp_fcp_info & 0x0f);
   cmd->ulpOwner = OWN_CHIP;
   cmd->ulpLe = 1;

   /* Timeout for this command is 30 seconds */
   curtime(&fcptr->timeout);


   /* Need to set the FCP timeout in the fcptr structure and the IOCB
    * for this I/O to get the adapter to run a timer.
    */
   {
      uint32    time_out;

      time_out = fc_scsi_abort_timeout_ticks;
      if (binfo->fc_flag & FC_FABRIC) {
         time_out += (fc_ticks_per_second * 
            (clp[CFG_FCPFABRIC_TMO].a_current + (2 * binfo->fc_ratov)));
      }

      fcptr->timeout = ((ulong)fcptr->timeout + time_out + fc_scsi_abort_timeout_ticks);

      /* Set the FCP timeout in the IOCB to get the adapter to run a timer */
      if ((time_out / fc_ticks_per_second) < 256)
         cmd->ulpTimeout = time_out / fc_ticks_per_second;

   }

   lp = (uint32 * ) & fcptr->fcp_cmd;
   dev_ptr->active_io_count++;
   dev_ptr->nodep->num_active_io++;

   /* Queue command to last iocb entry in xmit queue */
   if (rp->fc_tx.q_first == NULL) {
      rp->fc_tx.q_first = (uchar * )temp;
   } else {
      ((IOCBQ * )(rp->fc_tx.q_last))->q  = (uchar * )temp;
   }
   rp->fc_tx.q_last = (uchar * )temp;
   rp->fc_tx.q_cnt++;

   fc_enq_fcbuf_active(rp, fcptr);
   return (0);
}


/************************************************************************/
/*                                                                      */
/* NAME:        fc_issue_cmd                                            */
/*                                                                      */
/* FUNCTION:    issues a waiting FCP command, or ABORT/BDR command      */
/*                                                                      */
/* EXECUTION ENVIRONMENT:                                               */
/*      Called by a process or the interrupt handler                    */
/*                                                                      */
/* INPUTS:                                                              */
/*      ap      pointer to the adapter structure                        */
/*                                                                      */
/* RETURN VALUE DESCRIPTION:    none                                    */
/*                                                                      */
/* ERROR DESCRIPTION:  none                                             */
/*                                                                      */
/* EXTERNAL PROCEDURES CALLED:                                          */
/*      iodone                                                          */
/************************************************************************/
_static_ void
fc_issue_cmd(
   fc_dev_ctl_t * ap)
{
   dvi_t         * dev_ptr, * requeue_ptr;
   T_SCSIBUF     * sbp;
   int             rc, requeue, exit;
   FC_BRD_INFO   * binfo;
   RING          * rp;
   node_t        * nodep;

   binfo = &ap->info;
   if(binfo->fc_flag & FC_ESTABLISH_LINK) 
      return;

   rp = &binfo->fc_ring[FC_FCP_RING];

   /* ALUN */
   /* If the abort/bdr queue is not empty we deal with it first */
   for (dev_ptr = ap->ABORT_BDR_head; (dev_ptr != NULL); 
       dev_ptr = ap->ABORT_BDR_head) {

      if(dev_ptr->flags & CHK_SCSI_ABDR) {
         rc = issue_abdr(ap, dev_ptr, rp, dev_ptr->lun_id);
         if (rc != 0) {
            break;
         }
      }

      fc_deq_abort_bdr(dev_ptr);
   }

   requeue_ptr = NULL;
   exit = 0;

   /* See if there is something on the waiting queue */
   while (((dev_ptr = ap->DEVICE_WAITING_head) != NULL)
        && (binfo->fc_ffstate == FC_READY)
        && (dev_ptr != requeue_ptr)) {

      nodep = dev_ptr->nodep;
      /* Check if a target queue depth is set */
      if (nodep->rptlunstate == REPORT_LUN_ONGOING) {
         requeue = 1;
      } else if (nodep->tgt_queue_depth && 
          (nodep->tgt_queue_depth == nodep->num_active_io)) {
         if (dev_ptr->nodep->last_dev == NULL)
            dev_ptr->nodep->last_dev = dev_ptr;
         requeue = 1;
      } else if (dev_ptr->flags & (CHK_SCSI_ABDR | SCSI_TQ_HALTED)) {
         requeue = 1;
      } else {
         requeue = 0;

         while ((sbp = dev_ptr->pend_head) != NULL)
         {
            if ((dev_ptr->active_io_count >= dev_ptr->fcp_cur_queue_depth) ||
                (dev_ptr->stop_send_io)) {
               requeue = 1;
               break;
            }
            if ((rc = issue_fcp_cmd(ap, dev_ptr, sbp, 1))) {
               if (rc & FCP_REQUEUE) {
                  requeue = 1;
                  break;
               } else if (rc & FCP_EXIT) {
                  exit = 1;
                  break;
               }
               continue;
            }
            dev_ptr->pend_count--;
            dev_ptr->pend_head = (T_SCSIBUF *) sbp->bufstruct.av_forw;
            if (dev_ptr->pend_head == NULL)
               dev_ptr->pend_tail = NULL;
            else
               dev_ptr->pend_head->bufstruct.av_back = NULL;

            /* Check if a target queue depth is set */
            if (nodep->tgt_queue_depth && 
                (nodep->tgt_queue_depth == nodep->num_active_io)) {
               /* requeue I/O if max cmds to tgt are outstanding */
               if (dev_ptr->nodep->last_dev == NULL)
                  dev_ptr->nodep->last_dev = dev_ptr;
               requeue = 1;
               break;
            }
         }    /* while pend_head */
      }
      if (exit)
         break;

      fc_deq_wait(dev_ptr);

      if (requeue) {
         if (requeue_ptr == NULL)
            requeue_ptr = dev_ptr;
         fc_enq_wait(dev_ptr);
      }

   }   /* while wait queue */

   if (rp->fc_tx.q_cnt) {
      issue_iocb_cmd(binfo, rp, 0);
      /* [SYNC] */
      if (binfo->fc_flag & FC_POLL_MODE) {
         fc_polling(binfo, HA_R2ATT);
      }
   }

   return;

}       /* End fc_issue_cmd */


/**************************************************************************/
/*                                                                        */
/* NAME: fc_enq_fcbuf_active, fc_enq_wait, fc_enq_fcbuf, fc_enq_abort_bdr */
/*                                                                        */
/* FUNCTION:                                                              */
/*      Utility routines to handle queuing of device structures to each   */
/*      of the queues in use.                                             */
/*                                                                        */
/* EXECUTION ENVIRONMENT:                                                 */
/*                                                                        */
/* RETURN VALUE DESCRIPTION:  none                                        */
/*                                                                        */
/* ERROR DESCRIPTION:  The following errno values may be returned:        */
/*      none                                                              */
/*                                                                        */
/**************************************************************************/
_static_ void
fc_enq_fcbuf_active(
RING            *rp,    /* Pointer to ring for fcbufs */
fc_buf_t        *fcptr) /* Pointer to fcbuf to enqueue */
{
   FC_BRD_INFO   * binfo;
   fc_dev_ctl_t  *p_dev_ctl;

   binfo = (FC_BRD_INFO * )(rp->fc_binfo);
   p_dev_ctl = (fc_dev_ctl_t *)(binfo->fc_p_dev_ctl);
   /* Sync the FCP_CMND payload data */
   /* Use correct offset and size for syncing */
   fc_mpdata_sync(fcptr->fc_cmd_dma_handle, (off_t)fcptr->offset, 
      sizeof(FCP_CMND), DDI_DMA_SYNC_FORDEV);

   /* Enqueue the fcbuf on the FCP ring active queue */
   if (rp->fc_txp.q_first) {
      fcptr->fc_bkwd = (fc_buf_t * )rp->fc_txp.q_last;
      ((fc_buf_t * )rp->fc_txp.q_last)->fc_fwd = fcptr;
      rp->fc_txp.q_last = (uchar * )fcptr;
   } else {
      rp->fc_txp.q_first = (uchar * )fcptr;
      rp->fc_txp.q_last = (uchar * )fcptr;
      fcptr->fc_bkwd = NULL;
   }

   fcptr->fc_fwd = NULL;
   rp->fc_txp.q_cnt++;
   if(rp->fc_txp.q_cnt > rp->fc_txp.q_max) {
      rp->fc_txp.q_max = rp->fc_txp.q_cnt;
   }
   binfo->fc_table->fcp_array[fcptr->iotag] = fcptr;
}       /* End fc_enq_fcbuf_active */


/*
 * Name:        fc_enq_wait
 * Function:    Place dev_ptr on the adapter's wait queue.
 * Input:       dvi_t *dev_ptr  dev_ptr to enqueue.
 * Returns:     nothing.
 */
_static_ void
fc_enq_wait(
dvi_t   *dev_ptr)
{
   fc_dev_ctl_t  * ap;

   ap = dev_ptr->nodep->ap;

   /* Queue the dev_ptr if it is not already on the queue */
   if ((dev_ptr->DEVICE_WAITING_fwd == NULL)
        && (ap->DEVICE_WAITING_tail != dev_ptr)) {

      if (ap->DEVICE_WAITING_head == NULL) {
         ap->DEVICE_WAITING_head = dev_ptr;
      } else {
         ap->DEVICE_WAITING_tail->DEVICE_WAITING_fwd = dev_ptr;
      }
      ap->DEVICE_WAITING_tail = dev_ptr;
   }
}       /* End fc_enq_wait */

/* ALUN */
/*
 * Name:        fc_enq_fcbuf
 * Function:    Place fc_buf on the device's free queue.
 * Input:       fc_buf_t *fcptr         fc_buf to enqueue
 * Returns:     nothing.
 */
_static_ void
fc_enq_fcbuf(
fc_buf_t        *fcptr)
{
   dvi_t         * dev_ptr;

   dev_ptr = fcptr->dev_ptr;

   if (dev_ptr->fcbuf_head == NULL) {
      dev_ptr->fcbuf_head = fcptr;
   } else {
      dev_ptr->fcbuf_tail->fc_fwd = fcptr;
   }
   fcptr->fc_fwd = NULL;
   dev_ptr->fcbuf_tail = fcptr;
   dev_ptr->numfcbufs++;

   if (dev_ptr->numfcbufs == dev_ptr->fcp_lun_queue_depth) {
      if (dev_ptr->flags & SCSI_TQ_CLEARING) {
         /* Call iodone for all the CLEARQ error bufs */
         fc_free_clearq(dev_ptr);
      }
     if (dev_ptr->queue_state == STOPPING) {
         /* If we are trying to close, check to see if all done */
      }
   }

   return;
}       /* End fc_enq_fcbuf */


/*
 * Name:        fc_enq_abort_bdr
 * Function:    Place dev_ptr on the adapter's Bus Device Reset queue.
 * Input:       dvi_t *dev_ptr  dev_ptr to enqueue.
 * Returns:     nothing.
 */
_static_ void
fc_enq_abort_bdr(
   dvi_t * dev_ptr)
{
   fc_dev_ctl_t  * ap;

   ap = dev_ptr->nodep->ap;

   if (ap->ABORT_BDR_head == NULL) {
      dev_ptr->ABORT_BDR_fwd = NULL;
      dev_ptr->ABORT_BDR_bkwd = NULL;
      ap->ABORT_BDR_head = dev_ptr;
      ap->ABORT_BDR_tail = dev_ptr;
   } else {
      dev_ptr->ABORT_BDR_bkwd = ap->ABORT_BDR_tail;
      dev_ptr->ABORT_BDR_fwd = NULL;
      ap->ABORT_BDR_tail->ABORT_BDR_fwd = dev_ptr;
      ap->ABORT_BDR_tail = dev_ptr;
   }
}       /* End fc_enq_abort_bdr */


/**************************************************************************/
/*                                                                        */
/* NAME: fc_deq_fcbuf_active, fc_deq_wait, fc_deq_fcbuf, fc_deq_abort_bdr */
/*                                                                        */
/* FUNCTION:                                                              */
/*      Utility routines to handle dequeueing device structures from      */
/*      each of the queues in use.                                        */
/*                                                                        */
/* EXECUTION ENVIRONMENT:                                                 */
/*                                                                        */
/* ERROR DESCRIPTION:  The following errno values may be returned:        */
/*      none                                                              */
/*                                                                        */
/**************************************************************************/
_static_ fc_buf_t *
fc_deq_fcbuf_active(
   RING * rp,
   ushort iotag)                /* tag to match I/O */
{
   FC_BRD_INFO * binfo;
   fc_dev_ctl_t * p_dev_ctl;
   fc_buf_t    * fcptr = NULL;

   binfo = (FC_BRD_INFO * )(rp->fc_binfo);
   p_dev_ctl = (fc_dev_ctl_t *)(binfo->fc_p_dev_ctl);
   /* Remove an fcbuf from the FCP ring active queue based on iotag */

   if ((iotag < MAX_FCP_CMDS) && 
       (fcptr = binfo->fc_table->fcp_array[iotag])) {

      /* Remove fcbuf from list, adjust first, last and cnt */
      if (fcptr->fc_bkwd) {
         fcptr->fc_bkwd->fc_fwd = fcptr->fc_fwd;
      } else {
         rp->fc_txp.q_first = (uchar * )fcptr->fc_fwd;
      }

      if (fcptr->fc_fwd) {
         fcptr->fc_fwd->fc_bkwd = fcptr->fc_bkwd;
      } else {
         rp->fc_txp.q_last = (uchar * )fcptr->fc_bkwd;
      }

      rp->fc_txp.q_cnt--;
      binfo->fc_table->fcp_array[iotag] = NULL;
   }

   if (fcptr) {
      if (binfo->fc_flag & FC_SLI2) {
         MATCHMAP * next_bmp;

         while(fcptr->bmp) {
            next_bmp = (MATCHMAP *)fcptr->bmp->fc_mptr;
            fc_mem_put(binfo, MEM_BPL, (uchar *)fcptr->bmp);
            fcptr->bmp = next_bmp;
         }
      }
      fcptr->bmp = 0;

      /* Use correct offset and size for syncing */
      fc_mpdata_sync(fcptr->fc_cmd_dma_handle,
         (off_t)(fcptr->offset + sizeof(FCP_CMND)),
         (u_int) sizeof(FCP_RSP), DDI_DMA_SYNC_FORCPU);

   }
   return(fcptr);
}       /* End fc_deq_fcbuf_active */


/*
 * Name:        fc_deq_wait
 * Function:    Remove a dev_ptr from the adapter's wait queue.
 * Input:       dvi_t *dev_ptr  dev_ptr to be dequeued.
 * Returns:     nothing.
 */
_local_ void
fc_deq_wait(
   dvi_t * dev_ptr)
{
   fc_dev_ctl_t  * ap;
   dvi_t *prev_ptr;

   if(dev_ptr == NULL) {
    return;
   }
   ap = dev_ptr->nodep->ap;
   if(ap->DEVICE_WAITING_head == NULL) {
    return;
   }

   if(dev_ptr != ap->DEVICE_WAITING_head) {
    prev_ptr = ap->DEVICE_WAITING_head;
        while(prev_ptr->DEVICE_WAITING_fwd != dev_ptr && 
          prev_ptr != ap->DEVICE_WAITING_tail)
    {
        prev_ptr=prev_ptr->DEVICE_WAITING_fwd; 
    }
    if(prev_ptr->DEVICE_WAITING_fwd == dev_ptr) {
        prev_ptr->DEVICE_WAITING_fwd = dev_ptr->DEVICE_WAITING_fwd;
        if(ap->DEVICE_WAITING_tail == dev_ptr) {
        ap->DEVICE_WAITING_tail = prev_ptr;
        }
        dev_ptr->DEVICE_WAITING_fwd = NULL;
    }
        return;
   }
   if (ap->DEVICE_WAITING_head == ap->DEVICE_WAITING_tail) {
      ap->DEVICE_WAITING_head = NULL;
      ap->DEVICE_WAITING_tail = NULL;
   } else {
      ap->DEVICE_WAITING_head = dev_ptr->DEVICE_WAITING_fwd;
   }
   dev_ptr->DEVICE_WAITING_fwd = NULL;

}       /* End fc_deq_wait */


/*
 * Name:        fc_deq_fcbuf
 * Function:    Remove an fc_buf from the device's free queue.
 * Input:       dvi_t *dev_ptr  dev_ptr with the free list.
 * Returns:     pointer to the fc_buf, or NULL if none exist.
 */
_static_ fc_buf_t *
fc_deq_fcbuf(
   dvi_t * dev_ptr)
{
   fc_buf_t * fcptr;

   if (dev_ptr->fcbuf_head == NULL)
      return(NULL);

   fcptr = dev_ptr->fcbuf_head;
   if (dev_ptr->fcbuf_head == dev_ptr->fcbuf_tail) {
      dev_ptr->fcbuf_head = NULL;
      dev_ptr->fcbuf_tail = NULL;
   } else {
      dev_ptr->fcbuf_head = fcptr->fc_fwd;
   }
   dev_ptr->numfcbufs--;

   return(fcptr);

}       /* End fc_deq_fcbuf */


/*
 * Name:        fc_deq_abort_bdr
 * Function:    Removes a dev_ptr from the adapter's abort Bus Device Reset
 *              queue.
 * Input:       dvi_t *dev_ptr  dev_ptr to be removed.
 * Returns:     nothing.
 */
_local_ void
fc_deq_abort_bdr(
dvi_t   *dev_ptr)
{
   fc_dev_ctl_t  * ap;

   ap = dev_ptr->nodep->ap;

   if (ap->ABORT_BDR_head == ap->ABORT_BDR_tail) {
      ap->ABORT_BDR_head = NULL;
      ap->ABORT_BDR_tail = NULL;
   } else if (ap->ABORT_BDR_head == dev_ptr) {
      /* first one */
      ap->ABORT_BDR_head = dev_ptr->ABORT_BDR_fwd;
      dev_ptr->ABORT_BDR_fwd->ABORT_BDR_bkwd = dev_ptr->ABORT_BDR_bkwd;
   } else if (ap->ABORT_BDR_tail == dev_ptr) {
      /* last one */
      ap->ABORT_BDR_tail = dev_ptr->ABORT_BDR_bkwd;
      dev_ptr->ABORT_BDR_bkwd->ABORT_BDR_fwd = dev_ptr->ABORT_BDR_fwd;
   } else {
      /* in the middle */
      dev_ptr->ABORT_BDR_bkwd->ABORT_BDR_fwd = dev_ptr->ABORT_BDR_fwd;
      dev_ptr->ABORT_BDR_fwd->ABORT_BDR_bkwd = dev_ptr->ABORT_BDR_bkwd;
   }
   dev_ptr->ABORT_BDR_fwd = NULL;
   dev_ptr->ABORT_BDR_bkwd = NULL;

}       /* End fc_deq_abort_bdr */


/* Assign a SCSI ID to a nodelist table entry */
_static_ int
fc_assign_scsid(
fc_dev_ctl_t    *p_dev_ctl,
NODELIST        *ndlp)
{
   FC_BRD_INFO   * binfo;
   iCfgParam     * clp;
   node_t        * node_ptr;
   nodeh_t       * hp;
   NODELIST      * seedndlp;
   NODELIST      * new_ndlp;
   int  dev_index, i;

   binfo = &BINFO;
   clp = DD_CTL.p_config[binfo->fc_brd_no];
   /* Next check to see if our binding already has a SCSI ID */
   for (dev_index = 0; dev_index < MAX_FC_TARGETS; dev_index++) {
      hp =  &binfo->device_queue_hash[dev_index];
      i = (hp->node_flag & FCP_SEED_MASK);
      if ((i & FCP_SEED_DID) && (ndlp->nlp_DID == hp->un.dev_did)) 
         break;  /* a match */
      else if ((i & FCP_SEED_WWPN) &&
         (fc_geportname(&ndlp->nlp_portname, &hp->un.dev_portname) == 2))
         break;  /* a match */
      else if ((i & FCP_SEED_WWNN) &&  
         (fc_geportname(&ndlp->nlp_nodename, &hp->un.dev_nodename) == 2))
         break;  /* a match */
   }

   /* If not, assign a new SCSI ID / pan number */
   if (dev_index == MAX_FC_TARGETS) {
      seedndlp = binfo->fc_nlpbind_start;
      if(seedndlp == (NODELIST *)&binfo->fc_nlpbind_start)
        seedndlp = binfo->fc_nlpunmap_start;
      if(seedndlp == (NODELIST *)&binfo->fc_nlpunmap_start)
         seedndlp = binfo->fc_nlpmap_start;
      while(seedndlp != (NODELIST *)&binfo->fc_nlpmap_start) {
         new_ndlp = (NODELIST *)seedndlp->nlp_listp_next;
         
         if (seedndlp->nlp_type & NLP_SEED_MASK) {
            if (seedndlp->nlp_type & NLP_SEED_WWNN) {
               if (fc_geportname(&ndlp->nlp_nodename,
                  &seedndlp->nlp_nodename) == 2) {
                  ndlp->id.nlp_pan = seedndlp->id.nlp_pan;
                  ndlp->id.nlp_sid = seedndlp->id.nlp_sid;
                  ndlp->nlp_type |= NLP_SEED_WWNN;
                  if(seedndlp != ndlp) {
                     seedndlp->nlp_type &= ~NLP_FCP_TARGET;
                     fc_freenode(binfo, seedndlp, 0);
                     seedndlp->nlp_state = NLP_LIMBO;
                     fc_nlp_bind(binfo, seedndlp);
                  }
                  dev_index = INDEX(ndlp->id.nlp_pan, ndlp->id.nlp_sid);
                  hp =  &binfo->device_queue_hash[dev_index];

                  /* Claim SCSI ID by copying bind parameter to 
                   * proper index in device_queue_hash.
                   */
                  if(hp->node_ptr)
                     ndlp->nlp_targetp = (uchar *)hp->node_ptr;
                  fc_bcopy(&ndlp->nlp_nodename, &hp->un.dev_nodename,
                     sizeof(NAME_TYPE));
                  hp->node_flag &= ~FCP_SEED_MASK;
                  hp->node_flag |=  FCP_SEED_WWNN;
                  goto out1;
               }
            }
            if (seedndlp->nlp_type & NLP_SEED_WWPN) {
               if (fc_geportname(&ndlp->nlp_portname,
                  &seedndlp->nlp_portname) == 2) {
                  ndlp->id.nlp_pan = seedndlp->id.nlp_pan;
                  ndlp->id.nlp_sid = seedndlp->id.nlp_sid;
                  ndlp->nlp_type |= NLP_SEED_WWPN;
                  if(seedndlp != ndlp) {
                     seedndlp->nlp_type &= ~NLP_FCP_TARGET;
                     fc_freenode(binfo, seedndlp, 0);
                     seedndlp->nlp_state = NLP_LIMBO;
                     fc_nlp_bind(binfo, seedndlp);
                  }
                  dev_index = INDEX(ndlp->id.nlp_pan, ndlp->id.nlp_sid);
                  hp =  &binfo->device_queue_hash[dev_index];

                  /* Claim SCSI ID by copying bind parameter to 
                   * proper index in device_queue_hash.
                   */
                  if(hp->node_ptr)
                     ndlp->nlp_targetp = (uchar *)hp->node_ptr;
                  fc_bcopy(&ndlp->nlp_portname, &hp->un.dev_portname,
                     sizeof(NAME_TYPE));
                  hp->node_flag &= ~FCP_SEED_MASK;
                  hp->node_flag |=  FCP_SEED_WWPN;
                  goto out1;
               }
            }
            if (seedndlp->nlp_type & NLP_SEED_DID) {
               if (ndlp->nlp_DID == seedndlp->nlp_DID) {
                  ndlp->id.nlp_pan = seedndlp->id.nlp_pan;
                  ndlp->id.nlp_sid = seedndlp->id.nlp_sid;
                  ndlp->nlp_type |= NLP_SEED_DID;
                  if(seedndlp != ndlp) {
                     seedndlp->nlp_type &= ~NLP_FCP_TARGET;
                     fc_freenode(binfo, seedndlp, 0);
                     seedndlp->nlp_state = NLP_LIMBO;
                     fc_nlp_bind(binfo, seedndlp);
                  }
                  dev_index = INDEX(ndlp->id.nlp_pan, ndlp->id.nlp_sid);
                  hp =  &binfo->device_queue_hash[dev_index];

                  /* Claim SCSI ID by copying bind parameter to 
                   * proper index in device_queue_hash.
                   */
                  if(hp->node_ptr)
                     ndlp->nlp_targetp = (uchar *)hp->node_ptr;
                  hp->un.dev_did = ndlp->nlp_DID;
                  hp->node_flag &= ~FCP_SEED_MASK;
                  hp->node_flag |=  FCP_SEED_DID;
                  goto out1;
               }
            }
         }
         seedndlp = new_ndlp;
         if(seedndlp == (NODELIST *)&binfo->fc_nlpbind_start)
           seedndlp = binfo->fc_nlpunmap_start;
         if(seedndlp == (NODELIST *)&binfo->fc_nlpunmap_start)
            seedndlp = binfo->fc_nlpmap_start;
      }

      if(clp[CFG_AUTOMAP].a_current) {
         /* Fill in nodelist entry */
         if (DEV_PAN(p_dev_ctl->sid_cnt) == NLP_MAXPAN) {
            return(0);  /* No more available SCSI IDs */
         }

         /* If scan-down == 2 and we are private loop, automap
          * method is based on ALPA.
          */
         if((clp[CFG_SCAN_DOWN].a_current == 2) &&
            !(binfo->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) &&
            (binfo->fc_topology == TOPOLOGY_LOOP)) {
            for (i = 0; i < FC_MAXLOOP; i++) { 
               if(ndlp->nlp_DID == (uint32)AlpaArray[i])
                  break;
            }
            if(i == FC_MAXLOOP) {
               goto jmp_auto;
            }
            ndlp->id.nlp_pan = DEV_PAN(i);
            ndlp->id.nlp_sid = DEV_SID(i);
         }
         else {
            /* Check to make sure assigned scsi id does not overlap
             * with a seeded value.
             */
jmp_auto:
            seedndlp = binfo->fc_nlpbind_start;
            if(seedndlp == (NODELIST *)&binfo->fc_nlpbind_start)
              seedndlp = binfo->fc_nlpunmap_start;
            if(seedndlp == (NODELIST *)&binfo->fc_nlpunmap_start)
               seedndlp = binfo->fc_nlpmap_start;
            while(seedndlp != (NODELIST *)&binfo->fc_nlpmap_start) {
               if ((seedndlp->nlp_state == NLP_SEED)  ||
                   (seedndlp->nlp_type & NLP_SEED_MASK)) {
                  if ((seedndlp->id.nlp_pan == DEV_PAN(p_dev_ctl->sid_cnt)) &&
                      (seedndlp->id.nlp_sid == DEV_SID(p_dev_ctl->sid_cnt))) {
                      /* We overlap, so pick a new id and start again */
                      p_dev_ctl->sid_cnt++;
                      goto jmp_auto;
                  }
               }
               seedndlp = (NODELIST *)seedndlp->nlp_listp_next;
               if(seedndlp == (NODELIST *)&binfo->fc_nlpbind_start)
                 seedndlp = binfo->fc_nlpunmap_start;
               if(seedndlp == (NODELIST *)&binfo->fc_nlpunmap_start)
                  seedndlp = binfo->fc_nlpmap_start;
            }

            ndlp->id.nlp_pan = DEV_PAN(p_dev_ctl->sid_cnt);
            ndlp->id.nlp_sid = DEV_SID(p_dev_ctl->sid_cnt);
            p_dev_ctl->sid_cnt++;
         }
         ndlp->nlp_type |= NLP_AUTOMAP;

         dev_index = INDEX(ndlp->id.nlp_pan, ndlp->id.nlp_sid);
         hp =  &binfo->device_queue_hash[dev_index];

         /* Claim SCSI ID by copying bind parameter to 
          * proper index in device_queue_hash.
          */
         if(hp->node_ptr)
            ndlp->nlp_targetp = (uchar *)hp->node_ptr;
         switch(p_dev_ctl->fcp_mapping) {
         case FCP_SEED_DID:
            hp->un.dev_did = ndlp->nlp_DID;
            ndlp->nlp_type |= NLP_SEED_DID;
            break;
         case FCP_SEED_WWPN:
            fc_bcopy(&ndlp->nlp_portname, &hp->un.dev_portname, sizeof(NAME_TYPE));
            ndlp->nlp_type |= NLP_SEED_WWPN;
            break;
         case FCP_SEED_WWNN:
         default:
            fc_bcopy(&ndlp->nlp_nodename, &hp->un.dev_nodename, sizeof(NAME_TYPE));
            ndlp->nlp_type |= NLP_SEED_WWNN;
            break;
         }
         hp->node_flag &= ~FCP_SEED_MASK;
         hp->node_flag |=  p_dev_ctl->fcp_mapping;
         goto out1;
      }
      return(0);  /* Cannot assign a scsi id */
   }

   /* If scan-down == 2 and we are private loop, automap
    * method is based on ALPA.
    */
   if((clp[CFG_SCAN_DOWN].a_current == 2) &&
      !(binfo->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) &&
      (binfo->fc_topology == TOPOLOGY_LOOP)) {
      for (i = 0; i < FC_MAXLOOP; i++) { 
         if(ndlp->nlp_DID == (uint32)AlpaArray[i])
            break;
      }
      if(i == FC_MAXLOOP) {
         goto jmp_auto;
      }
      ndlp->id.nlp_pan = DEV_PAN(i);
      ndlp->id.nlp_sid = DEV_SID(i);
      goto out1;
   }
   /* Copy SCSI ID for the WWN into nodelist */
   ndlp->id.nlp_pan = DEV_PAN(dev_index);
   ndlp->id.nlp_sid = DEV_SID(dev_index);

   /* Update rpi for that SCSI ID's device node info */
   if ((node_ptr = (node_t * )ndlp->nlp_targetp) != NULL) {
      node_ptr->rpi = ndlp->nlp_Rpi;
      node_ptr->last_good_rpi = ndlp->nlp_Rpi;
      node_ptr->nlp = ndlp;
      node_ptr->flags &= ~FC_NODEV_TMO;
      ndlp->nlp_flag &= ~NLP_NODEV_TMO;
      if(node_ptr->nodev_tmr) {
         /* STOP nodev timer */
         fc_log_printf_msg_vargs( binfo->fc_brd_no,
                &fc_msgBlk0704,                  /* ptr to msg structure */
                 fc_mes0704,                     /* ptr to msg */
                  fc_msgBlk0704.msgPreambleStr,  /* begin varargs */
                   (ulong)ndlp,
                     ndlp->nlp_flag,
                      ndlp->nlp_state,
                       ndlp->nlp_DID);           /* end varargs */
         fc_clk_can(p_dev_ctl, node_ptr->nodev_tmr);
         node_ptr->nodev_tmr = 0;
      }
   }
   else {
      int  dev_index;
out1:
      dev_index = INDEX(ndlp->id.nlp_pan, ndlp->id.nlp_sid);
      node_ptr =  binfo->device_queue_hash[dev_index].node_ptr;
      if(node_ptr) {
         /* This is a new device that entered the loop */
         node_ptr->nlp = ndlp;
         node_ptr->rpi = ndlp->nlp_Rpi;
         node_ptr->last_good_rpi = ndlp->nlp_Rpi;
         node_ptr->scsi_id = dev_index;
         ndlp->nlp_targetp = (uchar *)node_ptr;
         node_ptr->flags &= ~FC_NODEV_TMO;
         ndlp->nlp_flag &= ~NLP_NODEV_TMO;
         if(node_ptr->nodev_tmr) {
            /* STOP nodev timer */
            fc_log_printf_msg_vargs( binfo->fc_brd_no,
                   &fc_msgBlk0705,                  /* ptr to msg structure */
                    fc_mes0705,                     /* ptr to msg */
                     fc_msgBlk0705.msgPreambleStr,  /* begin varargs */
                      (ulong)ndlp,
                        ndlp->nlp_flag,
                         ndlp->nlp_state,
                          ndlp->nlp_DID);           /* end varargs */
            fc_clk_can(p_dev_ctl, node_ptr->nodev_tmr);
            node_ptr->nodev_tmr = 0;
         }
      }
   }
   return(1);
}       /* End fc_assign_scsid */


/************************************************************************/
/*                                                                      */
/* NAME:        fc_fail_cmd                                             */
/*                                                                      */
/* FUNCTION:    Fail All Pending Commands Routine                       */
/*                                                                      */
/*      This routine is called to clear out all pending commands        */
/*      for a SCSI FCP device.                                          */
/*                                                                      */
/* EXECUTION ENVIRONMENT:                                               */
/*      This routine can only be called on priority levels              */
/*      equal to that of the interrupt handler.                         */
/*                                                                      */
/* DATA STRUCTURES:                                                     */
/*      sc_buf  - input/output request struct used between the adapter  */
/*                driver and the calling SCSI device driver             */
/*                                                                      */
/* INPUTS:                                                              */
/*      dev_info structure - pointer to device information structure    */
/*                                                                      */
/* RETURN VALUE DESCRIPTION:  The following are the return values:      */
/*      none                                                            */
/*                                                                      */
/************************************************************************/
_static_ void
fc_fail_cmd(
   dvi_t * dev_ptr,
   char error,
   uint32 statistic)
{
   T_SCSIBUF     * sbp;
   RING          * rp;
   IOCBQ         * iocb_cmd, *next;
   IOCB          * icmd;
   Q               tmpq;
   fc_buf_t      * fcptr;
   struct buf    * bp;
   dvi_t         * next_dev_ptr;
   fc_dev_ctl_t  * p_dev_ctl;
   FC_BRD_INFO   * binfo;
   iCfgParam     * clp;

   p_dev_ctl = dev_ptr->nodep->ap;
   binfo = &BINFO;
   rp = &binfo->fc_ring[FC_FCP_RING];
   clp = DD_CTL.p_config[binfo->fc_brd_no];
   /* First clear out all sc_buf structures in the pending queue */
   if(! clp[CFG_HOLDIO].a_current) {
      if((dev_ptr->nodep) &&
         (dev_ptr->nodep->rptlunstate == REPORT_LUN_ONGOING))
         goto out;
      sbp = dev_ptr->pend_head;
      dev_ptr->pend_head = NULL;      /* reset tail pointer */
      dev_ptr->pend_tail = NULL;      /* reset tail pointer */
      dev_ptr->pend_count = 0;

      while (sbp != NULL) {
         T_SCSIBUF     *nextsbp;

         sbp->bufstruct.b_flags |= B_ERROR;   /* set b_flags B_ERROR flag */
         sbp->bufstruct.b_error = error;
         sbp->bufstruct.b_resid = sbp->bufstruct.b_bcount;
         if (error) {
            sbp->status_validity = SC_ADAPTER_ERROR;
            SET_ADAPTER_STATUS(sbp,SC_NO_DEVICE_RESPONSE)
         } else {
            sbp->status_validity = 0;
         }

         /* Point to next sc_buf in pending chain, if any */
         nextsbp = (T_SCSIBUF *) sbp->bufstruct.av_forw;
         sbp->bufstruct.av_forw = 0;
         fc_do_iodone((struct buf *) sbp);  /* This could reque to pend_head */
         sbp = nextsbp;
      }
   }

out:
   /* Next clear out all fc_buf structures in the iocb queue for this device */
   tmpq.q_first = NULL;

   /* Get next command from ring xmit queue */
   iocb_cmd = fc_ringtx_get(rp);

   while (iocb_cmd) {
      icmd = &iocb_cmd->iocb;
      if ((icmd->ulpCommand != CMD_IOCB_CONTINUE_CN) && 
          (icmd->ulpContext == dev_ptr->nodep->last_good_rpi) && 
          (icmd->ulpIoTag < MAX_FCP_CMDS) && 
          (fcptr = binfo->fc_table->fcp_array[icmd->ulpIoTag]) && 
          (fcptr->dev_ptr == dev_ptr)) {

         if ((fcptr = fc_deq_fcbuf_active(rp, icmd->ulpIoTag)) != NULL) {
            bp = (struct buf *)fcptr->sc_bufp;

            /* Reject this command with error */
            if (fcptr->fcp_cmd.fcpCntl2) {

               /* This is a task management command */
               dev_ptr->ioctl_errno = error;
               if (fcptr->fcp_cmd.fcpCntl2 == ABORT_TASK_SET)
                  dev_ptr->flags &= ~SCSI_ABORT_TSET;

               if (fcptr->fcp_cmd.fcpCntl2 & TARGET_RESET) {
                  dev_ptr->flags &= ~SCSI_TARGET_RESET;
                  for (next_dev_ptr = dev_ptr->nodep->lunlist;
                     next_dev_ptr != NULL; next_dev_ptr = next_dev_ptr->next) {
                     next_dev_ptr->flags &= ~SCSI_TARGET_RESET;
                  }
               }

               if (fcptr->fcp_cmd.fcpCntl2 & LUN_RESET)
                  dev_ptr->flags &= ~SCSI_LUN_RESET;

               if (dev_ptr->ioctl_wakeup == 1) {
                  dev_ptr->ioctl_wakeup = 0;

                  fc_admin_wakeup(p_dev_ctl, dev_ptr, fcptr->sc_bufp);
               }
               else {
                  fc_do_iodone(bp);
               }

               dev_ptr->active_io_count--;
               dev_ptr->nodep->num_active_io--;

            } else {
               /* This is a regular FCP command */
               bp->b_error = error;
               bp->b_resid = bp->b_bcount;
               bp->b_flags |= B_ERROR;
               if (error) {
                  sbp = fcptr->sc_bufp;
                  sbp->status_validity = SC_ADAPTER_ERROR;
                  SET_ADAPTER_STATUS(sbp,SC_NO_DEVICE_RESPONSE)
               }

               sbp = fcptr->sc_bufp;

               dev_ptr->active_io_count--;
               dev_ptr->nodep->num_active_io--;
               fc_do_iodone(bp);
            }
            fc_enq_fcbuf(fcptr);
         }

         fc_mem_put(binfo, MEM_IOCB, (uchar * )iocb_cmd);

         while ((iocb_cmd = fc_ringtx_get(rp)) != NULL) {
            icmd = &iocb_cmd->iocb;
            if (icmd->ulpCommand != CMD_IOCB_CONTINUE_CN)
               break;
            fc_mem_put(binfo, MEM_IOCB, (uchar * )iocb_cmd);
         }
      } else {
         /* Queue this iocb to the temporary queue */
         if (tmpq.q_first) {
            ((IOCBQ * )tmpq.q_last)->q = (uchar * )iocb_cmd;
            tmpq.q_last = (uchar * )iocb_cmd;
         } else {
            tmpq.q_first = (uchar * )iocb_cmd;
            tmpq.q_last = (uchar * )iocb_cmd;
         }
         iocb_cmd->q = NULL;

         iocb_cmd = fc_ringtx_get(rp);
      }
   }

   /* Put the temporary queue back in the FCP iocb queue */
   iocb_cmd = (IOCBQ * )tmpq.q_first;
   while (iocb_cmd) {
      next = (IOCBQ * )iocb_cmd->q;
      fc_ringtx_put(rp, iocb_cmd);
      iocb_cmd = next;
   }

   return;
}       /* End fc_fail_cmd */

/* Fix up any changed RPIs in FCP IOCBs queued up a txq 
 * Called from CLEAR_LA after a link up.
 */
_static_ void
fc_fcp_fix_txq(
   fc_dev_ctl_t  * p_dev_ctl)
{
   RING          * rp;
   FC_BRD_INFO   * binfo;
   fc_buf_t      * fcptr;
   IOCBQ         * temp;
   IOCB          * cmd;
   dvi_t         * dev_ptr;
   unsigned long iflag;

   iflag = lpfc_q_disable_lock(p_dev_ctl);
   binfo = &BINFO;
   rp = &binfo->fc_ring[FC_FCP_RING];

   /* Make sure all RPIs on txq are still ok */
   temp = (IOCBQ *)rp->fc_tx.q_first;
   while (temp != NULL) {
      cmd = &temp->iocb;
      if ((fcptr = binfo->fc_table->fcp_array[cmd->ulpIoTag]) != NULL) {
         dev_ptr = fcptr->dev_ptr;
         if((dev_ptr) && (dev_ptr->nodep) &&
            (cmd->ulpContext != dev_ptr->nodep->rpi)) {
            cmd->ulpContext = dev_ptr->nodep->rpi;
         }
      }
      if(rp->fc_tx.q_last == (uchar * )temp)
         break;
      temp = (IOCBQ *)temp->q;
   }
   lpfc_q_unlock_enable(p_dev_ctl, iflag);
   return;
}   /* End fc_fcp_fix_txq */

_static_ void
fc_fail_pendq(
   dvi_t * dev_ptr,
   char error,
   uint32 statistic)
{
   T_SCSIBUF     * sbp;
   RING          * rp;
   fc_dev_ctl_t  * p_dev_ctl;
   FC_BRD_INFO   * binfo;
   iCfgParam     * clp;

   p_dev_ctl = dev_ptr->nodep->ap;
   binfo = &BINFO;
   clp = DD_CTL.p_config[binfo->fc_brd_no];
   rp = &binfo->fc_ring[FC_FCP_RING];

   if((dev_ptr->nodep) &&
      (dev_ptr->nodep->rptlunstate == REPORT_LUN_ONGOING))
      goto out;

   if(clp[CFG_HOLDIO].a_current)
      goto out;

   sbp = dev_ptr->pend_head;
   dev_ptr->pend_head = NULL;      /* reset tail pointer */
   dev_ptr->pend_tail = NULL;      /* reset tail pointer */
   dev_ptr->pend_count = 0;

   while (sbp != NULL) {
      T_SCSIBUF     *nextsbp;

      sbp->bufstruct.b_flags |= B_ERROR;   /* set b_flags B_ERROR flag */
      sbp->bufstruct.b_error = error;
      sbp->bufstruct.b_resid = sbp->bufstruct.b_bcount;
      if (error) {
         sbp->status_validity = SC_ADAPTER_ERROR;
         SET_ADAPTER_STATUS(sbp,SC_NO_DEVICE_RESPONSE)
      } else {
         sbp->status_validity = 0;
      }

      /* Point to next sc_buf in pending chain, if any */
      nextsbp = (T_SCSIBUF *) sbp->bufstruct.av_forw;
      sbp->bufstruct.av_forw = 0;
      fc_do_iodone((struct buf *) sbp);  /* This could reque to pend_head */
      sbp = nextsbp;
   }

out:
   return;
}       /* End fc_fail_pendq */

/************************************************************************/
/*                                                                      */
/* NAME:issue_fcp_cmd                                                   */
/*                                                                      */
/* FUNCTION:Issue an FCP command to the adapter iocb queue              */
/*                                                                      */
/* EXECUTION ENVIRONMENT:                                               */
/* This routine always runs at interrupt level                          */
/*                                                                      */
/* DATA STRUCTURES:                                                     */
/* sc_buf- input/output request struct used between the adapter         */
/*  driver and the calling SCSI device driver                           */
/*                                                                      */
/* RETURN VALUE DESCRIPTION:  0 = success                               */
/*      1 = continue                                                    */
/*      2 = requeue                                                     */
/*      4 = exit                                                        */
/*                                                                      */
/************************************************************************/
_static_ int
issue_fcp_cmd(
   fc_dev_ctl_t * p_dev_ctl,
   dvi_t * dev_ptr,
   T_SCSIBUF     * sbp,
   int pend)
{
   FC_BRD_INFO * binfo = &BINFO;
   iCfgParam   * clp;
   struct buf  * bp;
   fc_buf_t    * fcptr;
   int           i, rc;
   RING        * rp;
   IOCBQ       * temp;
   IOCB        * cmd;
   uint32        count, * lp;
   fc_lun_t      lun;
   ULP_BDE64   * bpl;
   MATCHMAP    * bmp;
   NODELIST    * ndlp;

   rp = &binfo->fc_ring[FC_FCP_RING];
   bp = (struct buf *) sbp;
   clp = DD_CTL.p_config[binfo->fc_brd_no];

   if((dev_ptr->nodep == 0) ||
      ((ndlp = dev_ptr->nodep->nlp) == 0))
      return(FCP_REQUEUE);

   if ( !(ndlp->capabilities & FC_CAP_AUTOSENSE) ) {
      if (dev_ptr->sense_valid &&
          (sbp->scsi_command.scsi_cmd.scsi_op_code == SCSI_REQUEST_SENSE)) {

         /* Request sense command - use saved sense data */
         if (bp->b_bcount > (int)dev_ptr->sense_length) {
            bp->b_resid = bp->b_bcount - (int)dev_ptr->sense_length;
            count = dev_ptr->sense_length;
         } else {
            count = bp->b_bcount;
         }
         lp = (uint32 * )dev_ptr->sense;
         lpfc_copy_sense(dev_ptr, bp);
         bp->b_error = 0;
         bp->b_flags &= ~B_ERROR;

         if (pend) {
            dev_ptr->pend_head = (T_SCSIBUF *) bp->av_forw;
            if (dev_ptr->pend_head == NULL)
               dev_ptr->pend_tail = NULL;
            else
               dev_ptr->pend_head->bufstruct.av_back = NULL;
            dev_ptr->pend_count--;
         }
         dev_ptr->sense_valid = 0;

         FCSTATCTR.fcpSense++;
         fc_do_iodone(bp);
         return(FCP_CONTINUE);
      }
   }


   if(dev_ptr->queue_state != ACTIVE) {
      return(FCP_REQUEUE);
   }

   if(binfo->fc_process_LA == 0) {
      return(FCP_REQUEUE);
   }

   /* Check if device is in process of resetting */
   if (dev_ptr->flags & SCSI_DEV_RESET) {
      return(FCP_REQUEUE);
   }

   if (dev_ptr->nodep->rpi == 0xFFFE) {

      if(clp[CFG_HOLDIO].a_current) {
         return(FCP_REQUEUE);
      }

      if((clp[CFG_NODEV_TMO].a_current) &&
        ((dev_ptr->nodep->flags & FC_NODEV_TMO) == 0)) {

         /* Kick off first PLOGI to device */
         if (!(ndlp->nlp_flag & NLP_REQ_SND)) {
            uint32 did;

            did = ndlp->nlp_DID;
            if(did == (uint32)0) {
               if((ndlp->nlp_type & (NLP_AUTOMAP | NLP_SEED_MASK)) &&
                  (ndlp->nlp_state == NLP_LIMBO) && ndlp->nlp_oldDID)
                  did = ndlp->nlp_oldDID;
            }
            ndlp->nlp_flag &= ~NLP_RM_ENTRY;
            if ((!(ndlp->nlp_flag & NLP_NODEV_TMO)) &&
               (did != (uint32)0)) {
               if(!(ndlp->nlp_flag & NLP_NS_REMOVED)) {
                  ndlp->nlp_flag |= NLP_NODEV_TMO;
                  fc_els_cmd(binfo, ELS_CMD_PLOGI, (void *)((ulong)did),
                      (uint32)0, (ushort)0, ndlp);
               }
            }
         }
         else {
            ndlp->nlp_flag |= NLP_NODEV_TMO;
         }
         return(FCP_REQUEUE);
      }

      /* The device is not active at this time */
      bp->b_error = EIO;
      bp->b_resid = bp->b_bcount;
      bp->b_flags |= B_ERROR;
      sbp->status_validity = SC_ADAPTER_ERROR;
      SET_ADAPTER_STATUS(sbp,SC_NO_DEVICE_RESPONSE)
      if (pend) {
         dev_ptr->pend_head = (T_SCSIBUF *) bp->av_forw;
         if (dev_ptr->pend_head == NULL)
            dev_ptr->pend_tail = NULL;
         else
            dev_ptr->pend_head->bufstruct.av_back = NULL;
         dev_ptr->pend_count--;
      }
         
      FCSTATCTR.fcpNoDevice++;
      fc_delay_iodone(p_dev_ctl, sbp);

      {
         uint32 did;
         uint32 pan;
         uint32 sid;

         did = ndlp->nlp_DID;
         pan = ndlp->id.nlp_pan;
         sid = ndlp->id.nlp_sid;

         if (!(dev_ptr->flags & DONT_LOG_INVALID_RPI)) {
            /* Cannot issue FCP command */
            fc_log_printf_msg_vargs( binfo->fc_brd_no,
                   &fc_msgBlk0706,                  /* ptr to msg structure */
                    fc_mes0706,                     /* ptr to msg */
                     fc_msgBlk0706.msgPreambleStr,  /* begin varargs */
                      did,
                       FC_SCSID(pan, sid));         /* end varargs */
            dev_ptr->flags |= DONT_LOG_INVALID_RPI;
         }
      }
      return(FCP_CONTINUE);
   }

   if (ndlp->nlp_action & NLP_DO_RSCN) {
      return(FCP_REQUEUE);
   }

   if ((fcptr = fc_deq_fcbuf(dev_ptr)) == NULL) {
      return(FCP_REQUEUE);
   }

   if ((temp = (IOCBQ * )fc_mem_get(binfo, MEM_IOCB)) == NULL) {
      fc_enq_fcbuf(fcptr);
      return(FCP_EXIT);
   }

   fc_bzero((void *)fcptr, sizeof(FCP_CMND) + sizeof(FCP_RSP));

   /* Copy SCSI cmd into FCP payload for xmit.*/
   lun = (uint32) sbp->scsi_command.scsi_lun;
   {
     int i;
     fcptr->fcp_cmd.fcpCdb[0]= sbp->scsi_command.scsi_cmd.scsi_op_code;
     fcptr->fcp_cmd.fcpCdb[1]= sbp->scsi_command.scsi_cmd.lun;
     for(i=0; i< (sizeof(struct sc_cmd)-2); i++)
       fcptr->fcp_cmd.fcpCdb[i+2]= sbp->scsi_command.scsi_cmd.scsi_bytes[i];
     fcptr->fcp_cmd.fcpCntl1 = sbp->scsi_command.flags;
   }

   /* Put LUN in the FCP command using the Peripheral Addressing Method */
   fcptr->fcp_cmd.fcpLunMsl = lun << FC_LUN_SHIFT;
   fcptr->fcp_cmd.fcpLunLsl = 0;

   /*
    * The Logical Unit Addressing method is not supported at
    * this current release.
    */
   if (dev_ptr->nodep->addr_mode == VOLUME_SET_ADDRESSING) {
      fcptr->fcp_cmd.fcpLunMsl |= SWAP_DATA(0x40000000);
   }

   fcptr->fcp_cmd.fcpDl = SWAP_DATA(bp->b_bcount);

   fcptr->sc_bufp = sbp;
   fcptr->flags = 0;

   /* set up an iotag so we can match the completion iocb */
   for (i = 0; i < MAX_FCP_CMDS; i++) {
      fcptr->iotag = rp->fc_iotag++;
      if (rp->fc_iotag >= MAX_FCP_CMDS)
         rp->fc_iotag = 1;
      if (binfo->fc_table->fcp_array[fcptr->iotag] == 0)
         break;
   }
   if (i >= MAX_FCP_CMDS) {
      /* No more command slots available, retry later */
      fc_mem_put(binfo, MEM_IOCB, (uchar * )temp);
      fc_enq_fcbuf(fcptr);
      return(FCP_EXIT);
   }

   fc_bzero((void *)temp, sizeof(IOCBQ));  /* zero the iocb entry */
   cmd = &temp->iocb;

   if (binfo->fc_flag & FC_SLI2) {
      /* Allocate buffer for Buffer ptr list */
      if ((bmp = (MATCHMAP * )fc_mem_get(binfo, MEM_BPL)) == 0) {
         fc_mem_put(binfo, MEM_IOCB, (uchar * )temp);
         fc_enq_fcbuf(fcptr);
         return(FCP_EXIT);
      }
      bpl = (ULP_BDE64 * )bmp->virt;
      bpl->addrHigh = PCIMEM_LONG((uint32)putPaddrHigh(GET_PAYLOAD_PHYS_ADDR(fcptr)));
      bpl->addrLow = PCIMEM_LONG((uint32)putPaddrLow(GET_PAYLOAD_PHYS_ADDR(fcptr)));
      bpl->tus.f.bdeSize = sizeof(FCP_CMND);
      bpl->tus.f.bdeFlags = BUFF_USE_CMND;
      bpl->tus.w = PCIMEM_LONG(bpl->tus.w);
      bpl++;
      bpl->addrHigh = PCIMEM_LONG((uint32)putPaddrHigh(GET_PAYLOAD_PHYS_ADDR(fcptr)+sizeof(FCP_CMND)));
      bpl->addrLow = PCIMEM_LONG((uint32)putPaddrLow(GET_PAYLOAD_PHYS_ADDR(fcptr)+sizeof(FCP_CMND)));
      bpl->tus.f.bdeSize = sizeof(FCP_RSP);
      bpl->tus.f.bdeFlags = (BUFF_USE_CMND | BUFF_USE_RCV);
      bpl->tus.w = PCIMEM_LONG(bpl->tus.w);
      bpl++;

      cmd->un.fcpi64.bdl.ulpIoTag32 = (uint32)0;
      cmd->un.fcpi64.bdl.addrHigh = (uint32)putPaddrHigh(bmp->phys);
      cmd->un.fcpi64.bdl.addrLow = (uint32)putPaddrLow(bmp->phys);
      cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof(ULP_BDE64));
      cmd->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDL;
      cmd->ulpBdeCount = 1;
      fcptr->bmp = bmp;
      temp->bpl = (uchar *)0;
   } else {
      bpl = 0;
      cmd->un.fcpi.fcpi_cmnd.bdeAddress = (uint32)putPaddrLow(GET_PAYLOAD_PHYS_ADDR(fcptr));
      cmd->un.fcpi.fcpi_cmnd.bdeSize = sizeof(FCP_CMND);
      cmd->un.fcpi.fcpi_rsp.bdeAddress = (uint32)(putPaddrLow((GET_PAYLOAD_PHYS_ADDR(fcptr) + sizeof(FCP_CMND))));
      cmd->un.fcpi.fcpi_rsp.bdeSize = sizeof(FCP_RSP);
      cmd->ulpBdeCount = 2;
      fcptr->bmp = 0;
      temp->bpl = (uchar *)0;
   }

   cmd->ulpContext = dev_ptr->nodep->rpi;
   cmd->ulpIoTag = fcptr->iotag;
   /*
    * if device is FCP-2 device, set the following bit that says
    * to run the FC-TAPE protocol.
    */
   if (ndlp->id.nlp_fcp_info & NLP_FCP_2_DEVICE) {
      cmd->ulpFCP2Rcvy = 1;
   }
   cmd->ulpClass = (ndlp->id.nlp_fcp_info & 0x0f);
   cmd->ulpOwner = OWN_CHIP;

   if (sbp->timeout_value == 0)
      sbp->timeout_value = 3600;  /* One hour in seconds */

   curtime(&fcptr->timeout);

   /* Need to set the FCP timeout in the fcptr structure and the IOCB
    * for this I/O to get the adapter to run a timer.
    */
   {
      uint32 time_out;

      if(sbp->timeout_value)
         time_out = sbp->timeout_value * fc_ticks_per_second;
      else
         time_out = 30 * fc_ticks_per_second;

      if (binfo->fc_flag & FC_FABRIC) {
         time_out += (fc_ticks_per_second *
            (clp[CFG_FCPFABRIC_TMO].a_current + (2 * binfo->fc_ratov)));
      }

      fcptr->timeout = ((ulong)fcptr->timeout + time_out + (300 * fc_ticks_per_second));
 
      /* Set the FCP timeout in the IOCB to get the adapter to run a timer */
      if ((time_out / fc_ticks_per_second) < 256)
         cmd->ulpTimeout = time_out / fc_ticks_per_second;
   }

   if (bp->b_bcount == 0) {
      /* Set up for SCSI command */
      if (binfo->fc_flag & FC_SLI2)
         cmd->ulpCommand = CMD_FCP_ICMND64_CR;
      else
         cmd->ulpCommand = CMD_FCP_ICMND_CR;

      if (((fcptr->fcp_cmd.fcpCdb[0] & 0xBF) == SCSI_RESERVE_UNIT) || 
          ((fcptr->fcp_cmd.fcpCdb[0] & 0xBF) == SCSI_RELEASE_UNIT)) {
         /* Mask off the lun field for reserve/release commands */
         fcptr->fcp_cmd.fcpCdb[1] &= 0x1f;
      }
      if(bpl) {
         bpl->addrHigh = 0;
         bpl->addrLow = 0;
         bpl->tus.w = 0;
      }
      cmd->un.fcpi.fcpi_parm = 0;
      fcptr->fcp_cmd.fcpCntl3 = 0;

      cmd->ulpLe = 1;
      /* Queue cmd chain to last iocb entry in xmit queue */
      if (rp->fc_tx.q_first == NULL) {
         rp->fc_tx.q_first = (uchar * )temp;
      } else {
         ((IOCBQ * )(rp->fc_tx.q_last))->q  = (uchar * )temp;
      }
      rp->fc_tx.q_last = (uchar * )temp;
      rp->fc_tx.q_cnt++;

   } else if (bp->b_flags & B_READ) {
      /* Set up for SCSI read */
      if (binfo->fc_flag & FC_SLI2)
         cmd->ulpCommand = CMD_FCP_IREAD64_CR;
      else
         cmd->ulpCommand = CMD_FCP_IREAD_CR;
      cmd->ulpPU = PARM_READ_CHECK;
      cmd->un.fcpi.fcpi_parm = bp->b_bcount;
      fcptr->fcp_cmd.fcpCntl3 = READ_DATA;
      if((rc = fc_fcp_bufmap(p_dev_ctl, sbp, fcptr, temp, bpl, dev_ptr, pend)) != 0)
         return(rc);
   } else {
      /* Set up for SCSI write */
      if (binfo->fc_flag & FC_SLI2)
         cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
      else
         cmd->ulpCommand = CMD_FCP_IWRITE_CR;
      fcptr->fcp_cmd.fcpCntl3 = WRITE_DATA;
      if((rc = fc_fcp_bufmap(p_dev_ctl, sbp, fcptr, temp, bpl, dev_ptr, pend)) != 0)
         return(rc);
   }

   if(dev_ptr->nodep->flags & FC_FCP2_RECOVERY)
      cmd->ulpFCP2Rcvy = 1;

   lp = (uint32 * ) & fcptr->fcp_cmd;
   fc_enq_fcbuf_active(rp, fcptr);

   dev_ptr->active_io_count++;
   dev_ptr->nodep->num_active_io++;
   FCSTATCTR.fcpCmd++;

   return(0);
}       /* End issue_fcp_cmd */


_static_ int
fc_failio(
   fc_dev_ctl_t  * p_dev_ctl)
{
   FC_BRD_INFO   * binfo;
   node_t  * node_ptr;
   dvi_t   * dev_ptr;
   struct buf *bp, *nextbp;
   int i;

   binfo = &BINFO;

   /* Clear the queues for one or more SCSI devices */
   for (i = 0; i < MAX_FC_TARGETS; i++) {
      if ((node_ptr = binfo->device_queue_hash[i].node_ptr) != NULL) {
         for (dev_ptr = node_ptr->lunlist; dev_ptr != NULL; 
             dev_ptr = dev_ptr->next) {

            dev_ptr->queue_state = HALTED;
            fc_return_standby_queue(dev_ptr,
               (uchar)((binfo->fc_flag & FC_BUS_RESET) ? EIO : EFAULT), 0);

            /* First send ABTS on outstanding I/Os in txp queue */
            fc_abort_fcp_txpq(binfo, dev_ptr);

            fc_fail_pendq(dev_ptr, (char)((binfo->fc_flag & FC_BUS_RESET) ? 
               EIO : EFAULT), 0);

            fc_fail_cmd(dev_ptr, (char)((binfo->fc_flag & FC_BUS_RESET) ? 
               EIO : EFAULT), 0);

            /* Call iodone for all the CLEARQ error bufs */
            fc_free_clearq(dev_ptr);
         }
      }
   }
   /* Call iodone for any commands that timed out previously */
   for (bp = p_dev_ctl->timeout_head; bp != NULL; ) {
      nextbp = bp->av_forw;
      bp->b_error = ETIMEDOUT;
      bp->b_flags |= B_ERROR;
      fc_do_iodone(bp);
      bp = nextbp;
   }
   p_dev_ctl->timeout_head = NULL;
   p_dev_ctl->timeout_count = 0;
   return(0);
}


_static_ void
fc_return_standby_queue(
   dvi_t * dev_ptr, 
   uchar status, 
   uint32 statistic)
{
   T_SCSIBUF * sp;

   /* It is possible to have IOs on the pending queue because
      of the way the scheduler works. */

   while ((sp = dev_ptr->standby_queue_head) != NULL) {
      dev_ptr->standby_count--;
      dev_ptr->standby_queue_head = (T_SCSIBUF *)sp->bufstruct.av_forw;
      fc_do_iodone((struct buf *) sp);
   }
   dev_ptr->standby_queue_head = NULL;
   dev_ptr->standby_queue_tail = NULL;
}

/*
 *      Restart all devices for a given adapter.  Should only be
 *      invoked at the conclusion of loop {re}discovery.
 */
_static_ int
fc_restart_all_devices(
   fc_dev_ctl_t * p_dev_ctl)
{
   dvi_t        * dev_ptr;
   FC_BRD_INFO  * binfo;
   int            i;
   node_t       * node_ptr;

   binfo = &BINFO;

   for (i = 0; i < MAX_FC_TARGETS; ++i) {
      if ((node_ptr = binfo->device_queue_hash[i].node_ptr) != NULL) {
         dev_ptr = node_ptr->lunlist;
         while (dev_ptr) {
            if ((dev_ptr->queue_state == RESTART_WHEN_READY) || 
                (dev_ptr->queue_state == HALTED)) {
               fc_restart_device(dev_ptr);
            }

            if (dev_ptr->nodep->rpi != 0xfffe)
               dev_ptr->flags &= ~(NORPI_RESET_DONE | DONT_LOG_INVALID_RPI);
            else
               dev_ptr->flags &= ~DONT_LOG_INVALID_RPI;
            fc_enq_wait(dev_ptr);
            dev_ptr = dev_ptr->next;
         }
      }
   }
   return(0);
}       /* End fc_restart_all_devices */

/*
 *      Restart a device by draining its standby queue.
 */
_static_ int
fc_restart_device(
   dvi_t * dev_ptr)
{
   FC_BRD_INFO       * binfo;

   binfo = &dev_ptr->nodep->ap->info;
   if (binfo->fc_ffstate != FC_READY || 
       (dev_ptr->flags & (SCSI_TQ_CLEARING | CHK_SCSI_ABDR))) {

      dev_ptr->queue_state = RESTART_WHEN_READY;
      return(0);
   }

   dev_ptr->queue_state = ACTIVE;
   dev_ptr->flags &= ~(SCSI_TQ_HALTED | CHK_SCSI_ABDR);
   fc_return_standby_queue(dev_ptr,
               (uchar)((binfo->fc_flag & FC_BUS_RESET) ? EIO : EFAULT), 0);

   return(1);
}       /* End fc_restart_device */

/* Called to reissue fcp command if tgt throttle was reached */
_static_ void
re_issue_fcp_cmd(
   dvi_t * dev_ptr)
{
   fc_dev_ctl_t  * ap;
   dvi_t         * next_ptr;
   dvi_t         * start_ptr;
   T_SCSIBUF     * sbp = NULL;
   int             rc;
   FC_BRD_INFO   * binfo;
   RING          * rp;

   if (dev_ptr == NULL)
      return;

   ap = dev_ptr->nodep->ap;
   binfo = &ap->info;

   rp = &binfo->fc_ring[FC_FCP_RING];

   next_ptr = dev_ptr;
   start_ptr = next_ptr->nodep->lunlist;

   if (start_ptr == NULL)
      return;

   do {

      if ((sbp = next_ptr->pend_head) != NULL)
         break;

      next_ptr = next_ptr->next;
      if (!next_ptr)
         next_ptr = start_ptr;
   } while ( next_ptr != dev_ptr );

   if (! sbp) {
      next_ptr->nodep->last_dev = NULL;
      return;
   }

   if ((rc = issue_fcp_cmd(ap, next_ptr, sbp, 1))) {
      if ((rc & FCP_REQUEUE) || (rc & FCP_EXIT)) 
        return;
   }
   next_ptr->pend_count--;
   next_ptr->pend_head = (T_SCSIBUF *) sbp->bufstruct.av_forw;
   if (next_ptr->pend_head == NULL)
      next_ptr->pend_tail = NULL;
   else
      next_ptr->pend_head->bufstruct.av_back = NULL;

   if (next_ptr->pend_count == 0)
      fc_deq_wait(next_ptr);

   next_ptr->nodep->last_dev = next_ptr->next;
   if (next_ptr->nodep->last_dev == NULL)
      next_ptr->nodep->last_dev = next_ptr->nodep->lunlist;

   if (rp->fc_tx.q_cnt)
      issue_iocb_cmd(binfo, rp, 0);

   return;
}       /* End re_issue_fcp_cmd */

/* Find a SCSI device structure for a given LUN */
_static_ dvi_t *
fc_find_lun(
FC_BRD_INFO     *binfo,
int              hash_index,
fc_lun_t         lun)
{
   node_t        * node_ptr;
   dvi_t         * dev_ptr;

   if ((hash_index < 0) || (hash_index > MAX_FC_TARGETS))
      return(NULL);

   node_ptr = binfo->device_queue_hash[hash_index].node_ptr;

   if (node_ptr == NULL) {
      dev_ptr = NULL;
   } else {
      for (dev_ptr = node_ptr->lunlist; dev_ptr != NULL; 
          dev_ptr = dev_ptr->next) {

         if (dev_ptr->lun_id == lun) {
            /* We found the correct entry */
            break;
         }
      }
   }
   return(dev_ptr);
}       /* End fc_find_lun */

_static_ int
fc_reset_dev_q_depth(
   fc_dev_ctl_t * p_dev_ctl)
{
   dvi_t        * dev_ptr;
   FC_BRD_INFO  * binfo;
   int            i;
   iCfgParam    * clp;
   node_t       * node_ptr;

   binfo = &BINFO;
   clp = DD_CTL.p_config[binfo->fc_brd_no];

   for (i = 0; i < MAX_FC_TARGETS; ++i) {
      if ((node_ptr = binfo->device_queue_hash[i].node_ptr) != NULL) {
         dev_ptr = node_ptr->lunlist;
         while (dev_ptr) {
            dev_ptr->fcp_cur_queue_depth = (ushort)clp[CFG_DFT_LUN_Q_DEPTH].a_current;
            dev_ptr = dev_ptr->next;
         }
      }
   }
   return(0);
}       /* End fc_reset_dev_q_depth */


/* [SYNC] */
_static_ void
fc_polling(
   FC_BRD_INFO *binfo,
   uint32 att_bit)
{
   volatile uint32   ha_copy;
   void           *ioa;
   fc_dev_ctl_t * p_dev_ctl;

   p_dev_ctl = (fc_dev_ctl_t *)binfo->fc_p_dev_ctl;
   do {
      ioa = (void *)FC_MAP_IO(&binfo->fc_iomap_io);  /* map in io registers */
      ha_copy = READ_CSR_REG(binfo, FC_HA_REG(binfo, ioa));
      FC_UNMAP_MEMIO(ioa);
      if (ha_copy & att_bit)
         break;
   } while (1);
   fc_intr((struct intr *)p_dev_ctl);
}
