root/usr/src/uts/common/io/cpqary3/cpqary3_util.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
 */

#include <sys/sdt.h>
#include "cpqary3.h"

/*
 * Local Functions Definitions
 */

int cpqary3_target_geometry(struct scsi_address *);
int8_t cpqary3_detect_target_geometry(cpqary3_t *);

/*
 * Function     :       cpqary3_read_conf_file
 * Description  :       This routine reads the driver configuration file.
 * Called By    :       cpqary3_attach()
 * Parameters   :       device-information pointer, per_controller
 * Calls        :       None
 * Return Values:       None
 */
void
cpqary3_read_conf_file(dev_info_t *dip, cpqary3_t *cpqary3p)
{
        char            *ptr;

        cpqary3p->noe_support = 0;

        /*
         * Plugin the code necessary to read from driver's conf file.
         * As of now, we are not interested in reading the onf file
         * for any purpose.
         *
         * eg. :
         *
         * retvalue = ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
         *      "cpqary3_online_debug", -1);
         */

        /*
         *  We are calling ddi_prop_lookup_string
         *  which gets the property value, which is passed at
         *  the grub menu. If the user wants to use the older
         *  target mapping algorithm,(prior to 1.80)at the grub menu
         *  "cpqary3_tgtmap=off" should be entered. if this
         *  string is entered, then we will set the
         *  value of the variable legacy_mapping to one, which
         *  will be used in
         *  cpqary3_detect_target_geometry()
         *  and cpqary3_probe4LVs(), to decide on the
         *  mapping algorithm
         */

        if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
            "cpqary3_tgtmap", &ptr) == DDI_PROP_SUCCESS) {
                if (strcmp("off", ptr) == 0) {
                        cpqary3p->legacy_mapping = 1;
                }
                ddi_prop_free(ptr);
        }

        if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
            "cpqary3_noesupport", &ptr) == DDI_PROP_SUCCESS) {
                if (strcmp("on", ptr) == 0) {
                        cpqary3p->noe_support = 1;
                }
                if (strcmp("off", ptr) == 0) {
                        cpqary3p->noe_support = 0;
                }
                ddi_prop_free(ptr);
        }
}

/*
 * Function     :       cpqary3_tick_hdlr
 * Description  :       This routine is called once in 60 seconds to detect any
 *                      command that is pending with the controller and has
 *                      timed out.
 *                      Once invoked, it re-initializes itself such that it is
 *                      invoked after an interval of 60 seconds.
 * Called By    :       kernel
 * Parameters   :       per_controller
 * Calls        :       None
 * Return Values:       None
 */
void
cpqary3_tick_hdlr(void *arg)
{
        clock_t                 cpqary3_lbolt;
        clock_t                 cpqary3_ticks;
        cpqary3_t               *ctlr;
        cpqary3_pkt_t           *pktp;
        struct scsi_pkt         *scsi_pktp;
        cpqary3_cmdpvt_t        *local;
        volatile CfgTable_t     *ctp;
        uint32_t                i;
        uint32_t                no_cmds = 0;

        /*
         * The per-controller shall be passed as argument.
         * Read the HeartBeat of the controller.
         * if the current heartbeat is the same as the one recorded earlier,
         * the f/w has locked up!!!
         */

        if (NULL == (ctlr = (cpqary3_t *)arg))
                return;

        ctp = (CfgTable_t *)ctlr->ct;

        /* CONTROLLER_LOCKUP */
        if (ctlr->heartbeat == DDI_GET32(ctlr, &ctp->HeartBeat)) {
                if (ctlr->lockup_logged == CPQARY3_FALSE) {
                        cmn_err(CE_WARN, "CPQary3 : "
                            "%s HBA firmware Locked !!!", ctlr->hba_name);
                        cmn_err(CE_WARN, "CPQary3 : "
                            "Please reboot the system");
                        cpqary3_intr_onoff(ctlr, CPQARY3_INTR_DISABLE);
                        if (ctlr->host_support & 0x4)
                                cpqary3_lockup_intr_onoff(ctlr,
                                    CPQARY3_LOCKUP_INTR_DISABLE);
                        ctlr->controller_lockup = CPQARY3_TRUE;
                        ctlr->lockup_logged = CPQARY3_TRUE;
                }
        }
        /* CONTROLLER_LOCKUP */
        no_cmds  = (uint32_t)((ctlr->ctlr_maxcmds / 3) *
            NO_OF_CMDLIST_IN_A_BLK);
        mutex_enter(&ctlr->sw_mutex);

        for (i = 0; i < no_cmds; i++) {
                local = &ctlr->cmdmemlistp->pool[i];
                ASSERT(local != NULL);
                pktp = MEM2PVTPKT(local);

                if (!pktp)
                        continue;

                if ((local->cmdpvt_flag == CPQARY3_TIMEOUT) ||
                    (local->cmdpvt_flag == CPQARY3_RESET)) {
                        continue;
                }

                if (local->occupied == CPQARY3_OCCUPIED) {
                        scsi_pktp = pktp->scsi_cmd_pkt;
                        cpqary3_lbolt = ddi_get_lbolt();
                        if ((scsi_pktp) && (scsi_pktp->pkt_time)) {
                                cpqary3_ticks = cpqary3_lbolt -
                                    pktp->cmd_start_time;

                                if ((drv_hztousec(cpqary3_ticks)/1000000) >
                                    scsi_pktp->pkt_time) {
                                        scsi_pktp->pkt_reason = CMD_TIMEOUT;
                                        scsi_pktp->pkt_statistics =
                                            STAT_TIMEOUT;
                                        scsi_pktp->pkt_state = STATE_GOT_BUS |
                                            STATE_GOT_TARGET | STATE_SENT_CMD;
                                        local->cmdpvt_flag = CPQARY3_TIMEOUT;

                                        /* This should always be the case */
                                        if (scsi_pktp->pkt_comp) {
                                                mutex_exit(&ctlr->sw_mutex);
                                                (*scsi_pktp->pkt_comp)
                                                    (scsi_pktp);
                                                mutex_enter(&ctlr->sw_mutex);
                                                continue;
                                        }
                                }
                        }
                }
        }

        ctlr->heartbeat = DDI_GET32(ctlr, &ctp->HeartBeat);
        mutex_exit(&ctlr->sw_mutex);
        ctlr->tick_tmout_id = timeout(cpqary3_tick_hdlr,
            (caddr_t)ctlr, drv_usectohz(CPQARY3_TICKTMOUT_VALUE));
}

/*
 * Function     :       cpqary3_init_ctlr_resource
 * Description  :       This routine initializes the command list, initializes
 *                      the controller, enables the interrupt.
 * Called By    :       cpqary3_attach()
 * Parameters   :       per_controller
 * Calls        :       cpqary3_init_ctlr(), cpqary3_meminit(),
 *                      cpqary3_intr_onoff(),
 * Return Values:       SUCCESS / FAILURE
 *                      [ Shall return failure if any of the mandatory
 *                      initializations / setup of resources fail ]
 */
uint16_t
cpqary3_init_ctlr_resource(cpqary3_t *ctlr)
{
#ifdef CPQARY3_DEBUG_MEM
        int8_t i = 0;
#endif

        /*
         * Initialize the Controller
         * Alocate Memory Pool for driver supported number of Commands
         * return if not successful
         * Allocate target structure for controller and initialize the same
         * Detect all existing targets and allocate target structure for each
         * Determine geometry for all existing targets
         * Initialize the condition variables
         */

        RETURN_FAILURE_IF_NULL(ctlr);

        if (CPQARY3_FAILURE == cpqary3_init_ctlr(ctlr))
                return ((CPQARY3_FAILURE));

        if (CPQARY3_FAILURE == cpqary3_meminit(ctlr))
                return ((CPQARY3_FAILURE));


#ifdef CPQARY3_DEBUG_MEM
        /*
         * This code is in place to test the memory management of this driver.
         * This block of code allocates and de-allocates memory as many number
         * of times as given in the for loop.
         * After the for loop is executed, it returns a failure, which in turn
         * would result in attach being failed.
         */
        cmn_err(CE_CONT, "CPQary3 : _init_ctlr_resource : Testing memory \n");
        for (i = 0; i < 15; i++) {
                if (CPQARY3_SUCCESS != cpqary3_meminit(ctlr)) {
                        cmn_err(CE_CONT, "CPQary3 : meminit failed : "
                            "attempt %d \n", i);
                        return (CPQARY3_FAILURE);
                }
                cmn_err(CE_CONT,
                    "CPQary3 : INIT successful : attempt %d \n", i);
                cpqary3_memfini(ctlr, CPQARY3_MEMLIST_DONE |
                    CPQARY3_PHYCTGS_DONE | CPQARY3_CMDMEM_DONE);
                cmn_err(CE_CONT,
                    "CPQary3 : FINI successful : attempt %d \n", i);
        }
        return (CPQARY3_FAILURE);
#endif

        ctlr->cpqary3_tgtp[CTLR_SCSI_ID] = MEM_ZALLOC(sizeof (cpqary3_tgt_t));
        if (!(ctlr->cpqary3_tgtp[CTLR_SCSI_ID])) {
                cmn_err(CE_WARN, "CPQary3: Target Initialization Failed");
                cpqary3_memfini(ctlr, CPQARY3_MEMLIST_DONE |
                    CPQARY3_PHYCTGS_DONE | CPQARY3_CMDMEM_DONE);
                return (CPQARY3_FAILURE);
        }
        ctlr->cpqary3_tgtp[CTLR_SCSI_ID]->type = CPQARY3_TARGET_CTLR;

        cpqary3_intr_onoff(ctlr, CPQARY3_INTR_DISABLE);

        /*
         * Initialize all condition variables :
         * for the immediate call back
         * for the disable noe
         * for fulsh cache
         * for probe device
         */

        cv_init(&ctlr->cv_immediate_wait, NULL, CV_DRIVER, NULL);
        cv_init(&ctlr->cv_noe_wait, NULL, CV_DRIVER, NULL);
        cv_init(&ctlr->cv_flushcache_wait, NULL, CV_DRIVER, NULL);
        cv_init(&ctlr->cv_abort_wait, NULL, CV_DRIVER, NULL);
        cv_init(&ctlr->cv_ioctl_wait, NULL, CV_DRIVER, NULL);

        return (CPQARY3_SUCCESS);
}

/*
 * Function     :       cpqary3_target_geometry
 * Description  :       This function returns the geometry for the target.
 * Called By    :       cpqary3_getcap()
 * Parameters   :       Target SCSI address
 * Calls        :       None
 * Return Values:       Device Geometry
 */
int
cpqary3_target_geometry(struct scsi_address *sa)
{
        cpqary3_t       *ctlr = SA2CTLR(sa);
        cpqary3_tgt_t   *tgtp = ctlr->cpqary3_tgtp[SA2TGT(sa)];

        /*
         * The target CHS are stored in the per-target structure
         * during attach time. Use these values
         */
        return ((tgtp->properties.drive.heads << 16) |
            tgtp->properties.drive.sectors);
}

/*
 * Function     :       cpqary3_synccmd_alloc
 * Description  :       This function allocates the DMA buffer for the commands
 * Called By    :       cpqary3_ioctl_send_bmiccmd(),
 *                      cpqary3_ioctl_send_scsicmd()
 *                      cpqary3_send_abortcmd(), cpqary3_flush_cache(),
 *                      cpqary3_probe4LVs(), cpqary3_probe4Tapes(),
 *                      cpqary3_detect_target_geometry()
 * Parameters   :       per_controller, buffer size
 * Calls        :       cpqary3_alloc_phyctgs_mem(), cpqary3_cmdlist_occupy()
 * Return Values:       memp
 */
cpqary3_cmdpvt_t *
cpqary3_synccmd_alloc(cpqary3_t *cpqary3p, size_t bufsz)
{
        cpqary3_private_t       *cmddmah = NULL;
        uint32_t                dmabufpa = 0;   /* XXX 32-bit pa? */
        cpqary3_cmdpvt_t        *memp = NULL;

        /*  first, allocate any necessary dma buffers  */
        if (bufsz > 0) {
                cpqary3_phyctg_t        *dmah = NULL;
                caddr_t                 dmabufva = NULL;

                /* first, allocate the command's dma handle */
                cmddmah = (cpqary3_private_t *)MEM_ZALLOC(sizeof (*cmddmah));
                if (cmddmah == NULL) {
                        cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
                            "no memory for cmddmah");
                        return (NULL);
                }

                /* next, allocate dma handle */
                dmah = (cpqary3_phyctg_t *)MEM_ZALLOC(sizeof (*dmah));
                if (dmah == NULL) {
                        MEM_SFREE(cmddmah, sizeof (*cmddmah));
                        cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
                            "no memory for dmah");
                        return (NULL);
                }
                /* now, allocate dma buffer */
                dmabufva = cpqary3_alloc_phyctgs_mem(cpqary3p, bufsz,
                    &dmabufpa, dmah);
                if (dmabufva == NULL) {
                        MEM_SFREE(cmddmah, sizeof (*cmddmah));
                        cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
                            "no memory for dma buf");
                        return (NULL);
                }
                bzero(dmabufva, bufsz);

                /* attach dma buffer to command dma handle */
                cmddmah->sg = dmabufva;
                cmddmah->phyctgp = dmah;
        }

        /* next, allocate a command packet */
        memp = cpqary3_cmdlist_occupy(cpqary3p);
        if (memp == NULL) {
                if (cmddmah != NULL) {
                        cpqary3_free_phyctgs_mem(cmddmah->phyctgp,
                            CPQARY3_FREE_PHYCTG_MEM);
                        MEM_SFREE(cmddmah, sizeof (*cmddmah));
                }
                cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
                    "cannot get free command");
                return (NULL);
        }
        memp->cmdpvt_flag = 0;
        memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
            CPQARY3_SYNCCMD_SUCCESS;

        /* attach dma resources to command */
        memp->driverdata = cmddmah;
        memp->cmdlist_memaddr->SG[0].Addr = dmabufpa;
        memp->cmdlist_memaddr->SG[0].Len  = (uint32_t)bufsz;

        /* done */
        return (memp);
}

/*
 * Function     :   cpqary3_synccmd_cleanup
 * Description  :   This routine cleans up the command
 * Called By    :   cpqary3_process_pkt(), cpqary3_synccmd_free()
 * Parameters   :   per_command_memory
 * Calls        :   cpqary3_free_phyctgs_mem(), cpqary3_cmdlist_release()
 * Return Values:   none
 */
void
cpqary3_synccmd_cleanup(cpqary3_cmdpvt_t *memp)
{
        /*
         * ordinary users should not call this routine
         * (use cpqary3_synccmd_free() instead).  this is
         * for use ONLY by cpqary3_synccmd_free() and
         * cpqary3_process_pkt().
         */

        if (memp->driverdata != NULL) {
                /* free dma resources */
                cpqary3_free_phyctgs_mem(memp->driverdata->phyctgp,
                    CPQARY3_FREE_PHYCTG_MEM);
                MEM_SFREE(memp->driverdata, sizeof (cpqary3_private_t));
                memp->driverdata = NULL;
        }
        /* release command */
        memp->cmdpvt_flag = 0;
        cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
}

/*
 * Function     :       cpqary3_synccmd_free
 * Description  :       This routine frees the command and the
 *                      associated resources.
 * Called By    :       cpqary3_ioctl_send_bmiccmd(),
 *                      cpqary3_ioctl_send_scsicmd()
 *                      cpqary3_send_abortcmd(), cpqary3_flush_cache(),
 *                      cpqary3_probe4LVs(), cpqary3_probe4Tapes(),
 *                      cpqary3_detect_target_geometry()
 * Parameters   :       per_controller, per_command_memory
 * Calls        :       cpqary3_synccmd_cleanup()
 * Return Values:       NONE
 */
void
cpqary3_synccmd_free(cpqary3_t *cpqary3p, cpqary3_cmdpvt_t *memp)
{
        /*
         * so, the user is done with this command packet.
         * we have three possible scenarios here:
         *
         * 1) the command was never submitted to the controller
         *
         * or
         *
         * 2) the command has completed at the controller and has
         *    been fully processed by the interrupt processing
         *    mechanism and is no longer on the submitted or
         *    retrieve queues.
         *
         * or
         *
         * 3) the command is not yet complete at the controller,
         *    and/or hasn't made it through cpqary3_process_pkt()
         *    yet.
         *
         * For cases (1) and (2), we can go ahead and free the
         * command and the associated resources.  For case (3), we
         * must mark the command as no longer needed, and let
         * cpqary3_process_pkt() clean it up instead.
         */

        mutex_enter(&(cpqary3p->sw_mutex));
        if (memp->cmdpvt_flag == CPQARY3_SYNC_SUBMITTED) {
                /*
                 * command is still pending (case #3 above).
                 * mark the command as abandoned and let
                 * cpqary3_process_pkt() clean it up.
                 */
                memp->cmdpvt_flag = CPQARY3_SYNC_TIMEOUT;
                mutex_exit(&(cpqary3p->sw_mutex));
                return;
        }
        memp->cmdpvt_flag = 0;
        mutex_exit(&(cpqary3p->sw_mutex));

        /*
         * command was either never submitted or has completed
         * (cases #1 and #2 above).  so, clean it up.
         */
        cpqary3_synccmd_cleanup(memp);

        /* done */
        return;

}  /* cpqary3_synccmd_free() */

/*
 * Function     :       cpqary3_synccmd_send
 * Description  :       This routine sends the command to the controller
 * Called By    :       cpqary3_ioctl_send_bmiccmd(),
 *                      cpqary3_ioctl_send_scsicmd()
 *                      cpqary3_send_abortcmd(), cpqary3_flush_cache(),
 *                      cpqary3_probe4LVs(), cpqary3_probe4Tapes(),
 *                      cpqary3_detect_target_geometry()
 * Parameters   :       per_controller, per_command_memory, timeout value,
 *                      flag(wait for reply)
 * Calls        :       cpqary3_submit(), cpqary3_add2submitted_cmdq()
 * Return Values:       SUCCESS / FAILURE
 */
int
cpqary3_synccmd_send(cpqary3_t *cpqary3p, cpqary3_cmdpvt_t *memp,
    clock_t timeoutms, int flags)
{
        clock_t         absto = 0;  /* absolute timeout */
        int             waitsig = 0;
        int             rc = 0;
        kcondvar_t      *cv = 0;

        /*  compute absolute timeout, if necessary  */
        if (timeoutms > 0)
                absto = ddi_get_lbolt() + drv_usectohz(timeoutms * 1000);

        /*  heed signals during wait?  */
        if (flags & CPQARY3_SYNCCMD_SEND_WAITSIG)
                waitsig = 1;

        /*  acquire the sw mutex for our wait  */
        mutex_enter(&(cpqary3p->sw_mutex));

        /*  submit command to controller  */
        mutex_enter(&(cpqary3p->hw_mutex));

        memp->cmdpvt_flag = CPQARY3_SYNC_SUBMITTED;
        memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
            CPQARY3_SYNCCMD_SUCCESS;
        if (EIO == cpqary3_submit(cpqary3p, memp->cmdlist_phyaddr)) {
                mutex_exit(&(cpqary3p->hw_mutex));
                mutex_exit(&(cpqary3p->sw_mutex));
                rc = -1;
                return (rc);
        }
        mutex_exit(&(cpqary3p->hw_mutex));

        /*  wait for command completion, timeout, or signal  */
        while (memp->cmdpvt_flag == CPQARY3_SYNC_SUBMITTED) {
                kmutex_t *mt = &(cpqary3p->sw_mutex);

                cv = &(cpqary3p->cv_ioctl_wait);
                /*  wait with the request behavior  */
                if (absto) {
                        clock_t   crc;
                        if (waitsig) {
                                crc = cv_timedwait_sig(cv, mt, absto);
                        } else {
                                crc = cv_timedwait(cv, mt, absto);
                        }
                        if (crc > 0)
                                rc = 0;
                        else
                                rc = (-1);
                } else {
                        if (waitsig) {
                                rc = cv_wait_sig(cv, mt);
                                if (rc > 0)
                                        rc = 0;
                                else
                                        rc = (-1);
                        } else {
                                cv_wait(cv, mt);
                                rc = 0;
                        }
                }


                /*
                 * if our wait was interrupted (timeout),
                 * then break here
                 */
                if (rc) {
                        break;
                }
        }

        /* our wait is done, so release the sw mutex */
        mutex_exit(&(cpqary3p->sw_mutex));

        /* return the results */
        return (rc);
}

/*
 * Function     :       cpqary3_detect_target_geometry
 * Description  :       This function determines the geometry for all
 *                      the existing targets for the controller.
 * Called By    :       cpqary3_tgt_init()
 * Parameters   :       per controller
 * Calls        :       cpqary3_synccmd_alloc(), cpqary3_synccmd_send()
 *                      cpqary3_synccmd_free()
 * Return Values:       SUCCESS / FAILURE
 *                      [ Shall return failure only if Memory constraints exist
 *                      or controller does not respond ]
 */
int8_t
cpqary3_detect_target_geometry(cpqary3_t *ctlr)
{
        int                     i;
        int8_t                  ld_count = 0;
        int8_t                  loop_cnt = 0;
        IdLogDrive              *idlogdrive;
        CommandList_t           *cmdlistp;
        cpqary3_cmdpvt_t        *cpqary3_cmdpvtp;

        RETURN_FAILURE_IF_NULL(ctlr);

        /*
         * Occupy a Command List
         * Allocate Memory for return data
         * If error, RETURN 0.
         * get the Request Block from the CommandList
         * Fill in the Request Packet with the corresponding values
         * Submit the Command and Poll for its completion
         * If success, continue else RETURN 0
         */

        /* Sync Changes */
        cpqary3_cmdpvtp = cpqary3_synccmd_alloc(ctlr, sizeof (IdLogDrive));
        if (cpqary3_cmdpvtp == NULL)
                return (CPQARY3_FAILURE);

        cmdlistp = cpqary3_cmdpvtp->cmdlist_memaddr;
        idlogdrive = (IdLogDrive *)cpqary3_cmdpvtp->driverdata->sg;
        /* Sync Changes */


        /* Update Cmd Header */
        cmdlistp->Header.SGList = 1;
        cmdlistp->Header.SGTotal = 1;
        cmdlistp->Header.Tag.drvinfo_n_err = CPQARY3_SYNCCMD_SUCCESS;

        /* Cmd Reques */
        cmdlistp->Request.CDBLen = CPQARY3_CDBLEN_16;
        cmdlistp->Request.CDB[0] = 0x26;
        cmdlistp->Request.CDB[6] = BMIC_IDENTIFY_LOGICAL_DRIVE;
        cmdlistp->Request.CDB[7] = (sizeof (IdLogDrive) >> 8) & 0xff;
        cmdlistp->Request.CDB[8] = sizeof (IdLogDrive) & 0xff;
        cmdlistp->Request.Type.Type = CISS_TYPE_CMD;
        cmdlistp->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
        cmdlistp->Request.Type.Direction = CISS_XFER_READ;

        /*
         * For all the Targets that exist, issue an IDENTIFY LOGICAL DRIVE.
         * That returns values which includes the dsired Geometry also.
         * Update the Geometry in the per-target structure.
         * NOTE : When the loop is executed for i=controller's SCSI ID, just
         * increament by one so that we are talking to the next logical
         * drive in our per-target structure.
         */

        /*
         * Depending upon the value of the variable legacy_mapping
         * set in cpqary3_attach(),
         * the target mapping algorithm to be used by the driver is decided.
         */

        if (ctlr->legacy_mapping == 1) {
                loop_cnt = ((ctlr->num_of_targets > CTLR_SCSI_ID) ?
                    (ctlr->num_of_targets + 1) : (ctlr->num_of_targets));

                for (i = 0; i < loop_cnt; i++) {
                        if (i == CTLR_SCSI_ID)  /* Go to Next logical target */
                                i++;

                        bzero(idlogdrive, sizeof (IdLogDrive));
                        cmdlistp->Request.CDB[1] =
                            ctlr->cpqary3_tgtp[i]->logical_id;

                        /* Always zero */
                        cmdlistp->Header.LUN.PhysDev.TargetId = 0;

                        /*
                         * Logical volume Id numbering scheme is as follows
                         * 0x00000, 0x00001, ... - for Direct Attached
                         * 0x10000, 0x10001, ... - If 1st Port of HBA is
                         * connected to  MSA20 / MSA500
                         * 0x20000, 0x20001, ... - If 2nd Port of HBA is
                         * connected to MSA20 / MSA500
                         */
                        cmdlistp->Header.LUN.PhysDev.Bus =
                            (ctlr->cpqary3_tgtp[i]->logical_id) >> 16;
                        cmdlistp->Header.LUN.PhysDev.Mode =
                            (cmdlistp->Header.LUN.PhysDev.Bus > 0) ?
                            MASK_PERIPHERIAL_DEV_ADDR : PERIPHERIAL_DEV_ADDR;

                        /*
                         * Submit the command
                         * Poll for its completion
                         * If polling is not successful, something is wrong
                         * with the controler
                         * Return FAILURE (No point in continuing if h/w is
                         * faulty !!!)
                         */

                        /* PERF */
                        cpqary3_cmdpvtp->complete = cpqary3_synccmd_complete;
                        /* PERF */

                        /* Sync Changes */
                        if (cpqary3_synccmd_send(ctlr, cpqary3_cmdpvtp, 90000,
                            CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
                                /* Timed out */
                                cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
                                return (CPQARY3_FAILURE);
                        }
                        if ((cpqary3_cmdpvtp->
                            cmdlist_memaddr->Header.Tag.drvinfo_n_err ==
                            CPQARY3_SYNCCMD_FAILURE) &&
                            (cpqary3_cmdpvtp->errorinfop->CommandStatus != 2)) {
                                DTRACE_PROBE1(id_logdrv_fail,
                                    ErrorInfo_t *, cpqary3_cmdpvtp->errorinfop);
                                cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
                                return (CPQARY3_FAILURE);
                        }
                        /* Sync Changes */

                        ctlr->cpqary3_tgtp[i]->properties.drive.heads =
                            idlogdrive->heads;
                        ctlr->cpqary3_tgtp[i]->properties.drive.sectors =
                            idlogdrive->sectors;

                        DTRACE_PROBE2(tgt_geometry_detect,
                            int, i, IdLogDrive *, idlogdrive);
                }
        } else {

                /*
                 * Fix for QXCR1000446657: Logical drives are re numbered
                 * after deleting a Logical drive.
                 * introduced, new variable ld_count, which gets
                 * incremented when the Target ID is found.
                 * And for i=controller's SCSI ID and LDs with holes are found,
                 * we continue talking to
                 * the next logical drive in the per-target structure
                 */

                for (i = 0; ld_count < ctlr->num_of_targets; i++) {
                        if (i == CTLR_SCSI_ID ||
                            ctlr->cpqary3_tgtp[i] == NULL)
                        /*  Go to the Next logical target  */
                        continue;
                        bzero(idlogdrive, sizeof (IdLogDrive));
                        cmdlistp->Request.CDB[1] =
                            ctlr->cpqary3_tgtp[i]->logical_id;
                        /* Always zero */
                        cmdlistp->Header.LUN.PhysDev.TargetId = 0;
                        /*
                         * Logical volume Id numbering scheme is as follows
                         * 0x00000, 0x00001, ... - for Direct Attached
                         * 0x10000, 0x10001, ... - If 1st Port of HBA is
                         * connected to  MSA20 / MSA500
                         * 0x20000, 0x20001, ... - If 2nd Port of HBA is
                         * connected to MSA20 / MSA500
                         */
                        cmdlistp->Header.LUN.PhysDev.Bus =
                            (ctlr->cpqary3_tgtp[i]->logical_id) >> 16;
                        cmdlistp->Header.LUN.PhysDev.Mode =
                            (cmdlistp->Header.LUN.PhysDev.Bus > 0) ?
                            MASK_PERIPHERIAL_DEV_ADDR : PERIPHERIAL_DEV_ADDR;
                        /* PERF */
                        cpqary3_cmdpvtp->complete = cpqary3_synccmd_complete;
                        /* PERF */

                        /*
                         * Submit the command
                         * Poll for its completion
                         * If polling is not successful, something is wrong
                         * with the controler
                         * Return FAILURE (No point in continuing if h/w is
                         * faulty !!!)
                         */

                        /* Sync Changes */
                        if (cpqary3_synccmd_send(ctlr, cpqary3_cmdpvtp, 90000,
                            CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
                                /* Timed out */
                                cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
                                return (CPQARY3_FAILURE);
                        }
                        if ((cpqary3_cmdpvtp->
                            cmdlist_memaddr->Header.Tag.drvinfo_n_err ==
                            CPQARY3_SYNCCMD_FAILURE) &&
                            (cpqary3_cmdpvtp->errorinfop->CommandStatus != 2)) {
                                DTRACE_PROBE1(id_logdrv_fail,
                                    ErrorInfo_t *, cpqary3_cmdpvtp->errorinfop);
                                cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
                                return (CPQARY3_FAILURE);
                        }
                        /* Sync Changes */

                        ctlr->cpqary3_tgtp[i]->properties.drive.heads =
                            idlogdrive->heads;
                        ctlr->cpqary3_tgtp[i]->properties.drive.sectors =
                            idlogdrive->sectors;

                        DTRACE_PROBE2(tgt_geometry_detect,
                            int, i, IdLogDrive *, idlogdrive);

                        ld_count++;
                }
        }

        /* Sync Changes */
        cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
        /* Sync Changes */

        return (CPQARY3_SUCCESS);
}