Skip to contentSkip to conversation

qla4xxx: new driver -- request for review.

PATCH In review Andrew Vasquez 5 participants 15 messages Sep 6, 2005 – Oct 28, 2005

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover qla4xxx: new driver -- request for review.

All,

Here's a first-pass run for submission-review of the qla4xxx driver for QLogic's ISP4010 and ISP4022 products.

The patchset breaks-down as follows:

o Driver definitions
o initialisation routines
o OS integration files
o iSNS routines
o ISR routines
o Mailbox routines
o Support routines
o Settings

There's still quite a bit of work to do:

- All command-queueing has been removed, so cable-pulls cannot be tolerated. We need to complete the iSCSI transport integration (which seems to no be present).

Are the iSCSI block()/unblock() routines moving forward similarly to FC and now SAS? Mike Christie had submitted patches:

	[PATCH] add block/unblock to iscsi class
	http://marc.theaimsgroup.com/?l=linux-scsi&m=111201856631844&w=2

some time back. Why were the patches dropped?

- Request-queue management cleanup

- iSNS code rework

- Additional cruft removal (unused structures and functions)

All patches can be found at the following URL:

	ftp://ftp.qlogic.com/outgoing/linux/iSCSI/upstream/5.00.03b15-k/

Comments and criticisms welcomed.

Regards,
Andrew Vasquez
QLogic Corporation

Signature · 1 line
Andrew Vasquez
#1 lore raw

PATCH 2/8 qla4xxx: initialization routines

ISP4xxx driver initialization routines.

---

 drivers/scsi/qla4xxx/ql4_init.c | 1753 +++++++++++++++++++++++++++++++++++++++
 drivers/scsi/qla4xxx/ql4_iocb.c |  595 +++++++++++++
 2 files changed, 2348 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/ql4_init.c
 create mode 100644 drivers/scsi/qla4xxx/ql4_iocb.c

6755c3b78809194fe31f551b3862639ae513b9a9

drivers/scsi/qla4xxx/ql4_init.c new file +1753 -0
new file mode 100644
@@ -0,0 +1,1753 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:
+ *
+ * 	1. Redistribution of source code must retain the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission.
+ *
+ * You may redistribute the hardware specific firmware binary file
+ * under the following terms:
+ *
+ * 	1. Redistribution of source code (only if applicable),
+ * 	   must retain the above copyright notice, this list of
+ * 	   conditions and the following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission
+ *
+ * REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+ * THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+ * CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+ * OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+ * TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ * ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+ * COMBINATION WITH THIS PROGRAM.
+ */
+
+#include "ql4_def.h"
+
+#include <linux/delay.h>
+
+/*
+*  QLogic ISP4xxx Hardware Support Function Prototypes.
+ */
+extern int ql4xdiscoverywait;
+
+/*
+ * Local routines
+ */
+static int qla4xxx_start_firmware(scsi_qla_host_t * ha);
+static int qla4xxx_config_nvram(scsi_qla_host_t * ha);
+
+static void
+ql4xxx_set_mac_number(scsi_qla_host_t * ha)
+{
+	uint32_t value;
+	uint8_t func_number;
+	unsigned long flags;
+
+	/* Get the function number */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	value = RD_REG_DWORD(&ha->reg->ctrl_status);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	func_number = (uint8_t) ((value >> 4) & 0x30);
+	switch (value & ISP_CONTROL_FN_MASK) {
+	case ISP_CONTROL_FN0_SCSI:
+		ha->mac_index = 1;
+		break;
+	case ISP_CONTROL_FN1_SCSI:
+		ha->mac_index = 3;
+		break;
+	default:
+		DEBUG2(printk("scsi%ld: %s: Invalid function number, "
+		    "ispControlStatus = 0x%x\n", ha->host_no, __func__, value));
+		break;
+	}
+	DEBUG2(printk("scsi%ld: %s: mac_index %d.\n", ha->host_no, __func__,
+	    ha->mac_index));
+}
+
+/**************************************************************************
+ * qla4xxx_free_ddb
+ *	This routine deallocates and unlinks the specified ddb_entry from the
+ *	adapter's
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	ddb_entry - Pointer to device database entry
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+void
+qla4xxx_free_ddb(scsi_qla_host_t * ha, struct ddb_entry *ddb_entry)
+{
+	/* Remove device entry from list */
+	list_del_init(&ddb_entry->list);
+
+	/* Remove device pointer from index mapping arrays */
+	ha->fw_ddb_index_map[ddb_entry->fw_ddb_index] =
+	    (ddb_entry_t *) INVALID_ENTRY;
+	ha->tot_ddbs--;
+
+	/* Free memory for device entry */
+	kfree(ddb_entry);
+}
+
+/**************************************************************************
+ * qla4xxx_free_ddb_list
+ *	This routine deallocates and removes all devices on the sppecified
+ *	adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+void
+qla4xxx_free_ddb_list(scsi_qla_host_t * ha)
+{
+	struct list_head *ptr;
+	struct ddb_entry *ddb_entry;
+
+	while (!list_empty(&ha->ddb_list)) {
+		/* Remove device entry from head of list */
+		ptr = ha->ddb_list.next;
+		list_del_init(ptr);
+
+		/* Free memory for device entry */
+		ddb_entry = list_entry(ptr, struct ddb_entry, list);
+		qla4xxx_free_ddb(ha, ddb_entry);
+	}
+}
+
+/*
+ * qla4xxx_init_rings
+ *	This routine initializes the internal queues for the specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Remarks:
+ *	The QLA4010 requires us to restart the queues at index 0.
+ *	The QLA4000 doesn't care, so just default to QLA4010's requirement.
+ * Returns:
+ *	QLA_SUCCESS - Always return success.
+ *
+ * Context:
+ *	Kernel context.
+ */
+int
+qla4xxx_init_rings(scsi_qla_host_t * ha)
+{
+	uint16_t i;
+	unsigned long flags = 0;
+
+	/* Initialize request queue. */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	ha->request_out = 0;
+	ha->request_in = 0;
+	ha->request_ptr = &ha->request_ring[ha->request_in];
+	ha->req_q_count = REQUEST_QUEUE_DEPTH;
+
+	/* Initialize response queue. */
+	ha->response_in = 0;
+	ha->response_out = 0;
+	ha->response_ptr = &ha->response_ring[ha->response_out];
+
+	/*
+	 * Initialize DMA Shadow registers.  The firmware is really supposed to
+	 * take care of this, but on some uniprocessor systems, the shadow
+	 * registers aren't cleared-- causing the interrupt_handler to think
+	 * there are responses to be processed when there aren't.
+	 */
+	ha->shadow_regs->req_q_out = __constant_cpu_to_le32(0);
+	ha->shadow_regs->rsp_q_in = __constant_cpu_to_le32(0);
+	wmb();
+
+	WRT_REG_DWORD(&ha->reg->req_q_in, 0);
+	WRT_REG_DWORD(&ha->reg->rsp_q_out, 0);
+	PCI_POSTING(&ha->reg->rsp_q_out);
+
+	/* Initialize active array */
+	for (i = 0; i < MAX_SRBS; i++)
+		ha->active_srb_array[i] = 0;
+	ha->active_srb_count = 0;
+
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return QLA_SUCCESS;
+}
+
+#define qla4xxx_mac_is_equal(mac1, mac2) (memcmp(mac1, mac2, MAC_ADDR_LEN) == 0)
+
+/**************************************************************************
+ * qla4xxx_validate_mac_address
+ *	This routine validates the M.A.C. Address(es) of the adapter
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully validated M.A.C. address
+ *	QLA_ERROR   - Failed to validate M.A.C. address
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_validate_mac_address(scsi_qla_host_t *ha)
+{
+	FLASH_SYS_INFO *sys_info = NULL;
+	dma_addr_t sys_info_dma;
+	int status = QLA_ERROR;
+
+	sys_info = dma_alloc_coherent(&ha->pdev->dev, sizeof(*sys_info),
+	    &sys_info_dma, GFP_KERNEL);
+	if (sys_info == NULL) {
+		DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n",
+		    ha->host_no, __func__));
+
+		goto exit_validate_mac_no_free;
+	}
+	memset(sys_info, 0, sizeof(*sys_info));
+
+	/* Get flash sys info */
+	if (qla4xxx_get_flash(ha, sys_info_dma, FLASH_OFFSET_SYS_INFO,
+	    sizeof(*sys_info)) != QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: get_flash FLASH_OFFSET_SYS_INFO "
+		    "failed\n", ha->host_no, __func__));
+
+		goto exit_validate_mac;
+	}
+
+	/* Save M.A.C. address & serial_number */
+	memcpy(ha->my_mac, &sys_info->physAddr[0].address[0],
+	    min(sizeof(ha->my_mac), sizeof(sys_info->physAddr[0].address)));
+	memcpy(ha->serial_number, &sys_info->acSerialNumber,
+	    min(sizeof(ha->serial_number), sizeof(sys_info->acSerialNumber)));
+
+	status = QLA_SUCCESS;
+
+exit_validate_mac:
+	dma_free_coherent(&ha->pdev->dev, sizeof(*sys_info), sys_info,
+	    sys_info_dma);
+
+exit_validate_mac_no_free:
+	return status;
+}
+
+/*
+ * qla4xxx_init_local_data
+ *	This routine initializes the local data for the specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully initialized local data
+ *	QLA_ERROR   - Failed to initialize local data
+ *
+ * Context:
+ *	Kernel context.
+ */
+static int
+qla4xxx_init_local_data(scsi_qla_host_t *ha)
+{
+	int i;
+
+	/* Initialize passthru PDU list */
+	for (i = 0; i < (MAX_PDU_ENTRIES - 1); i++)
+		ha->pdu_queue[i].Next = &ha->pdu_queue[i + 1];
+	ha->free_pdu_top = &ha->pdu_queue[0];
+	ha->free_pdu_bottom = &ha->pdu_queue[MAX_PDU_ENTRIES - 1];
+	ha->free_pdu_bottom->Next = NULL;
+	ha->pdu_active = 0;
+
+	/* Initilize aen queue */
+	ha->aen_q_count = MAX_AEN_ENTRIES;
+
+	/* Initialize local iSNS data */
+	qla4xxx_isns_init_attributes(ha);
+	ha->isns_flags = 0;
+	atomic_set(&ha->isns_restart_timer, 0);
+	ha->isns_connection_id = 0;
+	ha->isns_remote_port_num = 0;
+	ha->isns_scn_port_num = 0;
+	ha->isns_esi_port_num = 0;
+	ha->isns_nsh_port_num = 0;
+	memset(ha->isns_entity_id, 0, sizeof(ha->isns_entity_id));
+	ha->isns_num_discovered_targets = 0;
+
+	return qla4xxx_get_firmware_status(ha);
+}
+
+static int
+qla4xxx_fw_ready(scsi_qla_host_t * ha)
+{
+	uint32_t timeout_count;
+	int ready = 0;
+
+	DEBUG2(ql4_printk(KERN_INFO, ha, "Waiting for Firmware Ready..\n"));
+	for (timeout_count = ADAPTER_INIT_TOV; timeout_count > 0;
+	     timeout_count--) {
+		if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags))
+			qla4xxx_get_dhcp_ip_address(ha);
+		/* Get firmware state. */
+		if (qla4xxx_get_firmware_state(ha) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s: unable to get firmware "
+			    "state\n", ha->host_no, __func__));
+			break;
+
+		}
+
+		if (ha->firmware_state & FW_STATE_ERROR) {
+			DEBUG2(printk("scsi%ld: %s: an unrecoverable error has "
+			    "occurred\n", ha->host_no, __func__));
+			break;
+
+		}
+		if (ha->firmware_state & FW_STATE_CONFIG_WAIT) {
+			/*
+			 * The firmware has not yet been issued an Initialize
+			 * Firmware command, so issue it now.
+			 */
+			if (qla4xxx_initialize_fw_cb(ha) == QLA_ERROR)
+				break;
+
+			/* Go back and test for ready state - no wait. */
+			continue;
+		}
+
+		if (ha->firmware_state == FW_STATE_READY) {
+			DEBUG2(ql4_printk(KERN_INFO, ha, "Firmware Ready..\n"));
+			/* The firmware is ready to process SCSI commands. */
+			DEBUG2(ql4_printk(KERN_INFO, ha,
+			    "scsi%ld: %s: MEDIA TYPE - %s\n", ha->host_no,
+			    __func__, (ha->addl_fw_state &
+			    FW_ADDSTATE_OPTICAL_MEDIA) != 0 ? "OPTICAL" :
+				"COPPER"));
+			DEBUG2(ql4_printk(KERN_INFO, ha,
+			    "scsi%ld: %s: DHCP STATE Enabled " "%s\n",
+			    ha->host_no, __func__, (ha->addl_fw_state &
+			    FW_ADDSTATE_DHCP_ENABLED) != 0 ? "YES" : "NO"));
+			DEBUG2(ql4_printk(KERN_INFO, ha,
+			    "scsi%ld: %s: LINK %s\n", ha->host_no, __func__,
+			    (ha->addl_fw_state & FW_ADDSTATE_LINK_UP) != 0 ?
+			    "UP" : "DOWN"));
+			DEBUG2(ql4_printk(KERN_INFO, ha,
+			    "scsi%ld: %s: iSNS Service " "Started %s\n",
+			    ha->host_no, __func__, (ha->addl_fw_state &
+			    FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? "YES" : "NO"));
+
+			ready = 1;
+			/* If iSNS is enabled, start the iSNS service now. */
+			if ((ha->tcp_options & TOPT_ISNS_ENABLE) &&
+			    !IPAddrIsZero(ha->isns_ip_address)) {
+				uint32_t ip_addr = 0;
+
+				IPAddr2Uint32(ha->isns_ip_address, &ip_addr);
+				ql4_printk(KERN_INFO, ha,
+				    "Initializing ISNS..\n");
+				qla4xxx_isns_reenable(ha, ip_addr,
+				    ha->isns_server_port_number);
+			}
+			break;
+		}
+		DEBUG2(printk("scsi%ld: %s: waiting on fw, state=%x:%x - "
+		    "seconds expired= %d\n", ha->host_no, __func__,
+		    ha->firmware_state, ha->addl_fw_state, timeout_count));
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1 * HZ);
+	}			/* end of for */
+
+	if (timeout_count <= 0)
+		DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n",
+		    ha->host_no, __func__));
+	if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS)
+		ready = 1;
+
+	return ready;
+}
+
+/*
+ * qla4xxx_init_firmware
+ *	This routine initializes the firmware.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully initialized firmware
+ *	QLA_ERROR   - Failed to initialize firmware
+ *
+ * Context:
+ *	Kernel context.
+ */
+static int
+qla4xxx_init_firmware(scsi_qla_host_t * ha)
+{
+	int status = QLA_ERROR;
+
+	ql4_printk(KERN_INFO, ha, "Initializing firmware..\n");
+	if (qla4xxx_initialize_fw_cb(ha) == QLA_ERROR) {
+		DEBUG2(printk("scsi%ld: %s: Failed to initialize firmware "
+		    "control block\n", ha->host_no, __func__));
+		return status;
+	}
+	if (!qla4xxx_fw_ready(ha))
+		return status;
+
+	set_bit(AF_ONLINE, &ha->flags);
+	return qla4xxx_get_firmware_status(ha);
+}
+
+/**************************************************************************
+ * qla4xxx_find_isns_targets
+ *	This routine locates a device handle for ther given iSNS information.
+ *	If device doesn't exist, returns NULL.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      ip_addr - Pointer to IP address
+ *      alias - Pointer to iSCSI alias
+ *
+ * Returns:
+ *	Pointer to the corresponding internal device database structure
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static inline int
+qla4xxx_is_discovered_target(scsi_qla_host_t * ha, uint8_t * ip_addr,
+    uint8_t * alias, uint8_t * name_str)
+{
+	ISNS_DISCOVERED_TARGET *discovered_target = NULL;
+	int i, j;
+
+	for (i = 0; i < ha->isns_num_discovered_targets; i++) {
+		discovered_target = &ha->isns_disc_tgt_databasev[i];
+
+		for (j = 0; j < discovered_target->NumPortals; j++) {
+			if (memcmp(discovered_target->Portal[j].IPAddr, ip_addr,
+			    min(sizeof(discovered_target->Portal[j].IPAddr),
+				    sizeof(*ip_addr)) == 0)
+			    && memcmp(discovered_target->Alias, alias,
+				    min(sizeof(discovered_target->Alias),
+					    sizeof(*alias)) == 0)
+			    && memcmp(discovered_target->NameString, name_str,
+				    min(sizeof(discovered_target->Alias),
+					    sizeof(*name_str)) == 0)) {
+
+				return QLA_SUCCESS;
+			}
+		}
+	}
+	return QLA_ERROR;
+}
+
+static struct ddb_entry *
+qla4xxx_get_ddb_entry(scsi_qla_host_t *ha, uint32_t fw_ddb_index)
+{
+	DEV_DB_ENTRY *fw_ddb_entry = NULL;
+	dma_addr_t fw_ddb_entry_dma;
+	struct ddb_entry *ddb_entry = NULL;
+	int found = 0;
+	uint32_t device_state;
+
+	/* Make sure the dma buffer is valid */
+	fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+	    &fw_ddb_entry_dma, GFP_KERNEL);
+	if (fw_ddb_entry == NULL) {
+		DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n",
+		    ha->host_no, __func__));
+		return NULL;
+	}
+
+	if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry,
+	    fw_ddb_entry_dma, NULL, NULL, &device_state, NULL, NULL, NULL) ==
+	    QLA_ERROR) {
+		DEBUG2(printk("scsi%ld: %s: failed get_ddb_entry for "
+		    "fw_ddb_index %d\n", ha->host_no, __func__, fw_ddb_index));
+		return NULL;
+	}
+
+	/* Allocate DDB if not already allocated. */
+	DEBUG2(printk("scsi%ld: %s: Looking for ddb[%d]\n", ha->host_no,
+	    __func__, fw_ddb_index));
+	list_for_each_entry(ddb_entry, &ha->ddb_list, list) {
+		if (memcmp(ddb_entry->iscsi_name, fw_ddb_entry->iscsiName,
+		    ISCSI_NAME_SIZE) == 0) {
+			found++;
+			break;
+		}
+	}
+
+	if (!found) {
+		DEBUG2(printk("scsi%ld: %s: ddb[%d] not found - allocating "
+		    "new ddb\n", ha->host_no, __func__, fw_ddb_index));
+		ddb_entry = qla4xxx_alloc_ddb(ha, fw_ddb_index);
+	}
+
+	/* if not found allocate new ddb */
+	dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+	    fw_ddb_entry_dma);
+
+	return ddb_entry;
+}
+
+/**************************************************************************
+ * qla4xxx_update_ddb_entry
+ *	This routine updates the driver's internal device database entry
+ *	with information retrieved from the firmware's device database
+ *	entry for the specified device.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	ddb_entry - Pointer to device database entry
+ *
+ * Output:
+ *	ddb_entry - Structure filled in.
+ *
+ * Remarks:
+ *	The ddb_entry->fw_ddb_index field must be initialized prior to
+ *	calling this routine
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully update ddb_entry
+ *	QLA_ERROR   - Failed to update ddb_entry
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_update_ddb_entry(scsi_qla_host_t * ha, struct ddb_entry *ddb_entry,
+    uint32_t fw_ddb_index)
+{
+	DEV_DB_ENTRY *fw_ddb_entry = NULL;
+	dma_addr_t fw_ddb_entry_dma;
+	int status = QLA_ERROR;
+
+	if (ddb_entry == NULL) {
+		DEBUG2(printk("scsi%ld: %s: ddb_entry is NULL\n", ha->host_no,
+		    __func__));
+		goto exit_update_ddb;
+	}
+
+	/* Make sure the dma buffer is valid */
+	fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+	    &fw_ddb_entry_dma, GFP_KERNEL);
+	if (fw_ddb_entry == NULL) {
+		DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n",
+		    ha->host_no, __func__));
+
+		goto exit_update_ddb;
+	}
+
+	if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry,
+	    fw_ddb_entry_dma, NULL, NULL, &ddb_entry->fw_ddb_device_state, NULL,
+	    &ddb_entry->tcp_source_port_num, &ddb_entry->connection_id) ==
+	    QLA_ERROR) {
+		DEBUG2(printk("scsi%ld: %s: failed get_ddb_entry for "
+		    "fw_ddb_index %d\n", ha->host_no, __func__, fw_ddb_index));
+
+		goto exit_update_ddb;
+	}
+
+	status = QLA_SUCCESS;
+	ddb_entry->target_session_id = le16_to_cpu(fw_ddb_entry->TSID);
+	ddb_entry->task_mgmt_timeout =
+	    le16_to_cpu(fw_ddb_entry->taskMngmntTimeout);
+	ddb_entry->CmdSn = 0;
+	ddb_entry->exe_throttle = le16_to_cpu(fw_ddb_entry->exeThrottle);
+	ddb_entry->default_relogin_timeout =
+	    le16_to_cpu(fw_ddb_entry->taskMngmntTimeout);
+	ddb_entry->default_time2wait = le16_to_cpu(fw_ddb_entry->minTime2Wait);
+
+	/* Update index in case it changed */
+	ddb_entry->fw_ddb_index = fw_ddb_index;
+	ha->fw_ddb_index_map[fw_ddb_index] = ddb_entry;
+
+	memcpy(&ddb_entry->iscsi_name[0], &fw_ddb_entry->iscsiName[0],
+	    min(sizeof(ddb_entry->iscsi_name),
+		    sizeof(fw_ddb_entry->iscsiName)));
+	memcpy(&ddb_entry->ip_addr[0], &fw_ddb_entry->ipAddr[0],
+	    min(sizeof(ddb_entry->ip_addr), sizeof(fw_ddb_entry->ipAddr)));
+
+	if (qla4xxx_is_discovered_target(ha, fw_ddb_entry->ipAddr,
+	    fw_ddb_entry->iSCSIAlias, fw_ddb_entry->iscsiName) == QLA_SUCCESS)
+		set_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags);
+
+	DEBUG2(printk("scsi%ld: %s: ddb[%d] - State= %x status= %d.\n",
+	    ha->host_no, __func__, fw_ddb_index,
+	    ddb_entry->fw_ddb_device_state, status);)
+
+exit_update_ddb:
+	if (fw_ddb_entry)
+		dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+		    fw_ddb_entry, fw_ddb_entry_dma);
+
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_alloc_ddb
+ *	This routine allocates a ddb_entry, ititializes some values, and
+ *	inserts it into the ddb list.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      fw_ddb_index - Firmware's device database index
+ *
+ * Returns:
+ *	Pointer to internal device database structure
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+struct ddb_entry *
+qla4xxx_alloc_ddb(scsi_qla_host_t * ha, uint32_t fw_ddb_index)
+{
+	struct ddb_entry *ddb_entry;
+
+	DEBUG2(printk("scsi%ld: %s: fw_ddb_index [%d]\n", ha->host_no,
+	    __func__, fw_ddb_index));
+
+	ddb_entry = (struct ddb_entry *)kmalloc(sizeof(*ddb_entry), GFP_ATOMIC);
+	if (ddb_entry == NULL) {
+		DEBUG2(printk("scsi%ld: %s: Unable to allocate memory "
+		    "to add fw_ddb_index [%d]\n", ha->host_no, __func__,
+		    fw_ddb_index));
+		return ddb_entry;
+	}
+
+	memset(ddb_entry, 0, sizeof(*ddb_entry));
+	ddb_entry->fw_ddb_index = fw_ddb_index;
+	atomic_set(&ddb_entry->port_down_timer, ha->port_down_retry_count);
+	atomic_set(&ddb_entry->retry_relogin_timer, INVALID_ENTRY);
+	atomic_set(&ddb_entry->relogin_timer, 0);
+	atomic_set(&ddb_entry->relogin_retry_count, 0);
+	atomic_set(&ddb_entry->state, DDB_STATE_ONLINE);
+	list_add_tail(&ddb_entry->list, &ha->ddb_list);
+	ha->fw_ddb_index_map[fw_ddb_index] = ddb_entry;
+	ha->tot_ddbs++;
+
+	return ddb_entry;
+}
+
+/**************************************************************************
+ * qla4xxx_configure_ddbs
+ *	This routine searches for all valid firmware ddb entries and builds
+ *	an internal ddb list.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Remarks:
+ *	Ddbs that are considered valid are those with a device state of
+ *	SESSION_ACTIVE.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully built internal ddb list, if targets available
+ *	QLA_ERROR   - Error on a mailbox command
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_build_ddb_list(scsi_qla_host_t *ha)
+{
+	int status = QLA_SUCCESS;
+	uint32_t fw_ddb_index = 0;
+	uint32_t next_fw_ddb_index = 0;
+	uint32_t ddb_state;
+	uint32_t conn_err, err_code;
+	struct ddb_entry *ddb_entry;
+
+	ql4_printk(KERN_INFO, ha, "Initializing DDBs ...\n");
+	for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES;
+	     fw_ddb_index = next_fw_ddb_index) {
+		/* First, let's see if a device exists here */
+		if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL,
+		    &next_fw_ddb_index, &ddb_state, &conn_err, NULL, NULL) ==
+		    QLA_ERROR) {
+			DEBUG2(printk("scsi%ld: %s: get_ddb_entry, "
+			    "fw_ddb_index %d failed", ha->host_no, __func__,
+			    fw_ddb_index));
+			return QLA_ERROR;
+		}
+
+		DEBUG2(printk("scsi%ld: %s: Getting DDB[%d] ddbstate=0x%x, "
+		    "next_fw_ddb_index=%d.\n", ha->host_no, __func__,
+		    fw_ddb_index, ddb_state, next_fw_ddb_index));
+
+		/* Add DDB to internal our ddb list. */
+		ddb_entry = qla4xxx_get_ddb_entry(ha, fw_ddb_index);
+		if (ddb_entry == NULL) {
+			DEBUG2(printk("scsi%ld: %s: Unable to allocate memory "
+			    "for device at fw_ddb_index %d\n", ha->host_no,
+			    __func__, fw_ddb_index));
+			return QLA_ERROR;
+		}
+		/* Fill in the device structure */
+		if (qla4xxx_update_ddb_entry(ha, ddb_entry, fw_ddb_index) ==
+		    QLA_ERROR) {
+			ha->fw_ddb_index_map[fw_ddb_index] =
+			    (struct ddb_entry *)INVALID_ENTRY;
+
+			//      qla4xxx_free_ddb(ha, ddb_entry);
+			DEBUG2(printk("scsi%ld: %s: update_ddb_entry failed "
+			    "for fw_ddb_index %d.\n", ha->host_no, __func__,
+			    fw_ddb_index));
+			return QLA_ERROR;
+		}
+
+		/* if fw_ddb with session active state found,
+		 * add to ddb_list */
+		DEBUG2(printk("scsi%ld: %s: DDB[%d] added to list\n",
+		    ha->host_no, __func__, fw_ddb_index));
+
+		/* Issue relogin, if necessary. */
+		if (ddb_state == DDB_DS_SESSION_FAILED ||
+		    ddb_state == DDB_DS_NO_CONNECTION_ACTIVE) {
+
+			atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
+
+			/* Try and login to device */
+			DEBUG2(printk("scsi%ld: %s: Login to DDB[%d]\n",
+			    ha->host_no, __func__, fw_ddb_index));
+			err_code = ((conn_err & 0x00ff0000) >> 16);
+			if (err_code == 0x1c || err_code == 0x06) {
+				DEBUG2(printk("%s send target completed or "
+				    "access denied failure\n", __func__));
+			} else {
+				qla4xxx_set_ddb_entry(ha, fw_ddb_index, NULL,
+				    0);
+			}
+		}
+
+		/* We know we've reached the last device when
+		 * next_fw_ddb_index is 0 */
+		if (next_fw_ddb_index == 0)
+			break;
+	}
+
+	ql4_printk(KERN_INFO, ha, "DDB list done..\n");
+
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_devices_ready
+ *	This routine waits up to ql4xdiscoverywait seconds
+ *	F/W database during driver load time.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully (re)built internal ddb list
+ *	QLA_ERROR   - Failed to (re)build internal ddb list
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_devices_ready(scsi_qla_host_t * ha)
+{
+	int halt_wait;
+	unsigned long discovery_wtime;
+	struct ddb_entry *ddb_entry;
+	uint32_t fw_ddb_index;
+	uint32_t next_fw_ddb_index;
+	uint32_t fw_ddb_device_state;
+	uint32_t conn_err;
+	uint32_t err_code;
+
+	discovery_wtime = jiffies + (ql4xdiscoverywait * HZ);
+
+	DEBUG(printk("Waiting (%d) for devices ...\n", ql4xdiscoverywait));
+	do {
+		/* poll for AEN. */
+		qla4xxx_get_firmware_state(ha);
+		if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) {
+			/* Set time-between-relogin timer */
+			qla4xxx_process_aen(ha, RELOGIN_DDB_CHANGED_AENS);
+		}
+
+		/* if no relogins active or needed, halt discvery wait */
+		halt_wait = 1;
+
+		/* scan for relogins
+		 * ----------------- */
+		for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES;
+		    fw_ddb_index = next_fw_ddb_index) {
+			if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0,
+			    NULL, &next_fw_ddb_index, &fw_ddb_device_state,
+			    &conn_err, NULL, NULL) == QLA_ERROR)
+				return QLA_ERROR;
+
+			if (fw_ddb_device_state == DDB_DS_LOGIN_IN_PROCESS)
+				halt_wait = 0;
+
+			if (fw_ddb_device_state == DDB_DS_SESSION_FAILED ||
+			    fw_ddb_device_state ==
+			    DDB_DS_NO_CONNECTION_ACTIVE) {
+
+				/*
+				 * Don't want to do a relogin if connection
+				 * error is 0x1c.
+				 */
+				err_code = ((conn_err & 0x00ff0000) >> 16);
+				if (err_code == 0x1c || err_code == 0x06) {
+					DEBUG2(printk(
+					    "%s send target completed or "
+					    "access denied failure\n",
+					    __func__);)
+				} else {
+					/* We either have a device that is in
+					 * the process of relogging in or a
+					 * device that is waiting to be
+					 * relogged in */
+					halt_wait = 0;
+
+					ddb_entry =
+					    qla4xxx_lookup_ddb_by_fw_index(ha,
+						fw_ddb_index);
+					if (ddb_entry == NULL)
+						return QLA_ERROR;
+
+					if (ddb_entry->dev_scan_wait_to_start_relogin != 0
+					    && time_after_eq(jiffies,
+						    ddb_entry->dev_scan_wait_to_start_relogin))
+					{
+						ddb_entry->
+						    dev_scan_wait_to_start_relogin
+						    = 0;
+						qla4xxx_set_ddb_entry(ha,
+						    fw_ddb_index, NULL, 0);
+					}
+				}
+			}
+
+			/* We know we've reached the last device when
+			 * next_fw_ddb_index is 0 */
+			if (next_fw_ddb_index == 0)
+				break;
+		}
+
+		if (halt_wait) {
+			DEBUG2(printk("scsi%ld: %s: Delay halted.  Devices "
+			    "Ready.\n", ha->host_no, __func__));
+			return QLA_SUCCESS;
+		}
+
+		/* delay */
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ * 2);
+	} while (!time_after_eq(jiffies, discovery_wtime));
+
+	DEBUG3(qla4xxx_get_conn_event_log(ha));
+
+	return (QLA_SUCCESS);
+}
+
+static void
+qla4xxx_flush_AENS(scsi_qla_host_t *ha)
+{
+	unsigned long wtime;
+
+	/* Flush the 0x8014 AEN from the firmware as a result of
+	 * Auto connect. We are basically doing get_firmware_ddb()
+	 * to determine whether we need to log back in or not.
+	 *  Trying to do a set ddb before we have processed 0x8014
+	 *  will result in another set_ddb() for the same ddb. In other
+	 *  words there will be stale entries in the aen_q.
+	 */
+	wtime = jiffies + (2 * HZ);
+	do {
+		if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS)
+			if (ha->firmware_state & (BIT_2 | BIT_0))
+				return;
+
+		if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags))
+			qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+
+		/* delay */
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ * 1);
+
+	} while (!time_after_eq(jiffies, wtime));
+
+}
+
+static int
+qla4xxx_initialize_ddb_list(scsi_qla_host_t *ha)
+{
+	uint16_t fw_ddb_index;
+	int status = QLA_SUCCESS;
+
+	/* free the ddb list if is not empty */
+	if (!list_empty(&ha->ddb_list))
+		qla4xxx_free_ddb_list(ha);
+
+	for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; fw_ddb_index++)
+		ha->fw_ddb_index_map[fw_ddb_index] =
+		    (struct ddb_entry *)INVALID_ENTRY;
+
+	ha->tot_ddbs = 0;
+
+	qla4xxx_flush_AENS(ha);
+
+	/*
+	 * First perform device discovery for active
+	 * fw ddb indexes and build
+	 * ddb list.
+	 */
+	if ((status = qla4xxx_build_ddb_list(ha)) == QLA_ERROR)
+		return (status);
+
+	/* Wait for an AEN */
+	qla4xxx_devices_ready(ha);
+
+	/*
+	 * Targets can come online after the inital discovery, so processing
+	 * the aens here will catch them.
+	 */
+	if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags))
+		qla4xxx_process_aen(ha, PROCESS_ALL_AENS);
+
+	return status;
+}
+
+/*
+ * qla4xxx_update_ddb_list
+ *	This routine obtains device information from the F/W database after
+ *	firmware or adapter resets.  The device table is preserved.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully updated internal ddb list
+ *	QLA_ERROR   - Failed to update internal ddb list
+ *
+ * Context:
+ *	Kernel context.
+ */
+int
+qla4xxx_reinitialize_ddb_list(scsi_qla_host_t * ha)
+{
+	int status = QLA_SUCCESS;
+	struct ddb_entry *ddb_entry, *detemp;
+
+	/* Update the device information for all devices. */
+	list_for_each_entry_safe(ddb_entry, detemp, &ha->ddb_list, list) {
+		qla4xxx_update_ddb_entry(ha, ddb_entry,
+		    ddb_entry->fw_ddb_index);
+		if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) {
+			atomic_set(&ddb_entry->state, DDB_STATE_ONLINE);
+			DEBUG2(printk ("scsi%ld: %s: ddb index [%d] marked "
+			    "ONLINE\n", ha->host_no, __func__,
+			    ddb_entry->fw_ddb_index));
+		} else if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
+			qla4xxx_mark_device_missing(ha, ddb_entry);
+	}
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_relogin_device
+ *	This routine does a session relogin with the specified device.
+ *	The ddb entry must be assigned prior to making this call.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	ddb_entry - Pointer to device database entry
+ *
+ * Returns:
+ *    QLA_SUCCESS = Successfully relogged in device
+ *    QLA_ERROR   = Failed to relogin device
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_relogin_device(scsi_qla_host_t * ha, struct ddb_entry * ddb_entry)
+{
+	uint16_t relogin_timer;
+
+	relogin_timer = max(ddb_entry->default_relogin_timeout,
+	    (uint16_t)RELOGIN_TOV);
+	atomic_set(&ddb_entry->relogin_timer, relogin_timer);
+
+	DEBUG2(printk("scsi%ld: Relogin index [%d]. TOV=%d\n", ha->host_no,
+	    ddb_entry->fw_ddb_index, relogin_timer));
+
+	qla4xxx_set_ddb_entry(ha, ddb_entry->fw_ddb_index, NULL, 0);
+
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4010_topcat_soft_reset
+ *	This routine determines if the QLA4040 TopCat chip is present.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	None.
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static void
+qla4010_get_topcat_presence(scsi_qla_host_t * ha)
+{
+	unsigned long flags;
+	uint16_t topcat;
+
+	QL4XXX_LOCK_NVRAM(ha);
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	topcat = RD_NVRAM_WORD(ha, offsetof(eeprom_data_t, isp4010.topcat));
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	if ((topcat & TOPCAT_MASK) == TOPCAT_PRESENT)
+		set_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags);
+	else
+		clear_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags);
+	QL4XXX_UNLOCK_NVRAM(ha);
+}
+
+
+static int
+qla4xxx_config_nvram(scsi_qla_host_t * ha)
+{
+	unsigned long flags;
+	EXTERNAL_HW_CONFIG_REG extHwConfig;
+
+	DEBUG2(printk("scsi%ld: %s: Get EEProm parameters \n", ha->host_no,
+	    __func__));
+	QL4XXX_LOCK_FLASH(ha);
+	QL4XXX_LOCK_NVRAM(ha);
+	/* Get EEPRom Parameters from NVRAM and validate */
+	ql4_printk(KERN_INFO, ha, "Configuring NVRAM ...\n");
+	if (qla4xxx_is_NVRAM_configuration_valid(ha) == QLA_SUCCESS) {
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		extHwConfig.Asuint32_t = RD_NVRAM_WORD(ha,
+		    EEPROM_EXT_HW_CONF_OFFSET());
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	} else {
+		/*
+		 * QLogic adapters should always have a valid NVRAM.
+		 * If not valid, do not load.
+		 */
+		ql4_printk(KERN_WARNING, ha,
+		    "scsi%ld: %s: EEProm checksum invalid.  Please update "
+		    "your EEPROM\n", ha->host_no, __func__);
+
+		/* set defaults */
+		if (IS_QLA4010(ha))
+			extHwConfig.Asuint32_t = 0x1912;
+		else if (IS_QLA4022(ha))
+			extHwConfig.Asuint32_t = 0x0023;
+	}
+	DEBUG(printk("scsi%ld: %s: Setting extHwConfig to 0xFFFF%04x\n",
+	    ha->host_no, __func__, extHwConfig.Asuint32_t));
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	WRT_REG_DWORD(ISP_EXT_HW_CONF(ha),
+	    ((0xFFFF << 16) | extHwConfig.Asuint32_t));
+	PCI_POSTING(ISP_EXT_HW_CONF(ha));
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	QL4XXX_UNLOCK_NVRAM(ha);
+	QL4XXX_UNLOCK_FLASH(ha);
+
+	return 1;
+}
+
+static void
+qla4x00_pci_config(scsi_qla_host_t * ha)
+{
+	uint16_t w, mwi;
+
+	ql4_printk(KERN_INFO, ha, "Configuring PCI space...\n");
+
+	pci_set_master(ha->pdev);
+	mwi = 0;
+	if (pci_set_mwi(ha->pdev))
+		mwi = PCI_COMMAND_INVALIDATE;
+	/*
+	 * We want to respect framework's setting of PCI configuration space
+	 * command register and also want to make sure that all bits of
+	 * interest to us are properly set in command register.
+	 */
+	pci_read_config_word(ha->pdev, PCI_COMMAND, &w);
+	w |= mwi | (PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+	w &= ~PCI_COMMAND_INTX_DISABLE;
+	pci_write_config_word(ha->pdev, PCI_COMMAND, w);
+}
+
+static int
+qla4xxx_start_firmware_from_flash(scsi_qla_host_t * ha)
+{
+	int status = QLA_ERROR;
+	uint32_t max_wait_time;
+	unsigned long flags;
+	uint32_t mbox_status;
+
+	ql4_printk(KERN_INFO, ha, "Starting firmware ...\n");
+
+	/*
+	 * Start firmware from flash ROM
+	 *
+	 * WORKAROUND: Stuff a non-constant value that the firmware can
+	 * use as a seed for a random number generator in MB7 prior to
+	 * setting BOOT_ENABLE.  Fixes problem where the TCP
+	 * connections use the same TCP ports after each reboot,
+	 * causing some connections to not get re-established.
+	 */
+	DEBUG(printk("scsi%d: %s: Start firmware from flash ROM\n",
+	    ha->host_no, __func__));
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	WRT_REG_DWORD(&ha->reg->mailbox[7], jiffies);
+	if (IS_QLA4022(ha))
+		WRT_REG_DWORD(&ha->reg->u1.isp4022.nvram,
+		    SET_RMASK(NVR_WRITE_ENABLE));
+
+	WRT_REG_DWORD(&ha->reg->ctrl_status, SET_RMASK(CSR_BOOT_ENABLE));
+	PCI_POSTING(&ha->reg->ctrl_status);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	/* Wait for firmware to come UP. */
+	max_wait_time = FIRMWARE_UP_TOV * 4;
+	do {
+		uint32_t ctrl_status;
+
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		ctrl_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+		mbox_status = RD_REG_DWORD(&ha->reg->mailbox[0]);
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+		if (ctrl_status & SET_RMASK(CSR_SCSI_PROCESSOR_INTR))
+			break;
+		if (mbox_status == MBOX_STS_COMMAND_COMPLETE)
+			break;
+
+		DEBUG2(printk("scsi%ld: %s: Waiting for boot firmware to "
+		    "complete... ctrl_sts=0x%x, remaining=%d\n", ha->host_no,
+		    __func__, ctrl_status, max_wait_time));
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ / 4);
+	} while ((max_wait_time--));
+
+	if (mbox_status == MBOX_STS_COMMAND_COMPLETE) {
+		DEBUG(printk("scsi%ld: %s: Firmware has started\n",
+		    ha->host_no, __func__));
+
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		WRT_REG_DWORD(&ha->reg->ctrl_status,
+		    SET_RMASK(CSR_SCSI_PROCESSOR_INTR));
+		PCI_POSTING(&ha->reg->ctrl_status);
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+		status = QLA_SUCCESS;
+	} else {
+		printk(KERN_INFO "scsi%ld: %s: Boot firmware failed "
+		    "-  mbox status 0x%x\n", ha->host_no, __func__,
+		    mbox_status);
+		status = QLA_ERROR;
+	}
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_start_firmware
+ *	This routine performs the neccessary steps to start the firmware for
+ *	the QLA4010 adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully started QLA4xxx firmware
+ *	QLA_ERROR   - Failed to start QLA4xxx firmware
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_start_firmware(scsi_qla_host_t * ha)
+{
+	unsigned long flags = 0;
+	uint32_t mbox_status;
+	int status = QLA_ERROR;
+	int soft_reset = 1;
+	int config_chip = 0;
+
+	if (IS_QLA4010(ha))
+		qla4010_get_topcat_presence(ha);
+
+	if (IS_QLA4022(ha))
+		ql4xxx_set_mac_number(ha);
+
+	QL4XXX_LOCK_DRVR_WAIT(ha);
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	DEBUG2(printk("scsi%ld: %s: port_ctrl   = 0x%08X\n", ha->host_no,
+	    __func__, RD_REG_DWORD(ISP_PORT_CTRL(ha))));
+	DEBUG(printk("scsi%ld: %s: port_status = 0x%08X\n", ha->host_no,
+	    __func__, RD_REG_DWORD(ISP_PORT_STATUS(ha))));
+
+	/* Is Hardware already initialized? */
+	if ((RD_REG_DWORD(ISP_PORT_CTRL(ha)) & 0x8000) != 0) {
+		DEBUG(printk("scsi%ld: %s: Hardware has already been "
+		    "initialized\n", ha->host_no, __func__));
+
+		/* Receive firmware boot acknowledgement */
+		mbox_status = RD_REG_DWORD(&ha->reg->mailbox[0]);
+
+		DEBUG2(printk("scsi%ld: %s: H/W Config complete - mbox[0]= "
+		    "0x%x\n", ha->host_no, __func__, mbox_status));
+
+		/* Is firmware already booted? */
+		if (mbox_status == 0) {
+			/* F/W not running, must be config by net driver */
+			config_chip = 1;
+			soft_reset = 0;
+		} else {
+			WRT_REG_DWORD(&ha->reg->ctrl_status,
+			    SET_RMASK(CSR_SCSI_PROCESSOR_INTR));
+			PCI_POSTING(&ha->reg->ctrl_status);
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+			if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS) {
+				DEBUG2(printk("scsi%ld: %s: Get firmware "
+				    "state -- state = 0x%x\n", ha->host_no,
+				    __func__, ha->firmware_state));
+				/* F/W is running */
+				if (ha->firmware_state & FW_STATE_CONFIG_WAIT) {
+					DEBUG2(printk("scsi%ld: %s: Firmware "
+					    "in known state -- config and "
+					    "boot, state = 0x%x\n",
+					    ha->host_no, __func__,
+					    ha->firmware_state));
+					config_chip = 1;
+					soft_reset = 0;
+				}
+			} else {
+				DEBUG2(printk("scsi%ld: %s: Firmware in "
+				    "unknown state -- resetting, state = "
+				    "0x%x\n", ha->host_no, __func__,
+				    ha->firmware_state));
+			}
+			spin_lock_irqsave(&ha->hardware_lock, flags);
+		}
+	} else {
+		DEBUG(printk("scsi%ld: %s: H/W initialization hasn't been "
+		    "started - resetting\n", ha->host_no, __func__));
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	DEBUG(printk("scsi%ld: %s: Flags soft_rest=%d, config= %d\n ",
+	    ha->host_no, __func__, soft_reset, config_chip));
+	if (soft_reset) {
+		DEBUG(printk("scsi%ld: %s: Issue Soft Reset\n", ha->host_no,
+		    __func__));
+		status = qla4xxx_soft_reset(ha);
+		if (status == QLA_ERROR) {
+			DEBUG(printk("scsi%d: %s: Soft Reset failed!\n",
+			    ha->host_no, __func__));
+			QL4XXX_UNLOCK_DRVR(ha);
+			return QLA_ERROR;
+		}
+		config_chip = 1;
+		/* Reset clears the semaphore, so aquire again */
+		QL4XXX_LOCK_DRVR_WAIT(ha);
+	}
+
+	if (config_chip) {
+		if (qla4xxx_config_nvram(ha))
+			status = qla4xxx_start_firmware_from_flash(ha);
+
+		QL4XXX_UNLOCK_DRVR(ha);
+		if (status == QLA_SUCCESS) {
+			qla4xxx_get_fw_version(ha);
+			if (test_and_clear_bit(AF_GET_CRASH_RECORD, &ha->flags))
+				qla4xxx_get_crash_record(ha);
+		} else {
+			DEBUG(printk("scsi%ld: %s: Firmware has NOT started\n",
+			    ha->host_no, __func__));
+		}
+	}
+	return status;
+}
+
+
+/**************************************************************************
+ * qla4xxx_initialize_adapter
+ *	This routine parforms all of the steps necessary to initialize the
+ *	adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	renew_ddb_list - Indicates what to do with the adapter's ddb list
+ *			after adapter recovery has completed.
+ *			0=preserve ddb list, 1=destroy and rebuild ddb list
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully initialized adapter
+ *	QLA_ERROR   - Failed to initialize adapter
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_initialize_adapter(scsi_qla_host_t * ha, uint8_t renew_ddb_list)
+{
+	int status = QLA_ERROR;
+
+	qla4x00_pci_config(ha);
+
+	qla4xxx_disable_intrs(ha);
+
+	/* Initialize the Host adapter request/response queues and firmware */
+	if (qla4xxx_start_firmware(ha) == QLA_ERROR)
+		return status;
+
+	if (qla4xxx_validate_mac_address(ha) == QLA_ERROR)
+		return status;
+
+	if (qla4xxx_init_local_data(ha) == QLA_ERROR)
+		return status;
+
+	status = qla4xxx_init_firmware(ha);
+	if (status == QLA_ERROR)
+		return status;
+
+	/*
+	 * FW is waiting to get an IP address from DHCP server: Skip building
+	 * the ddb_list and wait for DHCP lease acquired aen to come in
+	 * followed by 0x8014 aen" to trigger the tgt discovery process.
+	 */
+	if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS)
+		return status;
+
+	/* Skip device discovery if ip and subnet is zero */
+	if (IPAddrIsZero(ha->ip_address) || IPAddrIsZero(ha->subnet_mask))
+		return status;
+
+	/* If iSNS Enabled, wait for iSNS targets */
+	if (test_bit (ISNS_FLAG_ISNS_ENABLED_IN_ISP, &ha->isns_flags)) {
+		unsigned long wait_cnt = jiffies + ql4xdiscoverywait * HZ;
+
+		DEBUG(printk("scsi%ld: Delay up to %d seconds while iSNS "
+		    "targets are being discovered.\n", ha->host_no,
+		    ql4xdiscoverywait));
+		while (!time_after_eq(jiffies, wait_cnt)) {
+			if (test_bit(ISNS_FLAG_DEV_SCAN_DONE, &ha->isns_flags))
+				break;
+			qla4xxx_get_firmware_state(ha);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(1 * HZ);
+		}
+
+		if (!test_bit(ISNS_FLAG_ISNS_SRV_ENABLED, &ha->isns_flags)) {
+			DEBUG2(printk("scsi%ld: iSNS service failed to start\n",
+			    ha->host_no));
+		} else {
+			if (!ha->isns_num_discovered_targets) {
+				DEBUG2(printk("scsi%ld: Failed to discover "
+				    "iSNS targets\n", ha->host_no));
+			}
+		}
+	}
+
+	if (renew_ddb_list == PRESERVE_DDB_LIST) {
+		/*
+		 * We want to preserve lun states (i.e. suspended, etc.)
+		 * for recovery initiated by the driver.  So just update
+		 * the device states for the existing ddb_list.
+		 */
+		qla4xxx_reinitialize_ddb_list(ha);
+	} else if (renew_ddb_list == REBUILD_DDB_LIST) {
+		/*
+		 * We want to build the ddb_list from scratch during
+		 * driver initialization and recovery initiated by the
+		 * INT_HBA_RESET IOCTL.
+		 */
+		status = qla4xxx_initialize_ddb_list(ha);
+		if (status == QLA_ERROR) {
+			DEBUG2(printk("%s(%ld) Error occurred during build ddb "
+			    "list\n", __func__, ha->host_no));
+			goto exit_init_hba;
+		}
+
+	}
+	if (!ha->tot_ddbs) {
+		DEBUG2(printk("scsi%ld: Failed to initialize devices or none "
+		    "present in Firmware device database\n", ha->host_no));
+	}
+
+exit_init_hba:
+	return status;
+
+}
+
+/**************************************************************************
+ * qla4xxx_add_device_dynamically
+ *	This routine processes adds a device as a result of an 8014h AEN.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      fw_ddb_index - Firmware's device database index
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+static void
+qla4xxx_add_device_dynamically(scsi_qla_host_t *ha, uint32_t fw_ddb_index)
+{
+	struct ddb_entry * ddb_entry;
+
+	/* First allocate a device structure */
+	ddb_entry = qla4xxx_get_ddb_entry(ha, fw_ddb_index);
+	if (ddb_entry == NULL) {
+		DEBUG2(printk(KERN_WARNING
+		    "scsi%ld: Unable to allocate memory to add fw_ddb_index "
+		    "%d\n", ha->host_no, fw_ddb_index));
+	} else if (qla4xxx_update_ddb_entry(ha, ddb_entry, fw_ddb_index) ==
+	    QLA_ERROR) {
+		ha->fw_ddb_index_map[fw_ddb_index] =
+		    (struct ddb_entry *)INVALID_ENTRY;
+		DEBUG2(printk(KERN_WARNING
+		    "scsi%ld: failed to add new device at index [%d]\n"
+		    "Unable to retrieve fw ddb entry\n", ha->host_no,
+		    fw_ddb_index));
+	} else {
+		/* New device. Let's add it to the database */
+		DEBUG2(printk("scsi%ld: %s: new device at index [%d]\n",
+		    ha->host_no, __func__, fw_ddb_index));
+/*FIXME*/
+		/*qla4xxx_config_os(ha);*/
+	}
+}
+
+/**************************************************************************
+ * qla4xxx_process_ddb_changed
+ *	This routine processes a Decive Database Changed AEN Event.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      fw_ddb_index - Firmware's device database index
+ *      state - Device state
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully processed ddb_changed aen
+ *	QLA_ERROR   - Failed to process ddb_changed aen
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_process_ddb_changed(scsi_qla_host_t * ha, uint32_t fw_ddb_index,
+    uint32_t state)
+{
+	struct ddb_entry * ddb_entry;
+	uint32_t old_fw_ddb_device_state;
+
+	/* check for out of range index */
+	if (fw_ddb_index >= MAX_DDB_ENTRIES)
+		return QLA_ERROR;
+
+	/* Get the corresponging ddb entry */
+	ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index);
+	/* Device does not currently exist in our database. */
+	if (ddb_entry == NULL) {
+		if (state == DDB_DS_SESSION_ACTIVE)
+			qla4xxx_add_device_dynamically(ha, fw_ddb_index);
+		return QLA_SUCCESS;
+	}
+
+	/* Device already exists in our database. */
+	old_fw_ddb_device_state = ddb_entry->fw_ddb_device_state;
+	DEBUG2(printk("scsi%ld: %s DDB - old state= 0x%x, new state=0x%x for "
+	    "index [%d]\n", ha->host_no, __func__,
+	    ddb_entry->fw_ddb_device_state, state, fw_ddb_index));
+	if (old_fw_ddb_device_state == state &&
+	    state == DDB_DS_SESSION_ACTIVE) {
+		/* Do nothing, state not changed. */
+		return QLA_SUCCESS;
+	}
+
+	ddb_entry->fw_ddb_device_state = state;
+	/* Device is back online. */
+	if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) {
+		atomic_set(&ddb_entry->port_down_timer,
+		    ha->port_down_retry_count);
+		atomic_set(&ddb_entry->state, DDB_STATE_ONLINE);
+		atomic_set(&ddb_entry->relogin_retry_count, 0);
+		atomic_set(&ddb_entry->relogin_timer, 0);
+		clear_bit(DF_RELOGIN, &ddb_entry->flags);
+		clear_bit(DF_NO_RELOGIN, &ddb_entry->flags);
+		/*
+		 * Change the lun state to READY in case the lun TIMEOUT before
+		 * the device came back.
+		 */
+	} else {
+		/* Device went away, try to relogin. */
+		/* Mark device missing */
+		if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
+			qla4xxx_mark_device_missing(ha, ddb_entry);
+		/*
+		 * Relogin if device state changed to a not active state.
+		 * However, do not relogin if this aen is a result of an IOCTL
+		 * logout (DF_NO_RELOGIN) or if this is a discovered device.
+		 */
+		if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_FAILED &&
+		    !test_bit(DF_RELOGIN, &ddb_entry->flags) &&
+		    !test_bit(DF_NO_RELOGIN, &ddb_entry->flags) &&
+		    !test_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags)) {
+			/*
+			 * This triggers a relogin.  After the relogin_timer
+			 * expires, the relogin gets scheduled.  We must wait a
+			 * minimum amount of time since receiving an 0x8014 AEN
+			 * with failed device_state or a logout response before
+			 * we can issue another relogin.
+			 */
+			/* Firmware padds this timeout: (time2wait +1).
+			 * Driver retry to login should be longer than F/W.
+			 * Otherwise F/W will fail
+			 * set_ddb() mbx cmd with 0x4005 since it still
+			 * counting down its time2wait.
+			 */
+			atomic_set(&ddb_entry->relogin_timer, 0);
+			atomic_set(&ddb_entry->retry_relogin_timer,
+			    ddb_entry->default_time2wait + 4);
+		}
+	}
+
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4xxx_login_device
+ *	This routine is called by the login IOCTL to log in the specified
+ *	device.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ * 	fw_ddb_index - Index of the device to login
+ * 	connection_id - Connection ID of the device to login
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully logged in device
+ *	QLA_ERROR   - Failed to login device
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_login_device(scsi_qla_host_t * ha, uint16_t fw_ddb_index,
+    uint16_t connection_id)
+{
+	struct ddb_entry * ddb_entry;
+	int status = QLA_ERROR;
+
+	ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index);
+	if (ddb_entry == NULL)
+		goto exit_login_device;
+
+	if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, NULL,
+	    &ddb_entry->fw_ddb_device_state, NULL, NULL, NULL) == QLA_ERROR)
+		goto exit_login_device;
+
+	if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) {
+		status = QLA_SUCCESS;
+		goto exit_login_device;
+	}
+
+	if (qla4xxx_conn_close_sess_logout(ha, fw_ddb_index, connection_id,
+	    LOGOUT_OPTION_RELOGIN) != QLA_SUCCESS)
+		goto exit_login_device;
+
+	status = QLA_SUCCESS;
+
+exit_login_device:
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_logout_device
+ *	This support routine is called by the logout IOCTL to log out
+ *	the specified device.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ * 	fw_ddb_index - Index of the device to logout
+ * 	connection_id - Connection ID of the device to logout
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully logged out device
+ *	QLA_ERROR   - Failed to logout device
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_logout_device(scsi_qla_host_t * ha, uint16_t fw_ddb_index,
+    uint16_t connection_id)
+{
+	int status = QLA_ERROR;
+	struct ddb_entry * ddb_entry;
+	uint32_t old_fw_ddb_device_state;
+
+	ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index);
+	if (ddb_entry == NULL)
+		goto exit_logout_device;
+
+	if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, NULL,
+	    &old_fw_ddb_device_state, NULL, NULL, NULL) != QLA_SUCCESS)
+		goto exit_logout_device;
+
+	set_bit(DF_NO_RELOGIN, &ddb_entry->flags);
+	if (qla4xxx_conn_close_sess_logout(ha, fw_ddb_index, connection_id,
+	    LOGOUT_OPTION_CLOSE_SESSION) != QLA_SUCCESS)
+		goto exit_logout_device;
+
+	status = QLA_SUCCESS;
+
+exit_logout_device:
+	return status;
+}
+
+/*
+ * qla4xxx_delete_device
+ *	This routine is called by the logout IOCTL to delete the specified
+ *      device.	 Send the LOGOUT and DELETE_DDB commands for the specified
+ *      target, even if it's not in our internal database.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ * 	fw_ddb_index - Index of the device to delete
+ * 	connection_id - Connection ID of the device to delete
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully deleted device
+ *	QLA_ERROR   - Failed to delete device
+ */
+int
+qla4xxx_delete_device(scsi_qla_host_t * ha, uint16_t fw_ddb_index,
+    uint16_t connection_id)
+{
+	int status = QLA_ERROR;
+	uint32_t fw_ddb_device_state = 0xFFFF;
+	u_long wait_count;
+	struct ddb_entry * ddb_entry;
+
+	/* If the device is in our internal tables, set the NO_RELOGIN bit. */
+	ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index);
+	if (ddb_entry != NULL)
+		set_bit(DF_NO_RELOGIN, &ddb_entry->flags);
+
+	/*
+	 * If the device state is already one that we can delete, bypass the
+	 * logout command.
+	 */
+	qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, NULL,
+	    &fw_ddb_device_state, NULL, NULL, NULL);
+	if (fw_ddb_device_state == DDB_DS_UNASSIGNED ||
+	    fw_ddb_device_state == DDB_DS_NO_CONNECTION_ACTIVE ||
+	    fw_ddb_device_state == DDB_DS_SESSION_FAILED)
+		goto delete_ddb;
+
+	/* First logout index */
+	if (qla4xxx_conn_close_sess_logout(ha, fw_ddb_index, connection_id,
+	    LOGOUT_OPTION_CLOSE_SESSION) != QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: LOGOUT_OPTION_CLOSE_SESSION "
+			"failed index [%d]\n", ha->host_no, __func__,
+			fw_ddb_index));
+		goto exit_delete_ddb;
+	}
+
+	/* Wait enough time to complete logout */
+	wait_count = jiffies + LOGOUT_TOV * HZ;
+	while (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, NULL,
+	    &fw_ddb_device_state, NULL, NULL, NULL) == QLA_SUCCESS) {
+		if (time_after_eq(jiffies, wait_count))
+			goto exit_delete_ddb;
+		if (fw_ddb_device_state == DDB_DS_UNASSIGNED ||
+		    fw_ddb_device_state == DDB_DS_NO_CONNECTION_ACTIVE ||
+		    fw_ddb_device_state == DDB_DS_SESSION_FAILED)
+			break;
+		udelay(50);
+	}
+
+delete_ddb:
+	/* Now delete index */
+	if (qla4xxx_clear_database_entry(ha, fw_ddb_index) == QLA_SUCCESS) {
+		status = QLA_SUCCESS;
+
+		if (!ddb_entry)
+			goto exit_delete_ddb;
+
+		atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
+		DEBUG(printk("scsi%ld: %s: removing index %d.\n", ha->host_no,
+		    __func__, fw_ddb_index));
+		ha->fw_ddb_index_map[fw_ddb_index] =
+		    (struct ddb_entry *)INVALID_ENTRY;
+	}
+
+exit_delete_ddb:
+	return status;
+
+}
drivers/scsi/qla4xxx/ql4_iocb.c new file +595 -0

595 lines not on this page · raw message

Signature · 1 line
Andrew Vasquez
#2 lore raw

Re: [PATCH 2/8] qla4xxx: initialization routines

Quoted 18 lines · Andrew Vasquez

ISP4xxx driver initialization routines.

---

drivers/scsi/qla4xxx/ql4_init.c | 1753 +++++++++++++++++++++++++++++++++++++++ drivers/scsi/qla4xxx/ql4_iocb.c | 595 +++++++++++++

2 files changed, 2348 insertions(+), 0 deletions(-)

create mode 100644 drivers/scsi/qla4xxx/ql4_init.c create mode 100644 drivers/scsi/qla4xxx/ql4_iocb.c

6755c3b78809194fe31f551b3862639ae513b9a9

(quoted diff) +0 -0

b/drivers/scsi/qla4xxx/ql4_init.c new file mode 100644

drivers/scsi/qla4xxx/ql4_init.c line 10

Quoted 10 lines
drivers/scsi/qla4xxx/ql4_init.c new file +10 -0
@@ -0,0 +1,1753 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:

[huge licence stuff]

Wouldn't it be enough to include that in one of the files (or even better: in an extra file in Documentation/scsi/ or somewhere) and then reference to it?

Quoted 15 lines
drivers/scsi/qla4xxx/ql4_init.c new file +15 -0
+/**************************************************************************
+ * qla4xxx_free_ddb
+ *	This routine deallocates and unlinks the specified ddb_entry from the
+ *	adapter's
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	ddb_entry - Pointer to device database entry
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+
Quoted 1 line

**************************************************************************/

Please take a look at Documentation/kernel-doc-nano-HOWTO.txt. And kernel context is pretty normal in a device driver.

Quoted 8 lines

+/*
+ * qla4xxx_init_rings
+ * This routine initializes the internal queues for the specified adapter.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Remarks:

Quoted 8 lines

+ * The QLA4010 requires us to restart the queues at index 0.
+ * The QLA4000 doesn't care, so just default to QLA4010's requirement.
+ * Returns:
+ * QLA_SUCCESS - Always return success.
+ *
+ * Context:
+ * Kernel context.
+ */

And here you have a different style just 2 functions later

Quoted 3 lines
drivers/scsi/qla4xxx/ql4_init.c new file +3 -0
+int
+qla4xxx_init_rings(scsi_qla_host_t * ha)
+{

[...]

drivers/scsi/qla4xxx/ql4_init.c line 219

Quoted 4 lines
drivers/scsi/qla4xxx/ql4_init.c new file +4 -0
+	/* Initialize active array */
+	for (i = 0; i < MAX_SRBS; i++)
+		ha->active_srb_array[i] = 0;
+	ha->active_srb_count = 0;

memset() ?

drivers/scsi/qla4xxx/ql4_init.c line 224

Quoted 4 lines
drivers/scsi/qla4xxx/ql4_init.c new file +4 -0
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return QLA_SUCCESS;
+}

I don't see it returning anything different. So this return value is rather useless, isn't it? And from my point of view I prefer to use the normal error codes as much as possible instead of inventing my own.

Eike

#3 lore raw

PATCH 3/8 qla4xxx: OS integration files

In reply to Andrew Vasquez

ISP4xxx driver OS files.

---

 drivers/scsi/Kconfig            |    1 
 drivers/scsi/Makefile           |    1 
 drivers/scsi/qla4xxx/Kconfig    |    5 
 drivers/scsi/qla4xxx/Makefile   |    4 
 drivers/scsi/qla4xxx/ql4_attr.c |  328 ++++++
 drivers/scsi/qla4xxx/ql4_os.c   | 2311 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 2650 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/Kconfig
 create mode 100644 drivers/scsi/qla4xxx/Makefile
 create mode 100644 drivers/scsi/qla4xxx/ql4_attr.c
 create mode 100644 drivers/scsi/qla4xxx/ql4_os.c

aa7e70e307c0c846eadc5dcfdbdb10f479f61625

drivers/scsi/Kconfig +1 -0
@@ -1309,6 +1309,7 @@ config SCSI_QLOGICPTI
 	  module will be called qlogicpti.
 
 source "drivers/scsi/qla2xxx/Kconfig"
+source "drivers/scsi/qla4xxx/Kconfig"
 
 config SCSI_LPFC
 	tristate "Emulex LightPulse Fibre Channel Support"
drivers/scsi/Makefile +1 -0
@@ -78,6 +78,7 @@ obj-$(CONFIG_SCSI_QLOGIC_ISP)	+= qlogici
 obj-$(CONFIG_SCSI_QLOGIC_FC)	+= qlogicfc.o 
 obj-$(CONFIG_SCSI_QLOGIC_1280)	+= qla1280.o 
 obj-$(CONFIG_SCSI_QLA2XXX)	+= qla2xxx/
+obj-$(CONFIG_SCSI_QLA4XXX)	+= qla4xxx/
 obj-$(CONFIG_SCSI_LPFC)		+= lpfc/
 obj-$(CONFIG_SCSI_PAS16)	+= pas16.o
 obj-$(CONFIG_SCSI_SEAGATE)	+= seagate.o
drivers/scsi/qla4xxx/Kconfig new file +5 -0
new file mode 100644
@@ -0,0 +1,5 @@
+config SCSI_QLA4XXX
+	tristate "QLogic ISP4XXX host adapter family support"
+	depends on PCI && SCSI
+	---help---
+	This driver supports the QLogic 40xx (ISP4XXX) host adapter family.
drivers/scsi/qla4xxx/Makefile new file +4 -0
new file mode 100644
@@ -0,0 +1,4 @@
+obj-$(CONFIG_SCSI_QLA4XXX) += qla4xxx.o
+
+qla4xxx-objs := ql4_os.o ql4_init.o ql4_mbx.o ql4_iocb.o ql4_isr.o \
+		ql4_isns.o ql4_nvram.o ql4_dbg.o
drivers/scsi/qla4xxx/ql4_attr.c new file +328 -0

328 lines not on this page · raw message

drivers/scsi/qla4xxx/ql4_os.c new file +2311 -0

2,311 lines not on this page · raw message

Signature · 1 line
Andrew Vasquez
#4 lore raw

PATCH 4/8 qla4xxx: iSNS routines

In reply to Andrew Vasquez

ISP4xxx driver iSNS routines and definitions.

---

 drivers/scsi/qla4xxx/ql4_isns.c | 2223 +++++++++++++++++++++++++++++++++++++++
 drivers/scsi/qla4xxx/ql4_isns.h |  401 +++++++
 2 files changed, 2624 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/ql4_isns.c
 create mode 100644 drivers/scsi/qla4xxx/ql4_isns.h

bf2f290d5776f4e883e67aed7fd928d35352fe72

drivers/scsi/qla4xxx/ql4_isns.c new file +2223 -0
new file mode 100644
@@ -0,0 +1,2223 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:
+ *
+ * 	1. Redistribution of source code must retain the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission.
+ *
+ * You may redistribute the hardware specific firmware binary file
+ * under the following terms:
+ *
+ * 	1. Redistribution of source code (only if applicable),
+ * 	   must retain the above copyright notice, this list of
+ * 	   conditions and the following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission
+ *
+ * REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+ * THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+ * CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+ * OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+ * TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ * ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+ * COMBINATION WITH THIS PROGRAM.
+ */
+
+#include "ql4_def.h"
+
+void qla4xxx_isns_enable_callback(scsi_qla_host_t *, uint32_t, uint32_t,
+				  uint32_t, uint32_t);
+
+int qla4xxx_isns_restart_service(scsi_qla_host_t *);
+uint32_t qla4xxx_isns_build_iocb_handle(scsi_qla_host_t *, uint32_t,
+					PDU_ENTRY *);
+int qla4xxx_isns_get_server_request(scsi_qla_host_t *, uint32_t, uint16_t);
+int qla4xxx_isns_reassemble_pdu(scsi_qla_host_t *, uint8_t *, uint32_t *);
+int qla4xxx_isns_parse_and_dispatch_server_request(scsi_qla_host_t *,
+						       uint8_t *, uint32_t,
+						       uint16_t);
+int qla4xxx_isns_parse_and_dispatch_server_response(scsi_qla_host_t *,
+							uint8_t *, uint32_t);
+int qla4xxx_isns_build_scn_registration_packet(scsi_qla_host_t * ha,
+						   uint8_t * buffer,
+						   uint32_t buffer_size,
+						   uint32_t * packet_size);
+int qla4xxx_isns_build_scn_deregistration_packet(scsi_qla_host_t * ha,
+						     uint8_t * buffer,
+						     uint32_t buffer_size,
+						     uint32_t * packet_size);
+int qla4xxx_isns_build_registration_packet(scsi_qla_host_t * ha,
+					       uint8_t * buff,
+					       uint32_t buff_size,
+					       uint8_t * isns_entity_id,
+					       uint8_t * ip_addr,
+					       uint32_t port_number,
+					       uint32_t scn_port,
+					       uint32_t esi_port,
+					       uint8_t * local_alias,
+					       uint32_t * packet_size);
+int qla4xxx_isns_build_deregistration_packet(scsi_qla_host_t * ha,
+						 uint8_t * buff,
+						 uint32_t buff_size,
+						 uint8_t * isns_entity_id,
+						 uint8_t * ip_addr,
+						 uint32_t port_number,
+						 uint32_t * packet_size);
+int qla4xxx_isns_build_request_packet(scsi_qla_host_t * ha,
+					  uint8_t * buff, uint32_t buff_size,
+					  uint16_t function_id,
+					  uint16_t tx_id,
+					  uint8_t use_replace_flag,
+					  ATTRIBUTE_LIST * attr_list,
+					  uint32_t * packet_size);
+int qla4xxx_isns_append_attribute(scsi_qla_host_t * ha, uint8_t ** buffer,
+				      uint8_t * buffer_end,
+				      ATTRIBUTE_LIST * attr_list);
+int qla4xxx_isns_dev_attr_reg(scsi_qla_host_t *);
+int qla4xxx_isns_dev_attr_reg_rsp(scsi_qla_host_t * ha, uint8_t * buffer,
+				      uint32_t buffer_size);
+int qla4xxx_isns_dev_attr_qry_rsp(scsi_qla_host_t * ha, uint8_t * buffer,
+				      uint32_t buffer_size);
+int qla4xxx_isns_dev_get_next_rsp(scsi_qla_host_t * ha, uint8_t * buffer,
+				      uint32_t buffer_size);
+int qla4xxx_isns_dev_dereg_rsp(scsi_qla_host_t * ha, uint8_t * buffer,
+				   uint32_t buffer_size);
+int qla4xxx_isns_scn_reg_rsp(scsi_qla_host_t * ha, uint8_t * buffer,
+				 uint32_t buffer_size);
+int qla4xxx_isns_scn_dereg_rsp(scsi_qla_host_t * ha, uint8_t * buffer,
+				   uint32_t buffer_size);
+int qla4xxx_isns_scn_dereg(scsi_qla_host_t *);
+int qla4xxx_isns_scn_reg(scsi_qla_host_t * ha);
+int qla4xxx_isns_dev_get_next(scsi_qla_host_t * ha,
+				  uint8_t * last_iscsi_name);
+
+const char *isns_error_code_msg[] = ISNS_ERROR_CODE_TBL();
+
+static void
+qla4xxx_strtolower(uint8_t *str)
+{
+	uint8_t *tmp;
+
+	for (tmp = str; *tmp != '\0'; tmp++)
+		if (*tmp >= 'A' && *tmp <= 'Z')
+			*tmp += 'a' - 'A';
+}
+
+void
+qla4xxx_isns_build_entity_id(scsi_qla_host_t *ha)
+{
+	sprintf(ha->isns_entity_id, "%s.%d", ha->serial_number,
+	    ha->function_number);
+	qla4xxx_strtolower(ha->isns_entity_id);
+}
+
+int
+qla4xxx_isns_reenable(scsi_qla_host_t *ha, uint32_t isns_ip_addr,
+    uint16_t isns_server_port_num)
+{
+	set_bit(ISNS_FLAG_REREGISTER, &ha->isns_flags);
+	ISNS_CLEAR_FLAGS(ha);
+	if (qla4xxx_isns_enable(ha, isns_ip_addr, isns_server_port_num) !=
+	    QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: Failed!\n", ha->host_no,
+		    __func__));
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+/* interrupt context, hardware lock set */
+void
+qla4xxx_isns_enable_callback(scsi_qla_host_t *ha, uint32_t svr, uint32_t scn,
+    uint32_t esi, uint32_t nsh)
+{
+	ha->isns_connection_id = (uint16_t) svr & 0x0000FFFF;
+	ha->isns_scn_conn_id = (uint16_t) scn & 0x0000FFFF;
+	ha->isns_esi_conn_id = (uint16_t) esi & 0x0000FFFF;
+	ha->isns_nsh_conn_id = (uint16_t) nsh & 0x0000FFFF;
+	ha->isns_remote_port_num = (uint16_t) (svr >> 16);
+	ha->isns_scn_port_num = (uint16_t) (scn >> 16);
+	ha->isns_esi_port_num = (uint16_t) (esi >> 16);
+	ha->isns_nsh_port_num = (uint16_t) (nsh >> 16);
+	DEBUG5(printk("scsi%ld: %s: iSNS Server TCP Connect "
+	    "succeeded %d\n", ha->host_no, __func__, svr));
+	DEBUG5(printk("scsi%ld: %s: Remote iSNS Server %d ConnID %x\n",
+	    ha->host_no, __func__, ha->isns_remote_port_num,
+	    ha->isns_connection_id));
+	DEBUG5(printk("scsi%ld: %s: Local  SCN  Listen %d ConnID %x\n",
+	    ha->host_no, __func__, ha->isns_scn_port_num,
+	    ha->isns_scn_conn_id));
+	DEBUG5(printk("scsi%ld: %s: Local  ESI  Listen %d ConnID %x\n",
+	    ha->host_no, __func__, ha->isns_esi_port_num,
+	    ha->isns_esi_conn_id));
+	DEBUG5(printk("scsi%ld: %s: Local  HSN  Listen %d ConnID %x\n",
+	    ha->host_no, __func__, ha->isns_nsh_port_num,
+	    ha->isns_nsh_conn_id));
+	if (ha->isns_connection_id == (uint16_t) - 1) {
+		DEBUG2(printk("scsi%ld: %s: iSNS server refused connection\n",
+		    ha->host_no, __func__));
+		qla4xxx_isns_restart_service(ha);
+		return;
+	}
+	set_bit(ISNS_FLAG_ISNS_SRV_ENABLED, &ha->isns_flags);
+	if (test_bit(ISNS_FLAG_REREGISTER, &ha->isns_flags)) {
+		if (qla4xxx_isns_scn_dereg(ha) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s: qla4xxx_isns_scn_dereg "
+			    "failed!\n", ha->host_no, __func__));
+			return;
+		}
+	} else {
+		if (qla4xxx_isns_dev_attr_reg(ha) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s: qla4xxx_isns_dev_attr_reg "
+			    "failed!\n", ha->host_no, __func__));
+			return;
+		}
+	}
+}
+
+int
+qla4xxx_isns_restart_service(scsi_qla_host_t *ha)
+{
+	qla4xxx_isns_disable(ha);
+	set_bit(ISNS_FLAG_RESTART_SERVICE, &ha->isns_flags);
+	ISNS_CLEAR_FLAGS(ha);
+
+	/* Set timer for restart to complete */
+	atomic_set(&ha->isns_restart_timer, ISNS_RESTART_TOV);
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_restart_service_completion(scsi_qla_host_t *ha,
+    uint32_t isns_ip_addr, uint16_t isns_server_port_num)
+{
+	DEBUG5(printk("scsi%ld: %s: isns_ip_addr %08x\n", ha->host_no,
+	    __func__, isns_ip_addr));
+	if (qla4xxx_isns_enable(ha, isns_ip_addr, isns_server_port_num) !=
+	    QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: failed!\n", ha->host_no,
+		    __func__));
+		return QLA_ERROR;
+	} else {
+		set_bit(ISNS_FLAG_REREGISTER, &ha->isns_flags);
+		ISNS_CLEAR_FLAGS(ha);
+		return QLA_SUCCESS;
+	}
+}
+
+static void
+qla4xxx_isns_init_isns_reg_attr_list(scsi_qla_host_t *ha)
+{
+	ATTRIBUTE_LIST isns_reg_attr_list[] = {
+
+		/* Source attribute */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+		{ ISNS_ATTR_TAG_ENTITY_IDENTIFIER, ISNS_ATTR_TYPE_STRING, -1 },
+
+		/* Entity ID. */
+		{ ISNS_ATTR_TAG_DELIMITER, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		/* Operating attributes to register */
+		{ ISNS_ATTR_TAG_ENTITY_IDENTIFIER, ISNS_ATTR_TYPE_STRING, -1},
+		{ ISNS_ATTR_TAG_ENTITY_PROTOCOL, ISNS_ATTR_TYPE_ULONG,
+			cpu_to_be32(ENTITY_PROTOCOL_ISCSI)},
+		{ ISNS_ATTR_TAG_PORTAL_IP_ADDRESS, ISNS_ATTR_TYPE_ADDRESS, -1 },
+		{ ISNS_ATTR_TAG_PORTAL_PORT, ISNS_ATTR_TYPE_ULONG, -1},
+		{ ISNS_ATTR_TAG_SCN_PORT, ISNS_ATTR_TYPE_ULONG, -1},
+		{ ISNS_ATTR_TAG_ESI_PORT, ISNS_ATTR_TYPE_ULONG, -1},
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string},
+		{ ISNS_ATTR_TAG_ISCSI_NODE_TYPE, ISNS_ATTR_TYPE_ULONG,
+			cpu_to_be32(ISCSI_NODE_TYPE_INITIATOR) },
+		{ ISNS_ATTR_TAG_ISCSI_ALIAS, ISNS_ATTR_TYPE_STRING, -1},
+
+		/* Friendly machine name? */
+		{ 0, 0, 0 },
+	};
+
+	memcpy(ha->isns_reg_attr_list, isns_reg_attr_list,
+	    sizeof(isns_reg_attr_list));
+}
+
+static void
+qla4xxx_isns_init_isns_dereg_attr_list(scsi_qla_host_t *ha)
+{
+	ATTRIBUTE_LIST isns_dereg_attr_list[] = {
+
+		/* Source attribute */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string},
+
+		/* No key attribute for DevDereg */
+		{ ISNS_ATTR_TAG_DELIMITER, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		/* Operating attributes */
+		{ ISNS_ATTR_TAG_ENTITY_IDENTIFIER, ISNS_ATTR_TYPE_STRING, -1 },
+
+		/* FQDN */
+#if 0
+		{ ISNS_ATTR_TAG_PORTAL_IP_ADDRESS, ISNS_ATTR_TYPE_ADDRESS, -1 },
+		{ ISNS_ATTR_TAG_PORTAL_PORT, ISNS_ATTR_TYPE_ULONG, -1 },
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+#endif
+		{ 0, 0, 0 },
+	};
+
+	memcpy(ha->isns_dereg_attr_list, isns_dereg_attr_list,
+	    sizeof(isns_dereg_attr_list));
+}
+
+static void
+qla4xxx_isns_init_isns_scn_reg_attr_list(scsi_qla_host_t * ha)
+{
+	ATTRIBUTE_LIST isns_scn_reg_attr_list[] = {
+
+		/* Source attribute */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+
+		/* Key attributes */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+
+		/*
+		 * Required delimiter to indicate division between key and
+		 * operating attrs.
+		 */
+		{ ISNS_ATTR_TAG_DELIMITER, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		/* Operating attributes */
+		{ ISNS_ATTR_TAG_ISCSI_SCN_BITMAP, ISNS_ATTR_TYPE_ULONG,
+			cpu_to_be32(ISCSI_SCN_OBJECT_UPDATED |
+					ISCSI_SCN_OBJECT_ADDED |
+					ISCSI_SCN_OBJECT_REMOVED |
+					ISCSI_SCN_TARGET_AND_SELF_INFO_ONLY) },
+		{ 0, 0, 0 },
+	};
+
+	memcpy(ha->isns_scn_reg_attr_list, isns_scn_reg_attr_list,
+	    sizeof(isns_scn_reg_attr_list));
+}
+
+static void
+qla4xxx_isns_init_isns_scn_dereg_attr_list(scsi_qla_host_t *ha)
+{
+	ATTRIBUTE_LIST isns_scn_dereg_attr_list[] = {
+
+		/* Source attribute */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+
+		/* Key attributes */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+
+		{ 0, 0, 0 },
+	};
+
+	memcpy(ha->isns_scn_dereg_attr_list, isns_scn_dereg_attr_list,
+	    sizeof(isns_scn_dereg_attr_list));
+}
+
+static void
+qla4xxx_isns_init_isns_dev_get_next_attr_list(scsi_qla_host_t *ha)
+{
+	ATTRIBUTE_LIST isns_dev_get_next_attr_list[] = {
+
+		/* Source attribute */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+
+		/* Key attributes */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING, -1 },
+
+		/*
+		 * Required delimiter to indicate division between key and
+		 * operating attrs.
+		 */
+		{ ISNS_ATTR_TAG_DELIMITER, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		/* Operating attributes (attributes of object matching key
+		 * attribute to return).
+		 */
+		{ ISNS_ATTR_TAG_ISCSI_NODE_TYPE, ISNS_ATTR_TYPE_ULONG,
+			cpu_to_be32(ISCSI_NODE_TYPE_TARGET)},
+
+		{ 0, 0, 0 },
+	};
+
+	memcpy(ha->isns_dev_get_next_attr_list, isns_dev_get_next_attr_list,
+	    sizeof(isns_dev_get_next_attr_list));
+}
+
+static void
+qla4xxx_isns_init_isns_dev_attr_qry_attr_list(scsi_qla_host_t * ha)
+{
+	ATTRIBUTE_LIST isns_dev_attr_qry_attr_list[] = {
+
+		/* Source attribute */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING,
+			(unsigned long)ha->name_string },
+
+		/* Key attributes */
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_STRING, -1 },
+
+		/*
+		 * Required delimiter to indicate division between key and
+		 * operating attrs.
+		 */
+		{ ISNS_ATTR_TAG_DELIMITER, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		/* Operating attributes (attributes of objects matching key
+		 * attributes to return)
+		 */
+		{ ISNS_ATTR_TAG_ENTITY_PROTOCOL, ISNS_ATTR_TYPE_EMPTY, 0 },
+		{ ISNS_ATTR_TAG_ISCSI_NAME, ISNS_ATTR_TYPE_EMPTY, 0 },
+		{ ISNS_ATTR_TAG_ISCSI_NODE_TYPE, ISNS_ATTR_TYPE_EMPTY, 0 },
+		{ ISNS_ATTR_TAG_ISCSI_ALIAS, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		/* Friendly name */
+		{ ISNS_ATTR_TAG_PORTAL_SYMBOLIC_NAME, ISNS_ATTR_TYPE_EMPTY, 0 },
+		{ ISNS_ATTR_TAG_PORTAL_IP_ADDRESS, ISNS_ATTR_TYPE_EMPTY, 0 },
+		{ ISNS_ATTR_TAG_PORTAL_PORT, ISNS_ATTR_TYPE_EMPTY, 0 },
+		{ ISNS_ATTR_TAG_PORTAL_SECURITY_BITMAP, ISNS_ATTR_TYPE_EMPTY,
+			0},
+		{ ISNS_ATTR_TAG_DD_ID, ISNS_ATTR_TYPE_EMPTY, 0 },
+
+		{ 0, 0, 0 },
+	};
+
+	memcpy(ha->isns_dev_attr_qry_attr_list, isns_dev_attr_qry_attr_list,
+	    sizeof(isns_dev_attr_qry_attr_list));
+}
+
+int
+qla4xxx_isns_init_attributes(scsi_qla_host_t * ha)
+{
+
+	/* Separate these calls to minimize stack usage */
+	qla4xxx_isns_init_isns_reg_attr_list(ha);
+	qla4xxx_isns_init_isns_dereg_attr_list(ha);
+	qla4xxx_isns_init_isns_scn_reg_attr_list(ha);
+	qla4xxx_isns_init_isns_scn_dereg_attr_list(ha);
+	qla4xxx_isns_init_isns_dev_get_next_attr_list(ha);
+	qla4xxx_isns_init_isns_dev_attr_qry_attr_list(ha);
+
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_append_attribute(scsi_qla_host_t *ha, uint8_t **buffer,
+    uint8_t *buffer_end, ATTRIBUTE_LIST *attribute)
+{
+	ISNS_ATTRIBUTE *isns_attr;
+	uint32_t data_len;
+	uint8_t *local;
+
+	isns_attr = (ISNS_ATTRIBUTE *) * buffer;
+	switch (attribute->type) {
+	case ISNS_ATTR_TYPE_EMPTY:
+		data_len = 0;
+		if ((&isns_attr->value[0] + data_len) > buffer_end)
+			return QLA_ERROR;
+
+		isns_attr->tag = cpu_to_be32(attribute->isns_tag);
+		isns_attr->length = cpu_to_be32(data_len);
+		break;
+
+	case ISNS_ATTR_TYPE_STRING:
+		/*
+		 * Length must include NULL terminator.
+		 * Note also that all iSNS strings must be UTF-8 encoded.
+		 * You should encode your strings for UTF-8 before registering
+		 * them with the iSNS server.
+		 */
+		data_len = strlen((uint8_t *) attribute->data) +
+		    sizeof(uint8_t);
+		if (data_len % 4)		/* Pad to 4 byte boundary. */
+			data_len += (4 - (data_len % 4));
+
+		if ((&isns_attr->value[0] + data_len) > buffer_end)
+			return QLA_ERROR;
+
+		isns_attr->tag = cpu_to_be32(attribute->isns_tag);
+		isns_attr->length = cpu_to_be32(data_len);
+		memset(isns_attr->value, 0, data_len);
+		strcpy(&isns_attr->value[0], (uint8_t *) attribute->data);
+		break;
+
+	case ISNS_ATTR_TYPE_ULONG:
+		data_len = sizeof(uint32_t);
+		if ((isns_attr->value + data_len) > buffer_end)
+			return QLA_ERROR;
+
+		isns_attr->tag = cpu_to_be32(attribute->isns_tag);
+		isns_attr->length = cpu_to_be32(data_len);
+		*(uint32_t *) isns_attr->value = (uint32_t) attribute->data;
+		break;
+
+	case ISNS_ATTR_TYPE_ADDRESS:
+		local = (uint8_t *) attribute->data;
+		data_len = 16;	/* Size of an IPv6 address */
+		if ((isns_attr->value + data_len) > buffer_end)
+			return QLA_ERROR;
+
+		isns_attr->tag = cpu_to_be32(attribute->isns_tag);
+		isns_attr->length = cpu_to_be32(data_len);
+
+		/*
+		 * Prepend IP Address with 0xFFFF to indicate this is an IPv4
+		 * only address. IPv6 addresses not supported by driver.
+		 */
+		memset(isns_attr->value, 0, 16);
+		isns_attr->value[10] = 0xFF;
+		isns_attr->value[11] = 0xFF;
+		isns_attr->value[12] = local[0];
+		isns_attr->value[13] = local[1];
+		isns_attr->value[14] = local[2];
+		isns_attr->value[15] = local[3];
+		break;
+
+	default:
+		return QLA_ERROR;
+	}
+
+	*buffer = &isns_attr->value[0] + data_len;
+	return QLA_SUCCESS;
+}
+
+uint32_t
+qla4xxx_isns_build_iocb_handle(scsi_qla_host_t *ha, uint32_t type,
+    PDU_ENTRY *pdu_entry)
+{
+	uint32_t handle;
+
+	handle = (IOCB_ISNS_PT_PDU_TYPE(type) | (((uint8_t *) pdu_entry -
+	    (uint8_t *) ha->pdu_queue) / sizeof(PDU_ENTRY)));
+	DEBUG5(printk("scsi%ld: %s: type %x PDU %p = handle %x\n",
+	    ha->host_no, __func__, type, pdu_entry, handle));
+
+	return handle;
+}
+
+/*
+ * Remarks:
+ *      hardware_lock locked upon entry
+ */
+int
+qla4xxx_isns_get_server_request(scsi_qla_host_t *ha, uint32_t pdu_buff_len,
+    uint16_t connection_id)
+{
+	PDU_ENTRY *pdu_entry;
+	pdu_entry = qla4xxx_get_pdu(ha, max(pdu_buff_len, (uint32_t)PAGE_SIZE));
+	if (pdu_entry == NULL) {
+		DEBUG5(printk("scsi%ld: %s: get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	pdu_entry->SendBuffLen = 0;
+	pdu_entry->RecvBuffLen = pdu_entry->BuffLen;
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX, connection_id,
+	    pdu_entry->DmaBuff, pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen,
+	    PT_FLAG_ISNS_PDU | PT_FLAG_WAIT_4_RESPONSE,
+	    qla4xxx_isns_build_iocb_handle(ha,
+		    /*ISNS_REQ_RSP_PDU */ ISNS_ASYNCH_REQ_PDU, pdu_entry)) !=
+	    QLA_SUCCESS) {
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_build_scn_registration_packet(scsi_qla_host_t *ha,
+    uint8_t *buffer, uint32_t buffer_size, uint32_t *packet_size)
+{
+	/*
+	 * Fill in all of the run time requested data in the attribute array
+	 * then call iSNSBuildRequestPacket to do the actual work.
+	 */
+	return qla4xxx_isns_build_request_packet(ha, buffer, buffer_size,
+	    ISNS_FCID_SCNReg, ha->isns_transaction_id, 0,
+	    ha->isns_scn_reg_attr_list, packet_size);
+}
+
+int
+qla4xxx_isns_build_scn_deregistration_packet(scsi_qla_host_t *ha,
+    uint8_t *buffer, uint32_t buffer_size, uint32_t *packet_size)
+{
+	/*
+	 * Fill in all of the run time requested data in the attribute array
+	 * then call iSNSBuildRequestPacket to do the actual work.
+	 */
+	return qla4xxx_isns_build_request_packet(ha, buffer, buffer_size,
+	    ISNS_FCID_SCNDereg, ha->isns_transaction_id, 0,
+	    ha->isns_scn_dereg_attr_list, packet_size);
+}
+
+int
+qla4xxx_isns_build_registration_packet(scsi_qla_host_t *ha, uint8_t *buff,
+    uint32_t buff_size, uint8_t *isns_entity_id, uint8_t *ip_addr,
+    uint32_t port_number, uint32_t scn_port, uint32_t esi_port,
+    uint8_t *local_alias, uint32_t *packet_size)
+{
+	/*
+	 * Fill in all of the run time requested data in the attribute array,
+	 * then call build_request_packet to do the actual work.
+	 */
+	ha->isns_reg_attr_list[1].data = (unsigned long)isns_entity_id;
+	ha->isns_reg_attr_list[3].data = (unsigned long)isns_entity_id;
+	ha->isns_reg_attr_list[5].data = (unsigned long)ip_addr;
+	ha->isns_reg_attr_list[6].data = cpu_to_be32(port_number);
+	ha->isns_reg_attr_list[7].data = cpu_to_be32(scn_port);
+	ha->isns_reg_attr_list[8].data = cpu_to_be32(esi_port);
+	if (local_alias && local_alias[0])
+		ha->isns_reg_attr_list[11].data = (unsigned long)local_alias;
+	else
+		ha->isns_reg_attr_list[11].data =
+		    (unsigned long)"<No alias specified>";
+
+	return qla4xxx_isns_build_request_packet(ha, buff, buff_size,
+	    ISNS_FCID_DevAttrReg, ha->isns_transaction_id, 0,
+	    ha->isns_reg_attr_list, packet_size);
+}
+
+int
+qla4xxx_isns_build_deregistration_packet(scsi_qla_host_t *ha, uint8_t *buff,
+    uint32_t buff_size, uint8_t *isns_entity_id, uint8_t *ip_addr,
+    uint32_t port_number, uint32_t *packet_size)
+{
+	/*
+	 * Fill in all of the run time requested data in the attribute array,
+	 * then call build_request_packet to do the actual work.
+	 */
+	ha->isns_dereg_attr_list[2].data = (unsigned long)isns_entity_id;
+
+#if 0
+	ha->isns_dereg_attr_list[3].data = (unsigned long)ip_addr;
+	ha->isns_dereg_attr_list[4].data =
+	    (unsigned long)cpu_to_be32(port_number);
+
+#endif
+	return qla4xxx_isns_build_request_packet(ha, buff, buff_size,
+	    ISNS_FCID_DevDereg, ha->isns_transaction_id, 0,
+	    ha->isns_dereg_attr_list, packet_size);
+}
+
+int
+qla4xxx_isns_build_request_packet(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size, uint16_t function_id, uint16_t tx_id,
+    uint8_t use_replace_flag, ATTRIBUTE_LIST *attr_list, uint32_t *packet_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	uint8_t *ptr;
+	uint8_t *buffer_end;
+	uint8_t *payload_start;
+	uint32_t i;
+	uint8_t success;
+
+	/*
+	 * Ensure that the buffer size is at a minimum sufficient to hold the
+	 * message header plus at least one attribute.
+	 */
+	if (buffer_size < (sizeof(*isns_message) + sizeof(*attr_list)))
+		return QLA_ERROR;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	buffer_end = (uint8_t *) ((unsigned long)buffer + buffer_size);
+
+	/* Initialize message header contents */
+	isns_message->isnsp_version = cpu_to_be16(ISNSP_VERSION);
+	isns_message->function_id = cpu_to_be16(function_id);
+	if (use_replace_flag)
+		isns_message->flags = cpu_to_be16(ISNSP_CLIENT_SENDER |
+		    ISNSP_FIRST_PDU | ISNSP_LAST_PDU | ISNSP_REPLACE_FLAG);
+	else
+		isns_message->flags = cpu_to_be16(ISNSP_CLIENT_SENDER |
+		    ISNSP_FIRST_PDU | ISNSP_LAST_PDU);
+
+	isns_message->transaction_id = cpu_to_be16(tx_id);
+	/* First and only packet in this message */
+	isns_message->sequence_id = 0;
+	ptr = payload_start = &isns_message->payload[0];
+
+	/*
+	 * Now that most of the message header has been initialized (we'll fill
+	 * in the size when we're finished), let's append the desired attributes
+	 * to the request packet.
+	 */
+	success = 1;
+	for (i = 0; attr_list[i].type && success; i++)
+		success = (qla4xxx_isns_append_attribute(ha, &ptr, buffer_end,
+		    &attr_list[i]) == QLA_SUCCESS);
+
+	if (!success) {
+		DEBUG5(printk("scsi%ld: %s: Ran out of buffer space\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	/*
+	 * We've successfully finished building the request packet.
+	 * Set the size field.
+	 */
+	isns_message->pdu_length = cpu_to_be16((unsigned long)ptr -
+	    (unsigned long) payload_start);
+	*packet_size = (uint32_t) ((unsigned long)ptr - (unsigned long)buffer);
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_build_server_request_response_packet(scsi_qla_host_t *ha,
+    uint8_t *buffer, uint32_t buffer_size, uint16_t function_id,
+    uint32_t error_code, uint16_t transaction_id, uint32_t *packet_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	uint8_t *ptr;
+	uint8_t *buffer_end;
+	uint8_t *payload_start;
+
+	/*
+	 * Ensure that the buffer size is at a minimum sufficient to hold the
+	 * message headers.
+	 */
+	if (buffer_size < (sizeof(ISNSP_MESSAGE_HEADER) +
+	    sizeof(ISNSP_RESPONSE_HEADER))) {
+		DEBUG2(printk("scsi%ld: %s: Insufficient buffer size %x\n",
+		    ha->host_no, __func__, buffer_size));
+		return QLA_ERROR;
+	}
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+	payload_start = (uint8_t *) isns_response;
+	buffer_end = (uint8_t *) (buffer + buffer_size);
+
+	/* Initialize message header contents. */
+	isns_message->isnsp_version = cpu_to_be16(ISNSP_VERSION);
+	isns_message->function_id = (function_id);
+
+	/*isns_message->function_id = cpu_to_be16(function_id); */
+	isns_message->flags =
+	    cpu_to_be16(ISNSP_CLIENT_SENDER | ISNSP_FIRST_PDU | ISNSP_LAST_PDU);
+	isns_message->transaction_id = (transaction_id);
+
+	/*isns_message->transaction_id = cpu_to_be16(transaction_id); */
+	/* First and only packet in this message */
+	isns_message->sequence_id = 0;
+	isns_response->error_code = cpu_to_be32(error_code);
+	ptr = &isns_response->attributes[0];
+
+	/* We've successfully finished building the request packet. */
+	isns_message->pdu_length = cpu_to_be16((unsigned long)ptr -
+	    (unsigned long) payload_start);
+	*packet_size = (unsigned long)ptr - (unsigned long)buffer;
+
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_build_dev_get_next_packet(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size, uint8_t *last_iscsi_name, uint32_t *packet_size)
+{
+	/*
+	 * Fill in all of the run time requested data in the attribute array
+	 * then call qla4xxx_isns_build_request_packet to do the actual work.
+	 */
+	if (last_iscsi_name && last_iscsi_name[0]) {
+		ha->isns_dev_get_next_attr_list[1].type = ISNS_ATTR_TYPE_STRING;
+		ha->isns_dev_get_next_attr_list[1].data =
+		    (unsigned long)last_iscsi_name;
+	} else {
+		ha->isns_dev_get_next_attr_list[1].type = ISNS_ATTR_TYPE_EMPTY;
+		ha->isns_dev_get_next_attr_list[1].data = 0;
+	}
+
+	return qla4xxx_isns_build_request_packet(ha, buffer, buffer_size,
+	    ISNS_FCID_DevGetNext, ha->isns_transaction_id, 0,
+	    ha->isns_dev_get_next_attr_list, packet_size);
+}
+
+int
+qla4xxx_isns_build_dev_attr_qry_packet(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size, uint8_t *object_iscsi_name, uint32_t *packet_size)
+{
+	/*
+	 * Fill in all of the run time requested data in the attribute array
+	 * then call qla4xxx_isns_build_request_packet to do the actual work.
+	 */
+	ha->isns_dev_attr_qry_attr_list[1].data =
+	    (unsigned long)object_iscsi_name;
+
+	return qla4xxx_isns_build_request_packet(ha, buffer, buffer_size,
+	    ISNS_FCID_DevAttrQry, ha->isns_transaction_id, 0,
+	    ha->isns_dev_attr_qry_attr_list, packet_size);
+}
+
+int
+qla4xxx_isns_parse_get_next_response(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size, uint32_t *isns_error, uint8_t *last_iscsi_name,
+    uint32_t last_iscsi_name_size, uint8_t *IsTarget)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	ISNS_ATTRIBUTE *isns_attr;
+	uint8_t *buffer_end;
+
+	*IsTarget = 0;
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	buffer_end = (uint8_t *) ((uint8_t *) & isns_message->payload[0] +
+	    be16_to_cpu(isns_message->pdu_length));
+
+	/* Validate pdu_length specified in the iSNS message header. */
+	if (((unsigned long)buffer_end - (unsigned long)buffer) > buffer_size) {
+		DEBUG5(printk("scsi%ld: %s: Invalid length field in "
+		    "iSNS response from iSNS server\n", ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	/*
+	 * It is safe to assume from this point on that the pdu_length value
+	 * (and thus our idea about the end of the buffer) is valid.
+	 */
+	if (be16_to_cpu(isns_message->function_id) != ISNS_FCID_DevGetNextRsp) {
+		DEBUG5(printk("scsi%ld: %s: Invalid Function ID "
+		    "(0x%04x) in iSNS response from iSNS server\n",
+		    ha->host_no, __func__,
+		    be16_to_cpu(isns_message->function_id)));
+		return QLA_ERROR;
+	}
+
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+	*isns_error = be32_to_cpu(isns_response->error_code);
+	if (*isns_error) {
+		DEBUG2(printk("scsi%ld: %s: iSNS Error code: %d\n",
+		    ha->host_no, __func__, *isns_error));
+		if (*isns_error == ISNS_ERR_NO_SUCH_ENTRY) {
+			DEBUG2(printk("scsi%ld: %s: No more targets.\n",
+			    ha->host_no, __func__));
+			set_bit(ISNS_FLAG_DEV_SCAN_DONE, &ha->isns_flags);
+		} else {
+			DEBUG2(printk("scsi%ld: %s: Get Next failed. Error "
+			    "code %x\n", ha->host_no, __func__, *isns_error));
+		}
+		return QLA_ERROR;
+	}
+	isns_attr = (ISNS_ATTRIBUTE *) & isns_response->attributes[0];
+
+	/* Save the returned key attribute for the next DevGetNext request. */
+	if (VALIDATE_ATTR(isns_attr, buffer_end) &&
+	    be32_to_cpu(isns_attr->tag) == ISNS_ATTR_TAG_ISCSI_NAME) {
+		strncpy(last_iscsi_name, &isns_attr->value[0],
+		    last_iscsi_name_size);
+	} else {
+		DEBUG2(printk("scsi%ld: %s: Bad Key attribute in "
+		    "DevGetNextRsp\n", ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	// Point to next attribute.
+	isns_attr = NEXT_ATTR(isns_attr);
+	if (VALIDATE_ATTR(isns_attr, buffer_end) &&
+	    be32_to_cpu(isns_attr->tag) == ISNS_ATTR_TAG_DELIMITER) {
+		;		/* Do nothing. */
+	} else {
+		DEBUG2(printk("scsi%ld: %s: No delimiter in DevGetNextRsp\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	*IsTarget = 1;		/*FIXME*/
+
+	/* Point to next attribute. */
+	isns_attr = NEXT_ATTR(isns_attr);
+	if (VALIDATE_ATTR(isns_attr, buffer_end) &&
+	    be32_to_cpu(isns_attr->tag) == ISNS_ATTR_TAG_ISCSI_NODE_TYPE) {
+		if (be32_to_cpu(*(uint32_t *) & isns_attr->value[0]) &
+		    ISCSI_NODE_TYPE_TARGET)
+			*IsTarget = 1;
+	}
+#if 0
+	else {
+		DEBUG5(printk("scsi%ld: %s: Bad operating attr in "
+		    "DevGetNextRsp (%d)\n", ha->host_no, __func__,
+		    be16_to_cpu(isns_attr->tag)));
+		return QLA_ERROR;
+	}
+
+#endif
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_parse_query_response(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size, uint32_t *isns_error,
+    ISNS_DISCOVERED_TARGET *isns_discovered_target, uint8_t *IsTarget,
+    uint8_t *last_iscsi_name)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	ISNS_ATTRIBUTE *isns_attr;
+	uint8_t *buffer_end;
+	uint8_t *tmpptr;
+	uint16_t wTmp;
+	uint32_t ulTmp;
+	uint32_t i;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	buffer_end = &isns_message->payload[0] +
+	    be16_to_cpu(isns_message->pdu_length);
+
+	/* Validate pdu_length specified in the iSNS message header. */
+	if (((unsigned long)buffer_end - (unsigned long)buffer) > buffer_size) {
+		DEBUG5(printk("scsi%ld: %s: Invalid length field in "
+		    "iSNS response from iSNS server\n", ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	/*
+	 * It is safe to assume from this point on that the pdu_length value
+	 * (and thus our idea about the end of the buffer) is valid.
+	 */
+	if (be16_to_cpu(isns_message->function_id) != ISNS_FCID_DevAttrQryRsp) {
+		DEBUG5(printk("scsi%ld: %s: Invalid Function ID %04x "
+		    "in iSNS response\n", ha->host_no, __func__,
+		    be16_to_cpu(isns_message->function_id)));
+		return QLA_ERROR;
+	}
+
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+	DEBUG2(printk("-----------------------------\n"));
+	DEBUG2(printk("scsi%ld: %s: DevAttrQry response from iSNS server:\n",
+	    ha->host_no, __func__));
+	*isns_error = be32_to_cpu(isns_response->error_code);
+	if (*isns_error) {
+		DEBUG5(printk("scsi%ld: %s: iSNS Query failed.  "
+		    "error_code %x.\n", ha->host_no, __func__, *isns_error));
+		return QLA_ERROR;
+	}
+	DEBUG2(printk("scsi%ld: %s: Attributes:\n", ha->host_no, __func__));
+
+	isns_attr = (ISNS_ATTRIBUTE *) & isns_response->attributes[0];
+
+	/* Skip key and delimiter attributes. */
+	while (VALIDATE_ATTR(isns_attr, buffer_end) &&
+	    be32_to_cpu(isns_attr->tag) != ISNS_ATTR_TAG_DELIMITER) {
+
+		/* Point to next attribute. */
+		if (be32_to_cpu(isns_attr->tag) == ISNS_ATTR_TAG_ISCSI_NAME) {
+			/*
+			 * Note that this string is in UTF-8 format.  In
+			 * production code, it would be necessary to convert
+			 * from UTF-8 before using the  string.
+			 */
+			DEBUG2(printk("scsi%ld: %s: MsgTag iSCSI Name: "
+			    "\"%s\"\n", ha->host_no, __func__,
+			    isns_attr->value));
+			if (strlen(isns_attr->value) > 256)
+				return QLA_ERROR;
+			strcpy(last_iscsi_name,
+			    (uint8_t *) &isns_attr->value[0]);
+		}
+		isns_attr = NEXT_ATTR(isns_attr);
+	}
+
+	if (!VALIDATE_ATTR(isns_attr, buffer_end) ||
+	    be32_to_cpu(isns_attr->tag) != ISNS_ATTR_TAG_DELIMITER) {
+		/* There was no delimiter attribute in the response. */
+		return QLA_ERROR;
+	}
+
+	/* Skip delimiter attribute. */
+	isns_attr = NEXT_ATTR(isns_attr);
+	while (VALIDATE_ATTR(isns_attr, buffer_end)) {
+		/*
+		 * We only need to parse for the operating attributes that we
+		 * requested in the DevAttrQuery.
+		 */
+		switch (be32_to_cpu(isns_attr->tag)) {
+		case ISNS_ATTR_TAG_ENTITY_PROTOCOL:
+			if (be32_to_cpu(*(uint32_t *) isns_attr->value) !=
+			    ENTITY_PROTOCOL_ISCSI) {
+				DEBUG5(printk("scsi%ld: %s: Entity "
+				    "does not support iSCSI protocol\n",
+				    ha->host_no, __func__));
+			}
+			break;
+
+		case ISNS_ATTR_TAG_ISCSI_NODE_TYPE:
+			switch (be32_to_cpu(*(uint32_t *) isns_attr->value)) {
+			case ISCSI_NODE_TYPE_TARGET:
+				*IsTarget = 1;
+				break;
+
+			case ISCSI_NODE_TYPE_INITIATOR:
+				*IsTarget = 0;
+				break;
+
+			case ISCSI_NODE_TYPE_CONTROL:
+				*IsTarget = 0;
+				break;
+
+			default:
+				*IsTarget = 0;
+				break;
+			}
+			break;
+
+		case ISNS_ATTR_TAG_MGMT_IP_ADDRESS:
+			/* WARNING: This doesn't handle IPv6 addresses. */
+			tmpptr = &isns_attr->value[0];
+			for (i = 0; i < 8; i++)
+				if (tmpptr[i])
+					return QLA_ERROR;
+
+			for (i = 8; i < 12; i++)
+				if (tmpptr[i] != 0 && tmpptr[i] != 0xFF)
+					return QLA_ERROR;
+
+			DEBUG5(printk("scsi%ld: %s: Management IP "
+			    "address: %u.%u.%u.%u\n", ha->host_no, __func__,
+			    tmpptr[12], tmpptr[13], tmpptr[14], tmpptr[15]));
+			break;
+
+		case ISNS_ATTR_TAG_PORTAL_IP_ADDRESS:
+			/* WARNING: This doesn't handle IPv6 addresses. */
+			tmpptr = &isns_attr->value[0];
+			for (i = 0; i < 8; i++)
+				if (tmpptr[i])
+					return QLA_ERROR;
+
+			for (i = 8; i < 12; i++)
+				if (tmpptr[i] != 0 && tmpptr[i] != 0xFF)
+					return QLA_ERROR;
+
+			DEBUG5(printk("scsi%ld: %s: Portal IP "
+			    "address: %u.%u.%u.%u\n", ha->host_no, __func__,
+			    tmpptr[12], tmpptr[13], tmpptr[14], tmpptr[15]));
+			if (isns_discovered_target->NumPortals >=
+			    ISNS_MAX_PORTALS)
+				break;
+
+			memcpy(isns_discovered_target->
+			    Portal[isns_discovered_target->NumPortals].IPAddr,
+			    &tmpptr[12], 4);
+			break;
+
+		case ISNS_ATTR_TAG_PORTAL_PORT:
+			wTmp = (uint16_t) (be32_to_cpu(
+			    *(uint32_t *)isns_attr->value));
+			DEBUG5(printk("scsi%ld: %s: Portal port: "
+			    "%u\n", ha->host_no, __func__,
+			    be32_to_cpu(*(uint32_t *)isns_attr-> value)));
+			if (isns_discovered_target->NumPortals >=
+			    ISNS_MAX_PORTALS)
+				break;
+
+			isns_discovered_target->
+				Portal[isns_discovered_target->NumPortals].
+				PortNumber = wTmp;
+			isns_discovered_target->NumPortals++;
+			break;
+
+		case ISNS_ATTR_TAG_PORTAL_SYMBOLIC_NAME:
+			/*
+			 * Note that this string is in UTF-8 format.  In
+			 * production code, it would be necessary to convert
+			 * from UTF-8 before using the  string.
+			 */
+			DEBUG5(printk("scsi%ld: %s: Portal Symbolic "
+			    "Name: \"%s\"\n", ha->host_no, __func__,
+			    &isns_attr->value[0]));
+
+#if 0
+			if (isns_discovered_target->NumPortals >=
+			    ISNS_MAX_PORTALS)
+				break;
+			qlstrncpy(isns_discovered_target->
+			    Portal[isns_discovered_target->NumPortals].
+			    SymbolicName, (uint8_t *) isns_attr->value, 32);
+			isns_discovered_target->
+				Portal[isns_discovered_target->NumPortals].
+				SymbolicName[31] = 0;
+#endif
+			break;
+
+		case ISNS_ATTR_TAG_SCN_PORT:
+			break;
+		case ISNS_ATTR_TAG_ESI_PORT:
+			break;
+		case ISNS_ATTR_TAG_ESI_INTERVAL:
+			break;
+		case ISNS_ATTR_TAG_REGISTRATION_PERIOD:
+			break;
+
+		case ISNS_ATTR_TAG_PORTAL_SECURITY_BITMAP:
+			ulTmp = be32_to_cpu(*(uint32_t *) isns_attr->value);
+
+			/* isns_discovered_target->SecurityBitmap = ulTmp; */
+			break;
+
+		case ISNS_ATTR_TAG_ENTITY_IDENTIFIER:
+			/*
+			 * Note that this string is in UTF-8 format.  In
+			 * production code, it would be necessary to convert
+			 * from UTF-8 before using the  string.
+			 */
+			break;
+
+		case ISNS_ATTR_TAG_ISCSI_NAME:
+			/*
+			 * Note that this string is in UTF-8 format.  In
+			 * production code, it would be necessary to convert
+			 * from UTF-8 before using the  string.
+			 */
+			if (strlen(isns_attr->value) > 256)
+				return (QLA_ERROR);
+			strcpy(isns_discovered_target->NameString,
+			       (uint8_t *) isns_attr->value);
+			break;
+
+		case ISNS_ATTR_TAG_ISCSI_ALIAS:
+			/*
+			 * Note that this string is in UTF-8 format.  In
+			 * production code, it would be necessary to convert
+			 * from UTF-8 before using the  string.
+			 */
+			if (strlen(isns_attr->value) <= 32)
+				strcpy(isns_discovered_target->Alias,
+				       (uint8_t *) isns_attr->value);
+			break;
+
+		case ISNS_ATTR_TAG_DD_ID:
+			ulTmp = be32_to_cpu(*(uint32_t *) isns_attr->value);
+			isns_discovered_target->DDID = ulTmp;
+			break;
+
+		default:
+			break;
+		}
+
+		/* Point to next attribute. */
+		isns_attr = NEXT_ATTR(isns_attr);
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_process_response(scsi_qla_host_t *ha,
+    PASSTHRU_STATUS_ENTRY *sts_entry)
+{
+	uint32_t handle = le32_to_cpu(sts_entry->handle);
+	uint32_t inResidual = le32_to_cpu(sts_entry->inResidual);
+	uint16_t connectionID = le16_to_cpu(sts_entry->connectionID);
+	PDU_ENTRY *pdu_entry =
+	    (PDU_ENTRY *) & ha->pdu_queue[IOCB_ISNS_PT_PDU_INDEX(handle)];
+	uint32_t pdu_type = IOCB_ISNS_PT_PDU_TYPE(handle);
+	uint8_t status = QLA_SUCCESS;
+
+	DEBUG5(printk("scsi%ld: %s isns_flags 0x%lx to=0x%x "
+	    "IOCS=0x%02x OutResidual/Len=0x%x/0x%x InResidual/Len=0x%x/0x%x\n",
+	    ha->host_no, __func__, ha->isns_flags,
+	    le16_to_cpu(sts_entry->timeout), sts_entry->completionStatus,
+	    le32_to_cpu(sts_entry->outResidual), pdu_entry->SendBuffLen,
+	    inResidual, pdu_entry->RecvBuffLen));
+	if (pdu_entry->RecvBuffLen - inResidual) {
+		DEBUG5(printk("PDU (0x%p) <-\n", pdu_entry->Buff));
+		DEBUG5(qla4xxx_dump_bytes(pdu_entry->Buff,
+		    pdu_entry->RecvBuffLen - inResidual));
+	}
+	if (sts_entry->completionStatus != PASSTHRU_STATUS_COMPLETE) {
+		qla4xxx_free_pdu(ha, pdu_entry);
+		set_bit(DPC_ISNS_RESTART, &ha->dpc_flags);
+		goto exit_pt_sts;
+	}
+	switch (pdu_type) {
+	case ISNS_ASYNCH_RSP_PDU:
+		qla4xxx_free_pdu(ha, pdu_entry);
+		break;
+
+	case ISNS_ASYNCH_REQ_PDU:
+		pdu_entry->RecvBuffLen -= inResidual;
+		DEBUG5(printk("scsi%ld: %s ISNS_ASYNCH_REQ_PDU  PDU "
+		    "Buff=%p, PDU RecvLen=0x%X\n", ha->host_no, __func__,
+		    pdu_entry->Buff, pdu_entry->RecvBuffLen));
+		if (qla4xxx_isns_reassemble_pdu(ha, pdu_entry->Buff,
+		    &pdu_entry->RecvBuffLen) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s ISNS_ASYNCH_REQ_PDU "
+			    "reassemble_pdu failed!\n", ha->host_no, __func__));
+			goto exit_pt_sts;
+		}
+		if (qla4xxx_isns_parse_and_dispatch_server_request(ha,
+		    pdu_entry->Buff, pdu_entry->RecvBuffLen, connectionID) !=
+		    QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s ISNS_ASYNCH_REQ_PDU "
+			    "parse_and_dispatch_server_request failed!\n",
+			    ha->host_no, __func__));
+		}
+		qla4xxx_free_pdu(ha, pdu_entry);
+		break;
+
+	case ISNS_REQ_RSP_PDU:
+		pdu_entry->RecvBuffLen -= inResidual;
+		if (qla4xxx_isns_reassemble_pdu(ha, pdu_entry->Buff,
+		    &pdu_entry->RecvBuffLen) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s ISNS_REQ_RSP_PDU "
+			    "reassemble_pdu failed!\n", ha->host_no, __func__));
+			goto exit_pt_sts;
+		}
+		if (qla4xxx_isns_parse_and_dispatch_server_response(ha,
+		    pdu_entry->Buff, pdu_entry->RecvBuffLen) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s ISNS_REQ_RSP_PDU "
+			    "parse_and_dispatch_server_response failed!\n",
+			    ha->host_no, __func__));
+		}
+		qla4xxx_free_pdu(ha, pdu_entry);
+		break;
+
+	default:
+		DEBUG2(printk("scsi%ld: %s iSNS handle 0x%x invalid\n",
+		    ha->host_no, __func__, sts_entry->handle));
+		status = QLA_ERROR;
+		break;
+	}
+exit_pt_sts:
+	return status;
+}
+
+int
+qla4xxx_isns_reassemble_pdu(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t *buffer_size)
+{
+	uint16_t copy_size = 0;
+	uint32_t new_pdu_length = 0;
+	uint32_t bytes_remaining;
+	uint32_t pdu_size;
+	uint8_t *dest_ptr = NULL;
+	uint8_t *src_ptr = NULL;
+	ISNSP_MESSAGE_HEADER *isns_message;
+	uint32_t i;
+
+	/*
+	 * We have read all the PDU's for this message.  Now reassemble them
+	 * into a single PDU.
+	 */
+	if (buffer == NULL || buffer_size == 0)
+		return QLA_ERROR;
+
+	if (*buffer_size == 0) {
+		DEBUG5(printk(KERN_WARNING "scsi%ld: %s: Length 0.  "
+		    "Nothing to reassemble\n", ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	new_pdu_length = 0;
+	bytes_remaining = *buffer_size;
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	if ((!be16_to_cpu(isns_message->flags) & ISNSP_FIRST_PDU)) {
+		*buffer_size = 0;
+		return QLA_ERROR;
+	}
+
+	/* First, calculate the size of the payload for the collapsed PDU */
+	do {
+		if (bytes_remaining < sizeof(ISNSP_MESSAGE_HEADER)) {
+			DEBUG2(printk(KERN_WARNING
+			    "scsi%ld: %s: Length 0.  bytes_remaining < "
+			    "sizeof(ISNSP_MESSAGE_HEADER).  BytesRemaining "
+			    "%x, discard PDU\n", ha->host_no, __func__,
+			    bytes_remaining));
+			*buffer_size = 0;
+			return QLA_ERROR;
+		} else if (be16_to_cpu(isns_message->isnsp_version) !=
+		    ISNSP_VERSION) {
+			DEBUG5(printk(KERN_WARNING
+			    "scsi%ld: %s: Bad Version number in iSNS Message "
+			    "Header (%04x, expecting %04x), discard PDU\n",
+			    ha->host_no, __func__,
+			    be16_to_cpu(isns_message-> isnsp_version),
+			    ISNSP_VERSION));
+			*buffer_size = 0;
+			return QLA_ERROR;
+		} else if (bytes_remaining < sizeof(ISNSP_MESSAGE_HEADER) +
+		    be16_to_cpu(isns_message->pdu_length)) {
+			DEBUG5(printk(KERN_WARNING
+			    "scsi%d: %s: Short PDU in sequence. "
+			    "BytesRemaining %x, discard PDU\n", ha->host_no,
+			    __func__, bytes_remaining));
+			*buffer_size = 0;
+			return QLA_ERROR;
+		} else if ((bytes_remaining == sizeof(ISNSP_MESSAGE_HEADER) +
+		    be16_to_cpu(isns_message->pdu_length)) &&
+			(!(be16_to_cpu(isns_message->flags) &
+			    ISNSP_LAST_PDU))) {
+			*buffer_size = 0;
+			return QLA_ERROR;
+		}
+
+		new_pdu_length += be16_to_cpu(isns_message->pdu_length);
+		pdu_size = sizeof(ISNSP_MESSAGE_HEADER) +
+		    be16_to_cpu(isns_message->pdu_length);
+		isns_message = (ISNSP_MESSAGE_HEADER *)
+			((uint8_t *) isns_message + pdu_size);
+		bytes_remaining = bytes_remaining > pdu_size ?
+		    bytes_remaining - pdu_size : 0;
+	} while (bytes_remaining);
+
+	dest_ptr = buffer;
+	bytes_remaining = *buffer_size;
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	i = 0;
+
+	DEBUG5(printk("scsi%ld: %s: PDU%d=%p payloadLength=%04x\n",
+	    ha->host_no, __func__, i, dest_ptr,
+	    be16_to_cpu(isns_message->pdu_length)));
+
+	while (bytes_remaining) {
+		/*
+		 * If this is the first PDU perform no copy, otherwise copy
+		 * just the payload.
+		 */
+		if (dest_ptr != buffer) {
+			i++;
+			copy_size = be16_to_cpu(isns_message->pdu_length);
+			src_ptr = (uint8_t *) isns_message->payload;
+			DEBUG5(printk("scsi%ld: %s: PDU%d %p <= %p "
+			    "(%04x)\n", ha->host_no, __func__, i, dest_ptr,
+			    src_ptr, copy_size));
+			memcpy(dest_ptr, src_ptr, copy_size);
+			dest_ptr += copy_size;
+		}
+		pdu_size = sizeof(ISNSP_MESSAGE_HEADER) +
+		    be16_to_cpu(isns_message->pdu_length);
+		isns_message = (ISNSP_MESSAGE_HEADER *)
+			((uint8_t *) isns_message + pdu_size);
+		bytes_remaining = bytes_remaining > pdu_size ?
+		    bytes_remaining - pdu_size : 0;
+	}
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+
+	/* Update pdu_length field in reassembled PDU to reflect actual */
+	/* combined PDU payload length. */
+	isns_message->pdu_length = cpu_to_be16(new_pdu_length);
+
+	/* Also set LAST_PDU flag in reassembled PDU */
+	isns_message->flags |= cpu_to_be16(ISNSP_LAST_PDU);
+
+	/* Return number of bytes in buffer to caller. */
+	*buffer_size = new_pdu_length + sizeof(ISNSP_MESSAGE_HEADER);
+
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_scn(scsi_qla_host_t *ha, uint8_t *req_buffer,
+    uint32_t req_buffer_size, uint16_t ConnectionId)
+{
+	ISNSP_MESSAGE_HEADER *isns_req_message;
+	ISNSP_MESSAGE_HEADER *isns_rsp_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	PDU_ENTRY *pdu_entry;
+	ISNS_ATTRIBUTE *attr;
+	uint8_t *req_buffer_end;
+	uint8_t *rsp_buffer_end;
+	uint8_t *payload_start;
+	uint8_t *ptr;
+	uint32_t packet_size;
+	uint32_t copy_size;
+
+	isns_req_message = (ISNSP_MESSAGE_HEADER *) req_buffer;
+	if ((pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE)) == NULL) {
+		DEBUG5(printk("scsi%ld: %s: qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	/* First, setup the response packet. */
+	if (qla4xxx_isns_build_server_request_response_packet(ha,
+	    pdu_entry->Buff, pdu_entry->BuffLen,
+	    be16_to_cpu(isns_req_message->function_id) | 0x8000,
+	    ISNS_ERR_SUCCESS, be16_to_cpu(isns_req_message->transaction_id),
+	    &packet_size) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: qla4xxx_isns_build_server_"
+		    "request_response_packet failed\n", ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+
+	isns_rsp_message = (ISNSP_MESSAGE_HEADER *) pdu_entry->Buff;
+	isns_response = (ISNSP_RESPONSE_HEADER *) &
+	    isns_rsp_message->payload[0];
+	payload_start = (uint8_t *) isns_response;
+	rsp_buffer_end = (uint8_t *) (pdu_entry->Buff + pdu_entry->BuffLen);
+	ptr = &isns_response->attributes[0];
+	req_buffer_end = (uint8_t *) ((uint8_t *) &
+	    isns_req_message->payload[0] +
+	    be16_to_cpu(isns_req_message->pdu_length));
+
+	/*
+	 * Point to the source attribute in the request.  We need to return
+	 * only this attribute in the SCN Response.
+	 */
+	attr = (ISNS_ATTRIBUTE *) & isns_req_message->payload[0];
+	if (!VALIDATE_ATTR(attr, req_buffer_end)) {
+		isns_response->error_code = cpu_to_be32(ISNS_ERR_MSG_FORMAT);
+		DEBUG5(printk("scsi%ld: %s: Malformed packet\n",
+		    ha->host_no, __func__));
+	}
+
+	/* Validate that this is an iSCSI Name attribute. */
+	if (be32_to_cpu(attr->tag) != ISNS_ATTR_TAG_ISCSI_NAME) {
+		DEBUG5(printk("scsi%ld: %s: Did not find iSCSN Name "
+		    "attribute\n", ha->host_no, __func__));
+	}
+
+	/* Copy source attribute to return buffer. */
+	copy_size = sizeof(ISNS_ATTRIBUTE) + be32_to_cpu(attr->length);
+	if (ptr + copy_size < rsp_buffer_end) {
+		/*
+		 * Attribute will fit in the response buffer.  Go ahead
+		 * and copy it.
+		 */
+		memcpy(ptr, attr, copy_size);
+		ptr += copy_size;
+	} else {
+		DEBUG5(printk("scsi%ld: %s: Insufficient buffer size\n",
+		    ha->host_no, __func__));
+	}
+
+	/* We've successfully finished building the response packet. */
+	/* Set the size field. */
+	isns_rsp_message->pdu_length = cpu_to_be16((unsigned long)ptr -
+	    (unsigned long) payload_start);
+	packet_size = (unsigned long)ptr - (unsigned long)pdu_entry->Buff;
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = 0;
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX, ConnectionId,
+	    pdu_entry->DmaBuff, pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen,
+	    PT_FLAG_ISNS_PDU, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_ASYNCH_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+
+	if (test_bit(ISNS_FLAG_SCN_IN_PROGRESS, &ha->isns_flags)) {
+		set_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags);
+	} else {
+		set_bit(ISNS_FLAG_SCN_IN_PROGRESS, &ha->isns_flags);
+		clear_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags);
+		ha->isns_num_discovered_targets = 0;
+		if (qla4xxx_isns_dev_get_next(ha, NULL) != QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_get_next failed\n", ha->host_no,
+			    __func__));
+			ISNS_CLEAR_FLAGS(ha);
+		}
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_esi(scsi_qla_host_t *ha, uint8_t *req_buffer,
+    uint32_t req_buffer_size, uint16_t ConnectionId)
+{
+	ISNSP_MESSAGE_HEADER *isns_req_message;
+	ISNSP_MESSAGE_HEADER *isns_rsp_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	PDU_ENTRY *pdu_entry;
+	ISNS_ATTRIBUTE *attr;
+	uint8_t *req_buffer_end;
+	uint8_t *rsp_buffer_end;
+	uint8_t *payload_start;
+	uint8_t *ptr;
+	uint32_t packet_size;
+	uint32_t copy_size;
+
+	isns_req_message = (ISNSP_MESSAGE_HEADER *) req_buffer;
+	if ((pdu_entry = qla4xxx_get_pdu(ha, req_buffer_size +
+	    sizeof(uint32_t))) == NULL) {
+		DEBUG5(printk("scsi%ld: %s:  qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	/* First, setup the response packet. */
+	if (qla4xxx_isns_build_server_request_response_packet(ha,
+	    pdu_entry->Buff, pdu_entry->BuffLen,
+	    be16_to_cpu(isns_req_message->function_id | 0x8000),
+	    ISNS_ERR_SUCCESS, be16_to_cpu(isns_req_message->transaction_id),
+	    &packet_size) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_isns_build_server_request_response_packet "
+		    "failed\n", ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+
+	isns_rsp_message = (ISNSP_MESSAGE_HEADER *) pdu_entry->Buff;
+	isns_response = (ISNSP_RESPONSE_HEADER *) &
+	    isns_rsp_message->payload[0];
+	payload_start = (uint8_t *) isns_response;
+	rsp_buffer_end = (uint8_t *) (pdu_entry->Buff + pdu_entry->BuffLen);
+	ptr = &isns_response->attributes[0];
+	req_buffer_end = (uint8_t *) ((uint8_t *) &
+	    isns_req_message->payload[0] +
+	    be16_to_cpu(isns_req_message->pdu_length));
+
+	/* Point to the source attribute in the request.  We need to return */
+	/* all attributes in the ESI Response. */
+	attr = (ISNS_ATTRIBUTE *) & isns_req_message->payload[0];
+
+	/* Copy source attributes to return buffer. */
+	copy_size = req_buffer_end - (uint8_t *) attr;
+	if (ptr + copy_size < rsp_buffer_end) {
+		/* Attributes will fit in the response buffer.  Go ahead */
+		/* and copy them. */
+		memcpy(ptr, attr, copy_size);
+		ptr += copy_size;
+	} else {
+		DEBUG5(printk("scsi%ld: %s: Insufficient buffer size\n",
+		    ha->host_no, __func__));
+	}
+
+	/* We've successfully finished building the response packet. */
+	/* Set the size field. */
+	isns_rsp_message->pdu_length = cpu_to_be16((unsigned long)ptr -
+	    (unsigned long) payload_start);
+	packet_size = (unsigned long)ptr - (unsigned long)pdu_entry->Buff;
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = 0;
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX, ConnectionId,
+	    pdu_entry->DmaBuff, pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen,
+	    PT_FLAG_ISNS_PDU, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_ASYNCH_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_server_request_error(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size, uint16_t connection_id, uint32_t error_code)
+{
+	PDU_ENTRY *pdu_entry;
+	ISNSP_MESSAGE_HEADER *isns_message;
+	uint16_t function_id;
+	uint32_t packet_size;
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	function_id = be16_to_cpu(isns_message->function_id);
+
+	// Return "Message Format Error"
+	if ((pdu_entry = qla4xxx_get_pdu(ha, sizeof(ISNSP_MESSAGE_HEADER) +
+	    sizeof(uint32_t))) == NULL) {
+		DEBUG5(printk("scsi%ld: %s:  qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	if (qla4xxx_isns_build_server_request_response_packet(ha,
+	    pdu_entry->Buff, pdu_entry->BuffLen,
+	    be16_to_cpu(isns_message->function_id) | 0x8000, error_code,
+	    be16_to_cpu(isns_message->transaction_id), &packet_size) !=
+	    QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_isns_build_server_request_response_packet "
+		    "failed\n", ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = 0;
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX, connection_id,
+	    pdu_entry->DmaBuff, pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen,
+	    PT_FLAG_ISNS_PDU, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_ASYNCH_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_parse_and_dispatch_server_request(scsi_qla_host_t *ha,
+    uint8_t *buffer, uint32_t buffer_size, uint16_t connection_id)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	uint16_t function_id;
+	uint16_t transaction_id;
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	function_id = be16_to_cpu(isns_message->function_id);
+	transaction_id = be16_to_cpu(isns_message->transaction_id);
+
+	/* Validate pdu_length specified in the iSNS message header. */
+	if ((offsetof(ISNSP_MESSAGE_HEADER, payload) +
+	    be16_to_cpu(isns_message->pdu_length)) > buffer_size) {
+		DEBUG5(printk("scsi%ld: %s: Invalid message size %u "
+		    "%u\n", ha->host_no, __func__,
+		    (uint32_t) (offsetof(ISNSP_MESSAGE_HEADER, payload) +
+			be16_to_cpu(isns_message->pdu_length)), buffer_size));
+		if (function_id <= ISNS_FCID_ESI) {
+			return qla4xxx_isns_server_request_error(ha, buffer,
+			    buffer_size, connection_id, ISNS_ERR_MSG_FORMAT);
+		}
+		return QLA_ERROR;
+	}
+
+	/* It is safe to assume from this point on that the pdu_length value */
+	/* (and thus our idea about the end of the buffer) is valid. */
+	switch (function_id) {
+	case ISNS_FCID_SCN:
+		return qla4xxx_isns_scn(ha, buffer, buffer_size, connection_id);
+		break;
+
+	case ISNS_FCID_ESI:
+		return qla4xxx_isns_esi(ha, buffer, buffer_size, connection_id);
+		break;
+
+	default:
+		if (function_id <= ISNS_FCID_ESI) {
+			/* Return "Message Not Supported" */
+			return qla4xxx_isns_server_request_error(ha, buffer,
+			    buffer_size, connection_id,
+			    ISNS_ERR_MSG_NOT_SUPPORTED);
+		}
+		return QLA_ERROR;
+		break;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_parse_and_dispatch_server_response(scsi_qla_host_t *ha,
+    uint8_t *buffer, uint32_t buffer_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	ISNS_ATTRIBUTE *isns_attr;
+	uint16_t function_id;
+	uint16_t transaction_id;
+	uint8_t *buffer_end;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	buffer_end = (uint8_t *) ((uint8_t *) isns_message->payload +
+	    be16_to_cpu(isns_message->pdu_length));
+	isns_attr = (ISNS_ATTRIBUTE *) isns_message->payload;
+
+	/* Validate pdu_length specified in the iSNS message header. */
+	if (((uint32_t *) buffer_end - (uint32_t *) buffer) > buffer_size) {
+		DEBUG5(printk("scsi%ld: %s: Invalid message size %u "
+		    "%u\n", ha->host_no, __func__,
+		    (unsigned int)((uint32_t *) buffer_end -
+			(uint32_t *) buffer), buffer_size));
+		return QLA_ERROR;
+	}
+	transaction_id = be16_to_cpu(isns_message->transaction_id);
+	function_id = be16_to_cpu(isns_message->function_id);
+
+	/*
+	 * It is safe to assume from this point on that the pdu_length value
+	 * (and thus our idea about the end of the buffer) is valid.
+	 */
+	if (transaction_id > ha->isns_transaction_id) {
+		DEBUG5(printk("scsi%ld: %s: Invalid message "
+		    "transaction ID recv %x exp %x\n", ha->host_no, __func__,
+		    transaction_id, ha->isns_transaction_id));
+		DEBUG5(qla4xxx_dump_bytes(buffer, buffer_size));
+		set_bit(DPC_ISNS_RESTART, &ha->dpc_flags);
+		return QLA_ERROR;
+	}
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+
+	switch (function_id) {
+	case ISNS_FCID_DevAttrRegRsp:
+		DEBUG5(printk("scsi%ld: %s: received %d "
+		    "DevAttrRegRsp\n", ha->host_no, __func__, transaction_id));
+		return qla4xxx_isns_dev_attr_reg_rsp(ha, buffer, buffer_size);
+
+	case ISNS_FCID_DevAttrQryRsp:
+		return qla4xxx_isns_dev_attr_qry_rsp(ha, buffer, buffer_size);
+
+	case ISNS_FCID_DevGetNextRsp:
+		return qla4xxx_isns_dev_get_next_rsp(ha, buffer, buffer_size);
+
+	case ISNS_FCID_DevDeregRsp:
+		return qla4xxx_isns_dev_dereg_rsp(ha, buffer, buffer_size);
+
+	case ISNS_FCID_SCNRegRsp:
+		return qla4xxx_isns_scn_reg_rsp(ha, buffer, buffer_size);
+
+	case ISNS_FCID_SCNDeregRsp:
+		return qla4xxx_isns_scn_dereg_rsp(ha, buffer, buffer_size);
+
+	default:
+		DEBUG5(printk("scsi%ld: %s: Received %d Unknown iSNS "
+		    "function_id %x\n", ha->host_no, __func__, transaction_id,
+		    function_id));
+		break;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_attr_reg(scsi_qla_host_t *ha)
+{
+	PDU_ENTRY *pdu_entry;
+	uint32_t packet_size;
+
+	pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE);
+	if (pdu_entry == NULL) {
+		DEBUG5(printk("scsi%ld: %s: get pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+
+	if (qla4xxx_isns_build_registration_packet(ha, pdu_entry->Buff,
+	    pdu_entry->BuffLen, ha->isns_entity_id, ha->ip_address,
+	    ha->isns_remote_port_num, ha->isns_scn_port_num,
+	    ha->isns_esi_port_num, ha->alias, &packet_size) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: "
+		    "qla4xxx_isns_build_registration_packet failed\n",
+		    ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = pdu_entry->BuffLen;
+	DEBUG5(printk("---------------------------\n"));
+	DEBUG5(printk("scsi%ld: %s:                    sending %d "
+	    "DevAttrReg\n", ha->host_no, __func__, ha->isns_transaction_id));
+	DEBUG5(printk("PDU (0x%p) 0x%x ->\n", pdu_entry->Buff,
+	    pdu_entry->SendBuffLen));
+	DEBUG5(qla4xxx_dump_bytes(pdu_entry->Buff, pdu_entry->SendBuffLen));
+	DEBUG5(printk("scsi%ld: %s: Registering iSNS . . .\n",
+	    ha->host_no, __func__));
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX,
+	    ISNS_DEFAULT_SERVER_CONN_ID, pdu_entry->DmaBuff,
+	    pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen, PT_FLAG_ISNS_PDU |
+	    PT_FLAG_WAIT_4_RESPONSE, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_REQ_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	ha->isns_transaction_id++;
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_attr_reg_rsp(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+	uint32_t error_code;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+	error_code = be32_to_cpu(isns_response->error_code);
+	if (error_code) {
+		DEBUG5(printk("scsi%ld: %s: iSNS DevAttrReg failed, "
+		    "error code (%x) \"%s\"\n", ha->host_no, __func__,
+		    error_code, isns_error_code_msg[error_code]));
+		clear_bit(ISNS_FLAG_ISNS_SRV_REGISTERED, &ha->isns_flags);
+		return QLA_ERROR;
+	}
+	set_bit(ISNS_FLAG_ISNS_SRV_REGISTERED, &ha->isns_flags);
+	if (qla4xxx_isns_scn_reg(ha) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: qla4xxx_isns_scn_reg "
+		    "failed\n", ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_scn_reg(scsi_qla_host_t *ha)
+{
+	PDU_ENTRY *isns_pdu_entry;
+	uint32_t packet_size;
+
+	if ((isns_pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE)) == NULL) {
+		DEBUG5(printk("scsi%ld: %s: qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	if (qla4xxx_isns_build_scn_registration_packet(ha,
+	    isns_pdu_entry->Buff, isns_pdu_entry->BuffLen, &packet_size) !=
+	    QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: qla4xxx_isns_build_scn_"
+		    "registration_packet failed\n", ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, isns_pdu_entry);
+		return QLA_ERROR;
+	}
+	isns_pdu_entry->SendBuffLen = packet_size;
+	isns_pdu_entry->RecvBuffLen = isns_pdu_entry->BuffLen;
+	DEBUG5(printk("---------------------------\n"));
+	DEBUG5(printk("scsi%ld :%s:                        sending  "
+	    "%d SCNReg\n", ha->host_no, __func__, ha->isns_transaction_id));
+	DEBUG5(printk("PDU (0x%p) 0x%x ->\n", isns_pdu_entry->Buff,
+	    isns_pdu_entry->SendBuffLen));
+	DEBUG5(qla4xxx_dump_bytes(isns_pdu_entry->Buff,
+	    isns_pdu_entry->SendBuffLen));
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX,
+	    ISNS_DEFAULT_SERVER_CONN_ID, isns_pdu_entry->DmaBuff,
+	    isns_pdu_entry->SendBuffLen, isns_pdu_entry->RecvBuffLen,
+	    PT_FLAG_ISNS_PDU | PT_FLAG_WAIT_4_RESPONSE,
+	    qla4xxx_isns_build_iocb_handle(ha, ISNS_REQ_RSP_PDU,
+		    isns_pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, isns_pdu_entry);
+		return QLA_ERROR;
+	}
+	ha->isns_transaction_id++;
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_scn_reg_rsp(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	isns_response = (ISNSP_RESPONSE_HEADER *) isns_message->payload;
+	if (isns_response->error_code) {
+		DEBUG5(printk("scsi%ld: %s: iSNS SCNReg failed, error "
+		    "code %x\n", ha->host_no, __func__,
+		    be32_to_cpu(isns_response->error_code)));
+		clear_bit(ISNS_FLAG_ISNS_SCN_REGISTERED, &ha->isns_flags);
+		return QLA_ERROR;
+	}
+	set_bit(ISNS_FLAG_ISNS_SCN_REGISTERED, &ha->isns_flags);
+	ha->isns_num_discovered_targets = 0;
+	if (qla4xxx_isns_dev_get_next(ha, NULL) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: qla4xxx_isns_dev_get_next "
+		    "failed\n", ha->host_no, __func__));
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_attr_qry(scsi_qla_host_t *ha, uint8_t *last_iscsi_name)
+{
+	PDU_ENTRY *pdu_entry;
+	uint32_t packet_size;
+
+	if ((pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE)) == NULL) {
+		DEBUG5(printk("scsi%ld: %s:  qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	if (qla4xxx_isns_build_dev_attr_qry_packet(ha, pdu_entry->Buff,
+	    pdu_entry->BuffLen, last_iscsi_name, &packet_size) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_isns_build_dev_attr_qry_packet failed\n",
+		    ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = pdu_entry->BuffLen;
+	DEBUG5(printk("---------------------------\n"));
+	DEBUG5(printk("scsi%ld: %s:                     sending  %d "
+	    "DevAttrQry\n", ha->host_no, __func__, ha->isns_transaction_id));
+	DEBUG5(printk("PDU (0x%p) 0x%x ->\n", pdu_entry->Buff,
+	    pdu_entry->SendBuffLen));
+	DEBUG5(qla4xxx_dump_bytes(pdu_entry->Buff, pdu_entry->SendBuffLen));
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX,
+	    ISNS_DEFAULT_SERVER_CONN_ID, pdu_entry->DmaBuff,
+	    pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen, PT_FLAG_ISNS_PDU |
+	    PT_FLAG_WAIT_4_RESPONSE, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_REQ_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	ha->isns_transaction_id++;
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_attr_qry_rsp(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size)
+{
+	uint8_t *last_iscsi_name = NULL;
+	ISNS_DISCOVERED_TARGET *discovered_target = NULL;
+	uint32_t isns_error;
+	int i;
+	uint8_t bIsTarget = 1;
+	uint8_t bFound = 0;
+	int status = QLA_SUCCESS;
+
+	if (test_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags)) {
+		clear_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags);
+		ha->isns_num_discovered_targets = 0;
+		if (qla4xxx_isns_dev_get_next(ha, NULL) != QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_get_next failed\n", ha->host_no,
+			    __func__));
+			goto exit_qry_rsp_clear_flags;
+		}
+		goto exit_qry_rsp;
+	}
+	last_iscsi_name = kmalloc(256, GFP_ATOMIC);
+	discovered_target = kmalloc(sizeof(*discovered_target), GFP_ATOMIC);
+	if (!last_iscsi_name || !discovered_target) {
+		DEBUG5(printk("scsi%ld: %s: failed to allocate "
+		    "memory\n", ha->host_no, __func__));
+		status = QLA_ERROR;
+		goto exit_qry_rsp;
+	}
+	memset(last_iscsi_name, 0, 256);
+	memset(discovered_target, 0, sizeof(ISNS_DISCOVERED_TARGET));
+	if (qla4xxx_isns_parse_query_response(ha, buffer, buffer_size,
+	    &isns_error, discovered_target, &bIsTarget, last_iscsi_name) ==
+	    QLA_SUCCESS) {
+		if (bIsTarget && discovered_target->NameString[0] &&
+		    discovered_target->NumPortals) {
+			for (i = 0; i < ha->isns_num_discovered_targets; i++) {
+				if (!strcmp(discovered_target->NameString,
+				    ha->isns_disc_tgt_databasev[i].
+				    NameString)) {
+					DEBUG5(printk("scsi%ld: %s: "
+					    "found at index %x\n", ha->host_no,
+					    __func__, i));
+					memcpy(&ha->isns_disc_tgt_databasev[i],
+					    discovered_target,
+					    sizeof(ISNS_DISCOVERED_TARGET));
+					ha->isns_disc_tgt_databasev[i] =
+					    *discovered_target;
+					bFound = 1;
+					break;
+				}
+			}
+			if (!bFound && i < MAX_ISNS_DISCOVERED_TARGETS) {
+				DEBUG5(printk("scsi%ld: %s: not "
+				    "already present, put in index %x\n",
+				    ha->host_no, __func__, i));
+				memcpy(&ha->isns_disc_tgt_databasev[i],
+				    discovered_target,
+				    sizeof(ISNS_DISCOVERED_TARGET));
+				ha->isns_num_discovered_targets++;
+			}
+		}
+	}
+	if (test_bit(ISNS_FLAG_QUERY_SINGLE_OBJECT, &ha->isns_flags))
+		goto exit_qry_rsp_clear_flags;
+	else if (last_iscsi_name[0] == 0)
+		goto exit_qry_rsp_clear_flags;
+	else {
+		if (qla4xxx_isns_dev_get_next(ha, last_iscsi_name) !=
+		    QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_get_next failed\n", ha->host_no,
+			    __func__));
+			goto exit_qry_rsp_clear_flags;
+		}
+	}
+	goto exit_qry_rsp;
+exit_qry_rsp_clear_flags:
+	ISNS_CLEAR_FLAGS(ha);
+
+exit_qry_rsp:
+	if (last_iscsi_name)
+		kfree(last_iscsi_name);
+	if (discovered_target)
+		kfree(discovered_target);
+	return status;
+}
+
+int
+qla4xxx_isns_dev_get_next(scsi_qla_host_t *ha, uint8_t *last_iscsi_name)
+{
+	PDU_ENTRY *pdu_entry;
+	uint32_t packet_size;
+
+	if ((pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE)) == NULL)
+		return QLA_ERROR;
+
+	if (qla4xxx_isns_build_dev_get_next_packet(ha, pdu_entry->Buff,
+	    pdu_entry->BuffLen, last_iscsi_name, &packet_size) !=
+	    QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_isns_build_dev_get_next_packet failed\n",
+		    ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = pdu_entry->BuffLen;
+	DEBUG5(printk("---------------------------\n"));
+	DEBUG5(printk("scsi%ld: %s:                     sending  %d "
+	    "DevGetNext\n", ha->host_no, __func__, ha->isns_transaction_id));
+	DEBUG5(printk("PDU (0x%p) 0x%x ->\n", pdu_entry->Buff,
+	    pdu_entry->SendBuffLen));
+	DEBUG5(qla4xxx_dump_bytes(pdu_entry->Buff, pdu_entry->SendBuffLen));
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX,
+	    ISNS_DEFAULT_SERVER_CONN_ID, pdu_entry->DmaBuff,
+	    pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen, PT_FLAG_ISNS_PDU |
+	    PT_FLAG_WAIT_4_RESPONSE, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_REQ_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	ha->isns_transaction_id++;
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_get_next_rsp(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size)
+{
+	uint32_t isns_error = 0;
+	uint8_t bIsTarget;
+	static uint8_t last_iscsi_name[256];
+
+	if (test_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags)) {
+		clear_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags);
+		ha->isns_num_discovered_targets = 0;
+		if (qla4xxx_isns_dev_get_next(ha, NULL) != QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_get_next failed\n", ha->host_no,
+			    __func__));
+			goto exit_get_next_rsp;
+		}
+		return QLA_SUCCESS;
+	}
+	if (qla4xxx_isns_parse_get_next_response(ha, buffer, buffer_size,
+	    &isns_error, &last_iscsi_name[0], sizeof(last_iscsi_name) - 1,
+	    &bIsTarget) != QLA_SUCCESS) {
+		if (isns_error != ISNS_ERR_NO_SUCH_ENTRY) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_parse_get_next_response failed\n",
+			    ha->host_no, __func__));
+		}
+		goto exit_get_next_rsp;
+	}
+#if 1
+	if (bIsTarget) {
+		if (qla4xxx_isns_dev_attr_qry(ha, &last_iscsi_name[0]) !=
+		    QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_attr_qry failed\n", ha->host_no,
+			    __func__));
+			goto exit_get_next_rsp;
+		}
+	} else {
+		if (qla4xxx_isns_dev_get_next(ha, &last_iscsi_name[0]) !=
+		    QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_get_next failed\n", ha->host_no,
+			    __func__));
+			goto exit_get_next_rsp;
+		}
+	}
+#else
+	if (qla4xxx_isns_dev_attr_qry(ha, &last_iscsi_name[0]) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s: "
+		    "qla4xxx_isns_dev_attr_qry failed\n", ha->host_no,
+		    __func__));
+		goto exit_get_next_rsp;
+	}
+#endif
+	return QLA_SUCCESS;
+
+exit_get_next_rsp:
+	clear_bit(ISNS_FLAG_SCN_IN_PROGRESS, &ha->isns_flags);
+	clear_bit(ISNS_FLAG_SCN_RESTART, &ha->isns_flags);
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_dereg(scsi_qla_host_t *ha)
+{
+	PDU_ENTRY *pdu_entry;
+	uint32_t packet_size;
+
+	if ((pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE)) == NULL) {
+		DEBUG5(printk("scsi%ld: %s:  qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	if (qla4xxx_isns_build_deregistration_packet(ha, pdu_entry->Buff,
+	    pdu_entry->BuffLen, ha->isns_entity_id, ha->isns_ip_address,
+	    ha->isns_server_port_number, &packet_size) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "QLiSNSBuildDeregistrationPacket failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = pdu_entry->BuffLen;
+	DEBUG5(printk("---------------------------\n"));
+	DEBUG5(printk("scsi%ld: %s:                       sending  "
+	    "%d DevDereg\n", ha->host_no, __func__, ha->isns_transaction_id));
+	DEBUG5(printk("PDU (0x%p) 0x%x ->\n", pdu_entry->Buff,
+	    pdu_entry->SendBuffLen));
+	DEBUG5(qla4xxx_dump_bytes(pdu_entry->Buff, pdu_entry->SendBuffLen));
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX,
+	    ISNS_DEFAULT_SERVER_CONN_ID, pdu_entry->DmaBuff,
+	    pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen, PT_FLAG_ISNS_PDU |
+	    PT_FLAG_WAIT_4_RESPONSE, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_REQ_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	ha->isns_transaction_id++;
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_dev_dereg_rsp(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+	clear_bit(ISNS_FLAG_ISNS_SRV_REGISTERED, &ha->isns_flags);
+	if (be32_to_cpu(isns_response->error_code)) {
+		DEBUG5(printk("scsi%ld: %s: iSNS SCNDereg rsp code "
+		    "%x\n", ha->host_no, __func__,
+		    be32_to_cpu(isns_response->error_code)));
+	}
+	if (test_bit(ISNS_FLAG_REREGISTER, &ha->isns_flags)) {
+		clear_bit(ISNS_FLAG_REREGISTER, &ha->isns_flags);
+		if (qla4xxx_isns_dev_attr_reg(ha) != QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: "
+			    "qla4xxx_isns_dev_attr_reg failed\n", ha->host_no,
+			    __func__));
+			return QLA_ERROR;
+		}
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_scn_dereg(scsi_qla_host_t *ha)
+{
+	PDU_ENTRY *pdu_entry;
+	uint32_t packet_size;
+
+	if ((pdu_entry = qla4xxx_get_pdu(ha, PAGE_SIZE)) == NULL) {
+		DEBUG5(printk("scsi%ld: %s:  qla4xxx_get_pdu failed\n",
+		    ha->host_no, __func__));
+		return QLA_ERROR;
+	}
+	if (qla4xxx_isns_build_scn_deregistration_packet(ha, pdu_entry->Buff,
+	    pdu_entry->BuffLen, &packet_size) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  qla4xxx_isns_build_scn_"
+		    "deregistration_packet failed\n", ha->host_no, __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	pdu_entry->SendBuffLen = packet_size;
+	pdu_entry->RecvBuffLen = pdu_entry->BuffLen;
+	DEBUG5(printk("---------------------------\n"));
+	DEBUG5(printk("scsi%ld: %s:                       sending  "
+	    "%d SCNDereg\n", ha->host_no, __func__, ha->isns_transaction_id));
+	DEBUG5(printk("PDU (0x%p) 0x%x ->\n", pdu_entry->Buff,
+	    pdu_entry->SendBuffLen));
+	DEBUG5(qla4xxx_dump_bytes(pdu_entry->Buff, pdu_entry->SendBuffLen));
+	clear_bit(ISNS_FLAG_DEV_SCAN_DONE, &ha->isns_flags);
+
+	if (qla4xxx_send_passthru0_iocb(ha, ISNS_DEVICE_INDEX,
+	    ISNS_DEFAULT_SERVER_CONN_ID, pdu_entry->DmaBuff,
+	    pdu_entry->SendBuffLen, pdu_entry->RecvBuffLen, PT_FLAG_ISNS_PDU |
+	    PT_FLAG_WAIT_4_RESPONSE, qla4xxx_isns_build_iocb_handle(ha,
+		    ISNS_REQ_RSP_PDU, pdu_entry)) != QLA_SUCCESS) {
+		DEBUG5(printk("scsi%ld: %s:  "
+		    "qla4xxx_send_passthru0_iocb failed\n", ha->host_no,
+		    __func__));
+		qla4xxx_free_pdu(ha, pdu_entry);
+		return QLA_ERROR;
+	}
+	ha->isns_transaction_id++;
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_scn_dereg_rsp(scsi_qla_host_t *ha, uint8_t *buffer,
+    uint32_t buffer_size)
+{
+	ISNSP_MESSAGE_HEADER *isns_message;
+	ISNSP_RESPONSE_HEADER *isns_response;
+
+	isns_message = (ISNSP_MESSAGE_HEADER *) buffer;
+	isns_response = (ISNSP_RESPONSE_HEADER *) & isns_message->payload[0];
+	clear_bit(ISNS_FLAG_ISNS_SCN_REGISTERED, &ha->isns_flags);
+	if (be32_to_cpu(isns_response->error_code)) {
+		DEBUG5(printk("scsi%ld: %s: iSNS SCNDereg rsp code "
+		    "%x\n", ha->host_no, __func__,
+		    be32_to_cpu(isns_response->error_code)));
+	}
+	if (test_bit(ISNS_FLAG_REREGISTER, &ha->isns_flags)) {
+		if (qla4xxx_isns_dev_dereg(ha) != QLA_SUCCESS) {
+			DEBUG5(printk("scsi%ld: %s: QLiSNSDevDereg "
+			    "failed\n", ha->host_no, __func__));
+			return QLA_ERROR;
+		}
+	}
+	return QLA_SUCCESS;
+}
drivers/scsi/qla4xxx/ql4_isns.h new file +401 -0

401 lines not on this page · raw message

Signature · 1 line
Andrew Vasquez
#5 lore raw

PATCH 5/8 qla4xxx: ISR routines

In reply to Andrew Vasquez

ISP4xxx driver ISR routines.

---

 drivers/scsi/qla4xxx/ql4_isr.c |  905 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 905 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/ql4_isr.c

e822048467375795703d23e45efe7c94c4a01bd9

drivers/scsi/qla4xxx/ql4_isr.c new file +905 -0
new file mode 100644
@@ -0,0 +1,905 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:
+ *
+ * 	1. Redistribution of source code must retain the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission.
+ *
+ * You may redistribute the hardware specific firmware binary file
+ * under the following terms:
+ *
+ * 	1. Redistribution of source code (only if applicable),
+ * 	   must retain the above copyright notice, this list of
+ * 	   conditions and the following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission
+ *
+ * REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+ * THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+ * CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+ * OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+ * TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ * ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+ * COMBINATION WITH THIS PROGRAM.
+ */
+
+#include "ql4_def.h"
+
+/**
+ * qla2x00_process_completed_request() - Process a Fast Post response.
+ * @ha: SCSI driver HA context
+ * @index: SRB index
+ */
+static void
+qla4xxx_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
+{
+	srb_t *srb;
+
+	srb = del_from_active_array(ha, index);
+	if (srb) {
+		/* Save ISP completion status */
+		srb->cmd->result = DID_OK << 16;
+		qla4xxx_srb_compl(ha, srb);
+	} else {
+		DEBUG2(printk("scsi%ld: Invalid ISP SCSI completion handle = "
+		    "%d\n", ha->host_no, index));
+		set_bit(DPC_RESET_HA, &ha->dpc_flags);
+	}
+}
+
+/**************************************************************************
+ * qla4xxx_status_entry
+ *	This routine processes Status IOCBs
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      sts_entry - Pointer to status entry structure.
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Interrupt context.
+ **************************************************************************/
+static void
+qla4xxx_status_entry(scsi_qla_host_t *ha, STATUS_ENTRY *sts_entry)
+{
+	uint8_t scsi_status;
+	struct scsi_cmnd *cmd;
+	srb_t *srb;
+	ddb_entry_t *ddb_entry;
+	uint32_t residual;
+	uint16_t sensebytecnt;
+
+	if (sts_entry->completionStatus == SCS_COMPLETE &&
+	    sts_entry->scsiStatus == 0) {
+		qla4xxx_process_completed_request(ha,
+		    le32_to_cpu(sts_entry->handle));
+		return;
+	}
+
+	srb = del_from_active_array(ha, le32_to_cpu(sts_entry->handle));
+	if (!srb) {
+		/* FIXMEdg: Don't we need to reset ISP in this case??? */
+		DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Status Entry invalid "
+		    "handle 0x%x, sp=%p. This cmd may have already been "
+		    "completed.\n", ha->host_no, __func__,
+		    le32_to_cpu(sts_entry->handle), srb));
+	}
+
+	cmd = srb->cmd;
+	if (cmd == NULL) {
+		DEBUG2(printk("scsi%ld: %s: Command already returned back to "
+		    "OS pkt->handle=%d srb=%p srb->state:%d\n", ha->host_no,
+		    __func__, sts_entry->handle, srb, srb->state));
+		ql4_printk(KERN_WARNING, ha,
+		    "Command is NULL: already returned to OS (srb=%p)\n", srb);
+		return;
+	}
+
+	ddb_entry = srb->ddb;
+	if (ddb_entry == NULL) {
+		cmd->result = DID_NO_CONNECT << 16;
+		goto status_entry_exit;
+	}
+
+	residual = le32_to_cpu(sts_entry->residualByteCnt);
+
+	/* Translate ISP error to a Linux SCSI error. */
+	scsi_status = sts_entry->scsiStatus;
+	switch (sts_entry->completionStatus) {
+	case SCS_COMPLETE:
+		if (scsi_status == 0) {
+			cmd->result = DID_OK << 16;
+			break;
+		}
+
+		if (sts_entry->iscsiFlags &
+		    (ISCSI_FLAG_RESIDUAL_OVER|ISCSI_FLAG_RESIDUAL_UNDER))
+			cmd->resid = residual;
+
+		cmd->result = DID_OK << 16 | scsi_status;
+
+		if (scsi_status != SCSI_CHECK_CONDITION)
+			break;
+
+		/* Copy Sense Data into sense buffer. */
+		memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+		sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt);
+		if (sensebytecnt == 0)
+			break;
+
+		memcpy(cmd->sense_buffer, sts_entry->senseData,
+		    min(sensebytecnt, (uint16_t) sizeof(cmd->sense_buffer)));
+
+		DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
+		    "ASC/ASCQ = %02x/%02x\n", ha->host_no,
+		    cmd->device->channel, cmd->device->id, cmd->device->lun,
+		    __func__, sts_entry->senseData[2] & 0x0f,
+		    sts_entry->senseData[12], sts_entry->senseData[13]));
+
+		srb->flags |= SRB_GOT_SENSE;
+		break;
+
+	case SCS_INCOMPLETE:
+		/* Always set the status to DID_ERROR, since
+		 * all conditions result in that status anyway */
+		cmd->result = DID_ERROR << 16;
+		break;
+
+	case SCS_RESET_OCCURRED:
+		DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Device RESET occurred\n",
+		    ha->host_no, cmd->device->channel, cmd->device->id,
+		    cmd->device->lun, __func__));
+
+		cmd->result = DID_RESET << 16;
+		break;
+
+	case SCS_ABORTED:
+		DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Abort occurred\n",
+		    ha->host_no, cmd->device->channel, cmd->device->id,
+		    cmd->device->lun, __func__));
+
+		cmd->result = DID_RESET << 16;
+		break;
+
+	case SCS_TIMEOUT:
+		DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: Timeout\n",
+		    ha->host_no, cmd->device->channel, cmd->device->id,
+		    cmd->device->lun));
+
+		cmd->result = DID_BUS_BUSY << 16;
+
+		/*
+		 * Mark device missing so that we won't continue to send
+		 * I/O to this device.  We should get a ddb state change
+		 * AEN soon.
+		 */
+		if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
+			qla4xxx_mark_device_missing(ha, ddb_entry);
+		break;
+
+	case SCS_DATA_UNDERRUN:
+	case SCS_DATA_OVERRUN:
+		if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) {
+			DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, "
+			    "residual = 0x%x\n", ha->host_no,
+			    cmd->device->channel, cmd->device->id,
+			    cmd->device->lun, __func__, residual));
+
+			cmd->result = DID_ERROR << 16;
+			break;
+		}
+
+		if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) == 0) {
+			cmd->resid = residual;
+			DEBUG2(printk("scsi%ld:%d:%d:%d: %s: UNDERRUN status "
+			    "detected, xferlen = 0x%x, residual = 0x%x\n",
+			    ha->host_no, cmd->device->channel, cmd->device->id,
+			    cmd->device->lun, __func__, cmd->request_bufflen,
+			    residual));
+		}
+
+		/*
+		 * If there is scsi_status, it takes precedense over
+		 * underflow condition.
+		 */
+		if (scsi_status != 0) {
+			cmd->result = DID_OK << 16 | scsi_status;
+
+			if (scsi_status != SCSI_CHECK_CONDITION)
+				break;
+
+			/* Copy Sense Data into sense buffer. */
+			memset(cmd->sense_buffer, 0,
+			    sizeof(cmd->sense_buffer));
+
+			sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt);
+			if (sensebytecnt == 0)
+				break;
+
+			memcpy(cmd->sense_buffer, sts_entry->senseData,
+			    min(sensebytecnt,
+				    (uint16_t) sizeof(cmd->sense_buffer)));
+
+			DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
+			    "ASC/ASCQ = %02x/%02x\n", ha->host_no,
+			    cmd->device->channel, cmd->device->id,
+			    cmd->device->lun, __func__,
+			    sts_entry->senseData[2] & 0x0f,
+			    sts_entry->senseData[12],
+			    sts_entry->senseData[13]));
+		} else {
+			/*
+			 * If RISC reports underrun and target does not
+			 * report it then we must have a lost frame, so
+			 * tell upper layer to retry it by reporting a
+			 * bus busy.
+			 */
+			if ((sts_entry->iscsiFlags &
+			    ISCSI_FLAG_RESIDUAL_UNDER) == 0) {
+				cmd->result = DID_BUS_BUSY << 16;
+			} else if ((cmd->request_bufflen - residual) <
+				   cmd->underflow) {
+				/*
+				 * Handle mid-layer underflow???
+				 *
+				 * For kernels less than 2.4, the driver must
+				 * return an error if an underflow is detected.
+				 * For kernels equal-to and above 2.4, the
+				 * mid-layer will appearantly handle the
+				 * underflow by detecting the residual count --
+				 * unfortunately, we do not see where this is
+				 * actually being done.  In the interim, we
+				 * will return DID_ERROR.
+				 */
+				DEBUG2(printk("scsi%ld:%d:%d:%d: %s: "
+				    "Mid-layer Data underrun, xferlen = 0x%x, "
+				    "residual = 0x%x\n", ha->host_no,
+				    cmd->device->channel, cmd->device->id,
+				    cmd->device->lun, __func__,
+				    cmd->request_bufflen, residual));
+
+				cmd->result = DID_ERROR << 16;
+			} else {
+				cmd->result = DID_OK << 16;
+			}
+		}
+		break;
+
+	case SCS_DEVICE_LOGGED_OUT:
+	case SCS_DEVICE_UNAVAILABLE:
+		/*
+		 * Mark device missing so that we won't continue to
+		 * send I/O to this device.  We should get a ddb
+		 * state change AEN soon.
+		 */
+		if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
+			qla4xxx_mark_device_missing(ha, ddb_entry);
+
+		if ((srb->flags & SRB_TAPE) || atomic_read(&ddb_entry->state) ==
+		    DDB_STATE_MISSING)
+			cmd->result = DID_NO_CONNECT << 16;
+		else
+			cmd->result = DID_ERROR << 16;
+		break;
+
+	case SCS_QUEUE_FULL:
+		/*
+		 * SCSI Mid-Layer handles device queue full
+		 */
+		cmd->result = DID_OK << 16 | sts_entry->scsiStatus;
+		DEBUG2(printk("scsi%ld:%d:%d: %s: QUEUE FULL detected "
+		    "compl=%02x, scsi=%02x, state=%02x, iFlags=%02x, "
+		    "iResp=%02x\n", ha->host_no, cmd->device->id,
+		    cmd->device->lun, __func__, sts_entry->completionStatus,
+		    sts_entry->scsiStatus, sts_entry->state_flags,
+		    sts_entry->iscsiFlags, sts_entry->iscsiResponse));
+		break;
+
+	default:
+		cmd->result = DID_ERROR << 16;
+		break;
+	}
+
+status_entry_exit:
+
+	/* complete the request */
+	srb->cc_stat = sts_entry->completionStatus;
+	qla4xxx_srb_compl(ha, srb);
+}
+
+/**************************************************************************
+ * qla4xxx_process_response_queue
+ *	This routine handles the Response Queue Completion.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Output:
+ *	None
+ *
+ * Remarks:
+ *	hardware_lock locked upon entry
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully processed response queue
+ *	QLA_ERROR   - Failed to process response queue
+ *
+ * Context:
+ *	Interrupt context.
+ **************************************************************************/
+static void
+qla4xxx_process_response_queue(scsi_qla_host_t * ha)
+{
+	uint32_t count = 0;
+	srb_t *srb = 0;
+	STATUS_ENTRY *sts_entry;
+
+	/* Process all responses from response queue */
+	while ((ha->response_in =
+	    (uint16_t)le32_to_cpu(ha->shadow_regs->rsp_q_in)) !=
+		ha->response_out) {
+		sts_entry = (STATUS_ENTRY *) ha->response_ptr;
+		count++;
+
+		/* Advance pointers for next entry */
+		if (ha->response_out == (RESPONSE_QUEUE_DEPTH - 1)) {
+			ha->response_out = 0;
+			ha->response_ptr = ha->response_ring;
+		} else {
+			ha->response_out++;
+			ha->response_ptr++;
+		}
+
+		/* process entry */
+		switch (sts_entry->hdr.entryType) {
+		case ET_STATUS:
+			/*
+			 * Common status - Single completion posted in single
+			 * IOSB.
+			 */
+			qla4xxx_status_entry(ha, sts_entry);
+			break;
+
+		case ET_PASSTHRU_STATUS:
+			qla4xxx_isns_process_response(ha,
+			    (PASSTHRU_STATUS_ENTRY *)sts_entry);
+			break;
+
+/* FIXMEdg: Cut and paste from fibre code */
+		case ET_STATUS_CONTINUATION:
+			/* Just throw away the status continuation entries */
+			DEBUG2(printk("scsi%ld: %s: Status Continuation entry "
+			    "- ignoring\n", ha->host_no, __func__));
+			break;
+
+		case ET_COMMAND:
+			/* ISP device queue is full. Command not accepted by
+			 * ISP.  Queue command for later */
+
+			srb = del_from_active_array(ha,
+			    le32_to_cpu(sts_entry->handle));
+			if (srb == NULL)
+				goto exit_prq_invalid_handle;
+
+			DEBUG2(printk("scsi%ld: %s: FW device queue full, "
+			    "srb %p\n", ha->host_no, __func__, srb));
+
+			/* ETRY normally by sending it back with DID_BUS_BUSY */
+			srb->cmd->result = DID_BUS_BUSY << 16;
+			qla4xxx_srb_compl(ha, srb);
+			break;
+
+		case ET_CONTINUE:
+			/* Just throw away the continuation entries */
+			DEBUG2(printk("scsi%ld: %s: Continuation entry - "
+			    "ignoring\n", ha->host_no, __func__));
+			break;
+
+		default:
+			/*
+			 * Invalid entry in response queue, reset RISC
+			 * firmware.
+			 */
+			DEBUG2(printk("scsi%ld: %s: Invalid entry %x in "
+			    "response queue \n", ha->host_no, __func__,
+			    sts_entry->hdr.entryType));
+			goto exit_prq_error;
+		}
+	}
+
+	/*
+	 * Done with responses, update the ISP For QLA4010, this also clears
+	 * the interrupt.
+	 */
+	WRT_REG_DWORD(&ha->reg->rsp_q_out, ha->response_out);
+	PCI_POSTING(&ha->reg->rsp_q_out);
+
+	return;
+
+exit_prq_invalid_handle:
+	DEBUG2(printk("scsi%ld: %s: Invalid handle(srb)=%p type=%x IOCS=%x\n",
+	    ha->host_no, __func__, srb, sts_entry->hdr.entryType,
+	    sts_entry->completionStatus));
+
+exit_prq_error:
+	WRT_REG_DWORD(&ha->reg->rsp_q_out, ha->response_out);
+	PCI_POSTING(&ha->reg->rsp_q_out);
+
+	set_bit(DPC_RESET_HA, &ha->dpc_flags);
+}
+
+/**************************************************************************
+ * qla4xxx_isr_decode_mailbox
+ *	This routine decodes the mailbox status during the ISR.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	mailbox_status - Mailbox status.
+ *
+ * Remarks:
+ *      hardware_lock locked upon entry
+ *
+ * Returns:
+ *	None.
+ *
+ * Context:
+ *	Interrupt context.
+ **************************************************************************/
+static void
+qla4xxx_isr_decode_mailbox(scsi_qla_host_t * ha, uint32_t mbox_status)
+{
+	/* used for MBOX_ASTS_ISNS_UNSOLICITED_PDU_RECEIVED */
+	int i;
+	static uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	if ((mbox_status == MBOX_STS_BUSY) ||
+	    (mbox_status == MBOX_STS_INTERMEDIATE_COMPLETION) ||
+	    (mbox_status >> 12 == MBOX_COMPLETION_STATUS)) {
+		ha->mbox_status[0] = mbox_status;
+
+		if (test_bit(AF_MBOX_COMMAND, &ha->flags)) {
+			/*
+			 * Copy all mailbox registers to a temporary
+			 * location and set mailbox command done flag
+			 */
+			for (i = 1; i < ha->mbox_status_count; i++)
+				ha->mbox_status[i] =
+				    RD_REG_DWORD(&ha->reg->mailbox[i]);
+
+			set_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
+			wake_up(&ha->mailbox_wait_queue);
+		}
+	} else if (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) {
+		/* Immediately process the AENs that don't require much work.
+		 * Only queue the database_changed AENs */
+		switch (mbox_status) {
+		case MBOX_ASTS_SYSTEM_ERROR:
+			/* Log Mailbox registers */
+			if (ql4xdontresethba) {
+				DEBUG2(printk("%s:Dont Reset HBA\n", __func__));
+			} else {
+				set_bit(AF_GET_CRASH_RECORD, &ha->flags);
+				set_bit(DPC_RESET_HA, &ha->dpc_flags);
+			}
+			break;
+
+		case MBOX_ASTS_REQUEST_TRANSFER_ERROR:
+		case MBOX_ASTS_RESPONSE_TRANSFER_ERROR:
+		case MBOX_ASTS_NVRAM_INVALID:
+		case MBOX_ASTS_IP_ADDRESS_CHANGED:
+		case MBOX_ASTS_DHCP_LEASE_EXPIRED:
+			DEBUG2(printk("scsi%ld: AEN %04x, ERROR Status, "
+			    "Reset HA\n", ha->host_no, mbox_status));
+			set_bit(DPC_RESET_HA, &ha->dpc_flags);
+			break;
+
+		case MBOX_ASTS_LINK_UP:
+			DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n",
+			    ha->host_no, mbox_status));
+			set_bit(AF_LINK_UP, &ha->flags);
+			break;
+
+		case MBOX_ASTS_LINK_DOWN:
+			DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n",
+			    ha->host_no, mbox_status));
+			clear_bit(AF_LINK_UP, &ha->flags);
+			break;
+
+		case MBOX_ASTS_HEARTBEAT:
+			ha->seconds_since_last_heartbeat = 0;
+			break;
+
+		case MBOX_ASTS_DHCP_LEASE_ACQUIRED:
+			DEBUG2(printk("scsi%ld: AEN %04x DHCP LEASE "
+			    "ACQUIRED\n", ha->host_no, mbox_status));
+			set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags);
+			break;
+
+		case MBOX_ASTS_PROTOCOL_STATISTIC_ALARM:
+		case MBOX_ASTS_SCSI_COMMAND_PDU_REJECTED: /* Target mode only */
+		case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED:  /* Connection mode */
+		case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR:
+		case MBOX_ASTS_SUBNET_STATE_CHANGE:
+			/* No action */
+			DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no,
+			    mbox_status));
+			break;
+
+		case MBOX_ASTS_MAC_ADDRESS_CHANGED:
+		case MBOX_ASTS_DNS:
+			/* No action */
+			DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x, "
+			    "mbox_sts[1]=%04x, mbox_sts[2]=%04x\n",
+			    ha->host_no, mbox_status,
+			    RD_REG_DWORD(&ha->reg->mailbox[1]),
+			    RD_REG_DWORD(&ha->reg->mailbox[2])));
+			break;
+
+		case MBOX_ASTS_SELF_TEST_FAILED:
+		case MBOX_ASTS_LOGIN_FAILED:
+			/* No action */
+			DEBUG2(printk("scsi%ld: AEN %04x, mbox_sts[1]=%04x, "
+			    "mbox_sts[2]=%04x, mbox_sts[3]=%04x\n",
+			    ha->host_no, mbox_status,
+			    RD_REG_DWORD(&ha->reg->mailbox[1]),
+			    RD_REG_DWORD(&ha->reg->mailbox[2]),
+			    RD_REG_DWORD(&ha->reg->mailbox[3])));
+			break;
+
+		case MBOX_ASTS_DATABASE_CHANGED:
+			/* Queue AEN information and process it in the DPC
+			 * routine */
+			if (ha->aen_q_count > 0) {
+				/* advance pointer */
+				if (ha->aen_in == (MAX_AEN_ENTRIES - 1))
+					ha->aen_in = 0;
+				else
+					ha->aen_in++;
+
+				/* decrement available counter */
+				ha->aen_q_count--;
+
+				for (i = 1; i < MBOX_AEN_REG_COUNT; i++)
+					ha->aen_q[ha->aen_in].mbox_sts[i] =
+					    RD_REG_DWORD(&ha->reg->mailbox[i]);
+
+				ha->aen_q[ha->aen_in].mbox_sts[0] = mbox_status;
+
+				/* print debug message */
+				DEBUG2(printk("scsi%ld: AEN[%d] %04x queued"
+				    " mb1:0x%x mb2:0x%x mb3:0x%x mb4:0x%x\n",
+				    ha->host_no, ha->aen_in, mbox_status,
+				    ha->aen_q[ha->aen_in].mbox_sts[1],
+				    ha->aen_q[ha->aen_in].mbox_sts[2],
+				    ha->aen_q[ha->aen_in].mbox_sts[3],
+				    ha->aen_q[ha->aen_in].  mbox_sts[4]));
+
+				    /* The DPC routine will process the aen */
+				    set_bit(DPC_AEN, &ha->dpc_flags);
+			} else {
+				DEBUG2(printk("scsi%ld: %s: aen %04x, queue "
+				    "overflowed!  AEN LOST!!\n", ha->host_no,
+				    __func__, mbox_status));
+
+				DEBUG2(printk("scsi%ld: DUMP AEN QUEUE\n",
+				    ha->host_no));
+
+				for (i = 0; i < MAX_AEN_ENTRIES; i++) {
+					DEBUG2(printk("AEN[%d] %04x %04x %04x "
+					    "%04x\n", i,
+					    ha->aen_q[i].mbox_sts[0],
+					    ha->aen_q[i].mbox_sts[1],
+					    ha->aen_q[i].mbox_sts[2],
+					    ha->aen_q[i].mbox_sts[3]));
+				}
+			}
+			break;
+
+		case MBOX_ASTS_ISNS_UNSOLICITED_PDU_RECEIVED:
+			memset(&mbox_sts, 0, sizeof(mbox_sts));
+			mbox_sts[0] = mbox_status;
+			mbox_sts[1] = RD_REG_DWORD(&ha->reg->mailbox[1]);
+			mbox_sts[2] = RD_REG_DWORD(&ha->reg->mailbox[2]);
+			mbox_sts[3] = RD_REG_DWORD(&ha->reg->mailbox[3]);
+			mbox_sts[4] = RD_REG_DWORD(&ha->reg->mailbox[4]);
+			mbox_sts[5] = RD_REG_DWORD(&ha->reg->mailbox[5]);
+
+			if (mbox_sts[1] == ISNS_EVENT_DATA_RECEIVED) {
+				if (qla4xxx_isns_get_server_request(ha,
+				    mbox_sts[3], mbox_sts[2]) != QLA_SUCCESS) {
+					/* Nothing? */
+				}
+			} else if (mbox_sts[1] ==
+			    ISNS_EVENT_CONNECTION_OPENED) {
+				qla4xxx_isns_enable_callback(ha, mbox_sts[2],
+				    mbox_sts[3], mbox_sts[4], mbox_sts[5]);
+			}
+			break;
+
+		default:
+			DEBUG2(printk(KERN_WARNING
+			    "scsi%ld: AEN %04x UNKNOWN\n", ha->host_no,
+			    mbox_status));
+			break;
+		}
+	} else {
+		DEBUG2(printk("scsi%ld: Unknown mailbox status %08X\n",
+		    ha->host_no, mbox_status));
+
+		ha->mbox_status[0] = mbox_status;
+	}
+}
+
+/**************************************************************************
+ * qla4xxx_interrupt_service_routine
+ *	This routine services the interrupt
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Remarks:
+ *      hardware_lock locked upon entry
+ *
+ * Returns:
+ *      QLA_SUCCESS - success, An interrupt was found and processed
+ *	QLA_ERROR - failure, The adapter was not interrupting
+ *
+ * Context:
+ *	Interrupt context.
+ **************************************************************************/
+void
+qla4xxx_interrupt_service_routine(scsi_qla_host_t * ha, uint32_t intr_status)
+{
+	/* Process response queue interrupt. */
+	if (intr_status & CSR_SCSI_COMPLETION_INTR)
+		qla4xxx_process_response_queue(ha);
+
+	/* Process mailbox/asynch event  interrupt.*/
+	if (intr_status & CSR_SCSI_PROCESSOR_INTR) {
+		qla4xxx_isr_decode_mailbox(ha,
+		    RD_REG_DWORD(&ha->reg->mailbox[0]));
+
+		/* Clear Mailbox Interrupt */
+		WRT_REG_DWORD(&ha->reg->ctrl_status,
+		    SET_RMASK(CSR_SCSI_PROCESSOR_INTR));
+		PCI_POSTING(&ha->reg->ctrl_status);
+	}
+}
+
+/**************************************************************************
+ * qla4xxx_intr_handler
+ *	This routine handles the H/W interrupt
+ *
+ * Input:
+ *	irq - Unused
+ *	dev_id - Pointer to host adapter structure
+ *	regs - Unused
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Interrupt context.
+ **************************************************************************/
+irqreturn_t
+qla4xxx_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+	scsi_qla_host_t *ha;
+	uint32_t intr_status;
+	unsigned long flags = 0;
+	uint8_t reqs_count = 0;
+
+	ha = (scsi_qla_host_t *) dev_id;
+	if (!ha) {
+		DEBUG2(printk(KERN_INFO
+		    "qla4xxx: Interrupt with NULL host ptr\n"));
+		return IRQ_NONE;
+	}
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	/*
+	 * Repeatedly service interrupts up to a maximum of
+	 * MAX_REQS_SERVICED_PER_INTR
+	 */
+	while (1) {
+		/*
+		 * Read interrupt status
+		 */
+		if (le32_to_cpu(ha->shadow_regs->rsp_q_in) !=
+		    ha->response_out)
+			intr_status = CSR_SCSI_COMPLETION_INTR;
+		else
+			intr_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+
+		if ((intr_status &
+		    (CSR_SCSI_RESET_INTR|CSR_FATAL_ERROR|INTR_PENDING)) == 0) {
+			if (reqs_count == 0)
+				ha->spurious_int_count++;
+			break;
+		}
+
+		if (intr_status & CSR_FATAL_ERROR) {
+			DEBUG2(printk(KERN_INFO "scsi%ld: Fatal Error, "
+			    "Status 0x%04x\n", ha->host_no,
+			    RD_REG_DWORD(ISP_PORT_ERROR_STATUS (ha))));
+
+			/* Issue Soft Reset to clear this error condition.
+			 * This will prevent the RISC from repeatedly
+			 * interrupting the driver; thus, allowing the DPC to
+			 * get scheduled to continue error recovery.
+			 * NOTE: Disabling RISC interrupts does not work in
+			 * this case, as CSR_FATAL_ERROR overrides
+			 * CSR_SCSI_INTR_ENABLE */
+			if ((RD_REG_DWORD(&ha->reg->ctrl_status) &
+			    CSR_SCSI_RESET_INTR) == 0) {
+				WRT_REG_DWORD(&ha->reg->ctrl_status,
+				    SET_RMASK(CSR_SOFT_RESET));
+				PCI_POSTING(&ha->reg->ctrl_status);
+			}
+
+			WRT_REG_DWORD(&ha->reg->ctrl_status,
+			    SET_RMASK(CSR_FATAL_ERROR));
+			PCI_POSTING(&ha->reg->ctrl_status);
+
+			__qla4xxx_disable_intrs(ha);
+
+			set_bit(DPC_RESET_HA, &ha->dpc_flags);
+
+			break;
+		} else if (intr_status & CSR_SCSI_RESET_INTR) {
+			clear_bit(AF_ONLINE, &ha->flags);
+			__qla4xxx_disable_intrs(ha);
+
+			WRT_REG_DWORD(&ha->reg->ctrl_status,
+			    SET_RMASK(CSR_SCSI_RESET_INTR));
+			PCI_POSTING(&ha->reg->ctrl_status);
+
+			set_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
+
+			break;
+		} else if (intr_status & INTR_PENDING) {
+			qla4xxx_interrupt_service_routine(ha, intr_status);
+			ha->total_io_count++;
+			if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) {
+				break;
+			}
+			intr_status = 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/**************************************************************************
+ * qla4xxx_process_aen
+ *	This routine processes Asynchronous Events received from the firmware.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	process_aen -
+ *      	PROCESS_ALL_AENS	 0
+ *      	FLUSH_DDB_CHANGED_AENS	 1
+ *      	RELOGIN_DDB_CHANGED_AENS 2
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+void
+qla4xxx_process_aen(scsi_qla_host_t * ha, uint8_t process_aen)
+{
+	uint32_t mbox_sts[MBOX_AEN_REG_COUNT];
+	aen_t *aen;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	while (ha->aen_out != ha->aen_in) {
+		/* Advance pointers for next entry */
+		if (ha->aen_out == (MAX_AEN_ENTRIES - 1))
+			ha->aen_out = 0;
+		else
+			ha->aen_out++;
+
+		ha->aen_q_count++;
+		aen = &ha->aen_q[ha->aen_out];
+
+		/* copy aen information to local structure */
+		for (i = 0; i < MBOX_AEN_REG_COUNT; i++)
+			mbox_sts[i] = aen->mbox_sts[i];
+
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+		DEBUG(printk("scsi%ld: AEN[%d] %04x, index [%d] state=%04x "
+		    "mod=%x conerr=%08x \n", ha->host_no, ha->aen_out,
+		    mbox_sts[0], mbox_sts[2], mbox_sts[3], mbox_sts[1],
+		    mbox_sts[4]));
+
+		switch (mbox_sts[0]) {
+		case MBOX_ASTS_DATABASE_CHANGED:
+			if (process_aen == FLUSH_DDB_CHANGED_AENS) {
+				DEBUG2(printk("scsi%ld: AEN[%d] %04x, index "
+				    "[%d] state=%04x IGNORED!\n", ha->host_no,
+				    ha->aen_out, mbox_sts[0], mbox_sts[2],
+				    mbox_sts[3]));
+				break;
+			} else if (process_aen == RELOGIN_DDB_CHANGED_AENS) {
+				/* for use during init time, we only want to
+				 * relogin non-active ddbs */
+				ddb_entry_t *ddb_entry;
+
+				ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha,
+				    mbox_sts[2]);
+				if (!ddb_entry)
+					break;
+
+				ddb_entry->dev_scan_wait_to_complete_relogin =0;
+				ddb_entry->dev_scan_wait_to_start_relogin =
+				    jiffies + ((ddb_entry->default_time2wait +
+					4) * HZ);
+
+				DEBUG2(printk("scsi%ld: ddb index [%d] initate "
+				    "RELOGIN after %d seconds\n", ha->host_no,
+				    ddb_entry->fw_ddb_index,
+				    ddb_entry->default_time2wait + 4));
+				break;
+			}
+
+			if (mbox_sts[1] == 0) {	/* Global DB change. */
+				qla4xxx_reinitialize_ddb_list(ha);
+			} else if (mbox_sts[1] == 1) {	/* Specific device. */
+				qla4xxx_process_ddb_changed(ha, mbox_sts[2],
+				    mbox_sts[3]);
+			}
+			break;
+		}
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+}
Signature · 1 line
Andrew Vasquez
#6 lore raw

PATCH 6/8 qla4xxx: Mailbox routines

In reply to Andrew Vasquez

ISP4xxx driver Mailbox routines.

---

 drivers/scsi/qla4xxx/ql4_mbx.c | 1132 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1132 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/ql4_mbx.c

f5b56bf482abb9d96f2d542fbef6957f2f52d545

drivers/scsi/qla4xxx/ql4_mbx.c new file +1132 -0
new file mode 100644
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:
+ *
+ * 	1. Redistribution of source code must retain the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission.
+ *
+ * You may redistribute the hardware specific firmware binary file
+ * under the following terms:
+ *
+ * 	1. Redistribution of source code (only if applicable),
+ * 	   must retain the above copyright notice, this list of
+ * 	   conditions and the following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission
+ *
+ * REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+ * THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+ * CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+ * OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+ * TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ * ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+ * COMBINATION WITH THIS PROGRAM.
+ */
+
+#include "ql4_def.h"
+
+#include <linux/delay.h>
+
+/*
+ * externals
+ */
+extern int ql4xportdownretrycount;
+extern int ql4xdiscoverywait;
+extern void qla4xxx_isns_build_entity_id(scsi_qla_host_t *);
+extern int qla4xxx_eh_wait_for_active_target_commands(scsi_qla_host_t *, int,
+    int lun);
+
+/**************************************************************************
+ * qla4xxx_mailbox_command
+ *	This routine sssue mailbox commands and waits for completion.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	inCount	 - number of mailbox registers to load.
+ *      outCount - number of mailbox registers to return.
+ *      mbx_cmd  - data pointer for mailbox in registers.
+ *      mbx_sts  - data pointer for mailbox out registers.
+ *
+ * Output:
+ *      mbx_sts - returned mailbox out data.
+ *
+ * Remarks:
+ *	If outCount is 0, this routine completes successfully WITHOUT waiting
+ *	for the mailbox command to complete.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Mailbox command completed successfully
+ *	QLA_ERROR   - Mailbox command competed in error.
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_mailbox_command(scsi_qla_host_t * ha, uint8_t inCount,
+    uint8_t outCount, uint32_t * mbx_cmd, uint32_t * mbx_sts)
+{
+	int status = QLA_ERROR;
+	uint8_t i;
+	u_long wait_count;
+	uint32_t intr_status;
+	unsigned long flags = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	down(&ha->mbox_sem);
+
+	/* Mailbox code active */
+	set_bit(AF_MBOX_COMMAND, &ha->flags);
+
+	/* Make sure that pointers are valid */
+	if (!mbx_cmd || !mbx_sts) {
+		DEBUG2(printk("scsi%ld: %s: Invalid mbx_cmd or mbx_sts "
+		    "pointer\n", ha->host_no, __func__));
+		goto mbox_exit;
+	}
+
+	/* To prevent overwriting mailbox registers for a command that has
+	 * not yet been serviced, check to see if a previously issued
+	 * mailbox command is interrupting.
+	 * -----------------------------------------------------------------
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	intr_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+	if (intr_status & CSR_SCSI_PROCESSOR_INTR) {
+		/* Service existing interrupt */
+		qla4xxx_interrupt_service_routine(ha, intr_status);
+		clear_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
+	}
+
+	/* Send the mailbox command to the firmware */
+	ha->mbox_status_count = outCount;
+	for (i = 0; i < outCount; i++)
+		ha->mbox_status[i] = 0;
+
+	/* Load all mailbox registers, except mailbox 0. */
+	for (i = 1; i < inCount; i++)
+		WRT_REG_DWORD(&ha->reg->mailbox[i], mbx_cmd[i]);
+
+	/* Wakeup firmware  */
+	WRT_REG_DWORD(&ha->reg->mailbox[0], mbx_cmd[0]);
+	PCI_POSTING(&ha->reg->mailbox[0]);
+	WRT_REG_DWORD(&ha->reg->ctrl_status, SET_RMASK(CSR_INTR_RISC));
+	PCI_POSTING(&ha->reg->ctrl_status);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	/* Wait for completion */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	add_wait_queue(&ha->mailbox_wait_queue, &wait);
+
+	/*
+	 * If we don't want status, don't wait for the mailbox command to
+	 * complete.  For example, MBOX_CMD_RESET_FW doesn't return status,
+	 * you must poll the inbound Interrupt Mask for completion.
+	 */
+	if (outCount == 0) {
+		status = QLA_SUCCESS;
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&ha->mailbox_wait_queue, &wait);
+		goto mbox_exit;
+	}
+
+	/* Wait for command to complete */
+	wait_count = jiffies + MBOX_TOV * HZ;
+	while (test_bit(AF_MBOX_COMMAND_DONE, &ha->flags) == 0) {
+		if (wait_count <= jiffies)
+			break;
+
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		intr_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+		if (intr_status & INTR_PENDING) {
+			/*
+			 * Service the interrupt.
+			 * The ISR will save the mailbox status registers
+			 * to a temporary storage location in the adapter
+			 * structure.
+			 */
+			ha->mbox_status_count = outCount;
+			qla4xxx_interrupt_service_routine(ha, intr_status);
+		}
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+		/*
+		 * Delay for 10 microseconds
+		 * NOTE: Interrupt_handler may be called here,
+		 *       if interrupts are enabled
+		 */
+#if 0
+		udelay(10);
+
+#else				/*  */
+		schedule_timeout(10);
+
+#endif				/*  */
+	}			/* wait loop */
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&ha->mailbox_wait_queue, &wait);
+
+	/* Check for mailbox timeout. */
+	if (!test_bit(AF_MBOX_COMMAND_DONE, &ha->flags)) {
+		DEBUG2(printk("scsi%ld: Mailbox Cmd 0x%08X timed out ...,"
+		    " Scheduling Adapter Reset\n", ha->host_no, mbx_cmd[0]));
+		ha->mailbox_timeout_count++;
+		mbx_sts[0] = (-1);
+		set_bit(DPC_RESET_HA, &ha->dpc_flags);
+		goto mbox_exit;
+	}
+
+	/*
+	 * Copy the mailbox out registers to the caller's mailbox in/out
+	 * structure.
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	for (i = 0; i < outCount; i++)
+		mbx_sts[i] = ha->mbox_status[i];
+
+	/* Set return status and error flags (if applicable). */
+	switch (ha->mbox_status[0]) {
+	case MBOX_STS_COMMAND_COMPLETE:
+		status = QLA_SUCCESS;
+		break;
+
+	case MBOX_STS_INTERMEDIATE_COMPLETION:
+		status = QLA_SUCCESS;
+		break;
+
+	case MBOX_STS_BUSY:
+		DEBUG2( printk("scsi%ld: %s: Cmd = %08X, ISP BUSY\n",
+		    ha->host_no, __func__, mbx_cmd[0]));
+		ha->mailbox_timeout_count++;
+		break;
+
+	default:
+		DEBUG2(printk("scsi%ld: %s: **** FAILED, cmd = %08X, "
+		    "sts = %08X ****\n", ha->host_no, __func__, mbx_cmd[0],
+		    mbx_sts[0]));
+		break;
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+mbox_exit:
+	clear_bit(AF_MBOX_COMMAND, &ha->flags);
+	clear_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
+	up(&ha->mbox_sem);
+
+	return status;
+}
+
+#if 0
+int qla4xxx_send_noop(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_NOP;
+	if (qla4xxx_mailbox_command(ha, 1, 1, &mbox_cmd[0], &mbox_sts[0])
+	    != QLA_SUCCESS) {
+		DEBUG2( printk(KERN_INFO "scsi%d: NOP failed\n", ha->host_no));
+		return (QLA_ERROR);
+	}
+
+	else {
+		return (QLA_SUCCESS);
+	}
+}
+
+int qla4xxx_mbx_test(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	int i;
+	int status;
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_REGISTER_TEST;
+	mbox_cmd[1] = 0x11111111;
+	mbox_cmd[2] = 0x22222222;
+	mbox_cmd[3] = 0x33333333;
+	mbox_cmd[4] = 0x44444444;
+	mbox_cmd[5] = 0x55555555;
+	mbox_cmd[6] = 0x66666666;
+	mbox_cmd[7] = 0x77777777;
+	if (qla4xxx_mailbox_command(ha, 8, 8, &mbox_cmd[0], &mbox_sts[0])
+	    != QLA_SUCCESS) {
+		return (QLA_ERROR);
+	}
+	if (mbox_sts[1] != 0x11111111 || mbox_sts[2] != 0x22222222
+	    || mbox_sts[3] != 0x33333333 || mbox_sts[4] != 0x44444444
+	    || mbox_sts[5] != 0x55555555 || mbox_sts[6] != 0x66666666
+	    || mbox_sts[7] != 0x77777777) {
+		status = QLA_ERROR;
+	}
+
+	else {
+		status = QLA_SUCCESS;
+	}
+	return (status);
+}
+
+#endif				/*  */
+
+/*
+ * qla4xxx_issue_iocb
+ *	Issue IOCB using mailbox command
+ *
+ * Input:
+ *	ha = adapter state pointer.
+ *	buffer = buffer pointer.
+ *	phys_addr = physical address of buffer.
+ *	size = size of buffer.
+ *	TARGET_QUEUE_LOCK must be released.
+ *	ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ *	qla2x00 local function return status code.
+ *
+ * Context:
+ *	Kernel context.
+ */
+int
+qla4xxx_issue_iocb(scsi_qla_host_t * ha, void *buffer, dma_addr_t phys_addr,
+    size_t size)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	int status;
+
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_EXECUTE_IOCB_A64;
+	mbox_cmd[1] = 0;
+	mbox_cmd[2] = LSDW(phys_addr);
+	mbox_cmd[3] = MSDW(phys_addr);
+	status = qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]);
+	return status;
+}
+
+int
+qla4xxx_conn_close_sess_logout(scsi_qla_host_t * ha, uint16_t fw_ddb_index,
+    uint16_t connection_id, uint16_t option)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_CONN_CLOSE_SESS_LOGOUT;
+	mbox_cmd[1] = fw_ddb_index;
+	mbox_cmd[2] = connection_id;
+	mbox_cmd[3] = LOGOUT_OPTION_RELOGIN;
+	if (qla4xxx_mailbox_command(ha, 4, 2, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_CONN_CLOSE_SESS_LOGOUT "
+		    "option %04x failed sts %04X %04X", ha->host_no, __func__,
+		    option, mbox_sts[0], mbox_sts[1]));
+		if (mbox_sts[0] == 0x4005)
+			DEBUG2(printk("%s reason %04X\n", __func__,
+			    mbox_sts[1]));
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_clear_database_entry(scsi_qla_host_t * ha, uint16_t fw_ddb_index)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_CLEAR_DATABASE_ENTRY;
+	mbox_cmd[1] = fw_ddb_index;
+	if (qla4xxx_mailbox_command(ha, 2, 5, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4xxx_initialize_fw_cb
+ *	This routine initializes the firmware control block for the
+ *	specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully initialized firmware ctrl block
+ *	QLA_ERROR   - Failed to initialize firmware ctrl block
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_initialize_fw_cb(scsi_qla_host_t * ha)
+{
+	INIT_FW_CTRL_BLK *init_fw_cb;
+	dma_addr_t init_fw_cb_dma;
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	int status = QLA_ERROR;
+
+	init_fw_cb = dma_alloc_coherent(&ha->pdev->dev,
+	    sizeof(INIT_FW_CTRL_BLK), &init_fw_cb_dma, GFP_KERNEL);
+	if (init_fw_cb == NULL) {
+		DEBUG2(printk("scsi%ld: %s: Unable to alloc init_cb\n",
+		    ha->host_no, __func__));
+		    return 10;
+	}
+	memset(init_fw_cb, 0, sizeof(INIT_FW_CTRL_BLK));
+
+	/* Get Initialize Firmware Control Block. */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK;
+	mbox_cmd[2] = LSDW(init_fw_cb_dma);
+	mbox_cmd[3] = MSDW(init_fw_cb_dma);
+	if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		dma_free_coherent(&ha->pdev->dev, sizeof(INIT_FW_CTRL_BLK),
+		    init_fw_cb, init_fw_cb_dma);
+		return status;
+	}
+
+	/* Initialize request and response queues. */
+	qla4xxx_init_rings(ha);
+
+	/* Fill in the request and response queue information. */
+	init_fw_cb->ReqQConsumerIndex = cpu_to_le16(ha->request_out);
+	init_fw_cb->ComplQProducerIndex = cpu_to_le16(ha->response_in);
+	init_fw_cb->ReqQLen = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH);
+	init_fw_cb->ComplQLen = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH);
+	init_fw_cb->ReqQAddrLo = cpu_to_le32(LSDW(ha->request_dma));
+	init_fw_cb->ReqQAddrHi = cpu_to_le32(MSDW(ha->request_dma));
+	init_fw_cb->ComplQAddrLo = cpu_to_le32(LSDW(ha->response_dma));
+	init_fw_cb->ComplQAddrHi = cpu_to_le32(MSDW(ha->response_dma));
+	init_fw_cb->ShadowRegBufAddrLo = cpu_to_le32(LSDW(ha->shadow_regs_dma));
+	init_fw_cb->ShadowRegBufAddrHi = cpu_to_le32(MSDW(ha->shadow_regs_dma));
+
+	/* Set up required options. */
+	init_fw_cb->FwOptions |=
+	    __constant_cpu_to_le16(FWOPT_SESSION_MODE | FWOPT_INITIATOR_MODE);
+	init_fw_cb->FwOptions &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE);
+
+	/* Save some info in adapter structure. */
+	ha->firmware_options = le16_to_cpu(init_fw_cb->FwOptions);
+	ha->tcp_options = le16_to_cpu(init_fw_cb->TCPOptions);
+	ha->heartbeat_interval = init_fw_cb->HeartbeatInterval;
+	ha->isns_server_port_number =
+	    le16_to_cpu(init_fw_cb->iSNSServerPortNumber);
+	memcpy(ha->ip_address, init_fw_cb->IPAddr,
+	    min(sizeof(ha->ip_address), sizeof(init_fw_cb->IPAddr)));
+	memcpy(ha->subnet_mask, init_fw_cb->SubnetMask,
+	    min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->SubnetMask)));
+	memcpy(ha->isns_ip_address, init_fw_cb->iSNSIPAddr,
+	    min(sizeof(ha->isns_ip_address), sizeof(init_fw_cb->iSNSIPAddr)));
+	memcpy(ha->name_string, init_fw_cb->iSCSINameString,
+	    min(sizeof(ha->name_string), sizeof(init_fw_cb->iSCSINameString)));
+	memcpy(ha->alias, init_fw_cb->Alias,
+	    min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));
+
+	/* Save Command Line Paramater info */
+	ha->port_down_retry_count = le16_to_cpu(init_fw_cb->KeepAliveTimeout);
+	ha->discovery_wait = ql4xdiscoverywait;
+
+	/* Send Initialize Firmware Control Block. */
+	mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE;
+	mbox_cmd[1] = 0;
+	mbox_cmd[2] = LSDW(init_fw_cb_dma);
+	mbox_cmd[3] = MSDW(init_fw_cb_dma);
+	if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) ==
+	    QLA_SUCCESS) {
+		status = QLA_SUCCESS;
+	} else {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_INITIALIZE_FIRMWARE "
+		    "failed w/ status %04X\n", ha->host_no, __func__,
+		    mbox_sts[0]));
+	}
+	dma_free_coherent(&ha->pdev->dev, sizeof(INIT_FW_CTRL_BLK), init_fw_cb,
+	    init_fw_cb_dma);
+
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_get_dhcp_ip_address
+ *	This routine retrieves the IP Address obtained via DHCP for the
+ *	specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully obtained DHCP IP Address
+ *	QLA_ERROR   - Failed to obtained
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_get_dhcp_ip_address(scsi_qla_host_t * ha)
+{
+	INIT_FW_CTRL_BLK *init_fw_cb;
+	dma_addr_t init_fw_cb_dma;
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	init_fw_cb = dma_alloc_coherent(&ha->pdev->dev,
+	    sizeof(INIT_FW_CTRL_BLK), &init_fw_cb_dma, GFP_KERNEL);
+	if (init_fw_cb == NULL) {
+		printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no,
+		    __func__);
+		return 10;
+	}
+
+	/* Get Initialize Firmware Control Block. */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	memset(init_fw_cb, 0, sizeof(INIT_FW_CTRL_BLK));
+	mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK;
+	mbox_cmd[2] = LSDW(init_fw_cb_dma);
+	mbox_cmd[3] = MSDW(init_fw_cb_dma);
+
+	if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n",
+		    ha->host_no, __func__));
+		dma_free_coherent(&ha->pdev->dev, sizeof(INIT_FW_CTRL_BLK),
+		    init_fw_cb, init_fw_cb_dma);
+		return QLA_ERROR;
+	}
+
+	/* Save IP Address. */
+	memcpy(ha->ip_address, init_fw_cb->IPAddr,
+	    min(sizeof(ha->ip_address), sizeof(init_fw_cb->IPAddr)));
+	memcpy(ha->subnet_mask, init_fw_cb->SubnetMask,
+	    min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->SubnetMask)));
+
+	dma_free_coherent(&ha->pdev->dev, sizeof(INIT_FW_CTRL_BLK), init_fw_cb,
+	    init_fw_cb_dma);
+
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4xxx_get_firmware_state
+ *	This routine retrieves the firmware state for the specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully retrieved firmware state
+ *	QLA_ERROR   - Failed to retrieve firmware state
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_get_firmware_state(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	/* Get firmware version */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_GET_FW_STATE;
+	if (qla4xxx_mailbox_command(ha, 1, 4, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_GET_FW_STATE failed w/ "
+		    "status %04X\n", ha->host_no, __func__, mbox_sts[0]));
+		return QLA_ERROR;
+	}
+	ha->firmware_state = mbox_sts[1];
+	ha->board_id = mbox_sts[2];
+	ha->addl_fw_state = mbox_sts[3];
+	DEBUG2(printk("%s firmware_state=0x%x\n", __func__,
+	    ha->firmware_state));
+
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4xxx_get_firmware_status
+ *	This routine retrieves the firmware status for the specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully retrieved firmware status
+ *	QLA_ERROR   - Failed to retrieve firmware status
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_get_firmware_status(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	/* Get firmware version */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_GET_FW_STATUS;
+	if (qla4xxx_mailbox_command(ha, 1, 3, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_GET_FW_STATUS failed w/ "
+		    "status %04X\n", ha->host_no, __func__, mbox_sts[0]));
+		return QLA_ERROR;
+	}
+
+	/* High-water mark of IOCBs */
+	ha->iocb_hiwat = mbox_sts[2];
+	if (ha->iocb_hiwat > IOCB_HIWAT_CUSHION)
+		ha->iocb_hiwat -= IOCB_HIWAT_CUSHION;
+	else
+		ql4_printk(KERN_INFO, ha, "WARNING!!!  You have less than %d "
+		    "firmare IOCBs available (%d).\n", IOCB_HIWAT_CUSHION,
+		    ha->iocb_hiwat);
+
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4xxx_get_fwddb_entry
+ *	This routine retrieves the firmware's device database entry.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      fw_ddb_index - Firmware's device database index
+ *      fw_ddb_entry - Pointer to firmware's device database entry structure
+ *      num_valid_ddb_entries - Pointer to number of valid ddb entries
+ *      next_ddb_index - Pointer to next valid device database index
+ *      fw_ddb_device_state - Pointer to device state
+ *
+ * Output:
+ *      fw_ddb_entry - Fills in structure if pointer is supplied
+ *      num_valid_ddb_entries - Fills in if pointer is supplied
+ *      next_ddb_index - Fills in if pointer is supplied
+ *      fw_ddb_device_state - Fills in if pointer is supplied
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully retrieved ddb info from firmware
+ *	QLA_ERROR   - Failed to retrieve ddb info from firmware
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_get_fwddb_entry(scsi_qla_host_t *ha, uint16_t fw_ddb_index,
+    DEV_DB_ENTRY *fw_ddb_entry, dma_addr_t fw_ddb_entry_dma,
+    uint32_t *num_valid_ddb_entries, uint32_t *next_ddb_index,
+    uint32_t *fw_ddb_device_state, uint32_t *conn_err_detail,
+    uint16_t *tcp_source_port_num, uint16_t *connection_id)
+{
+	int status = QLA_ERROR;
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	/* Make sure the device index is valid */
+	if (fw_ddb_index >= MAX_DDB_ENTRIES) {
+		DEBUG2(printk("scsi%ld: %s: index [%d] out of range.\n",
+		    ha->host_no, __func__, fw_ddb_index));
+		goto exit_get_fwddb;
+	}
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_GET_DATABASE_ENTRY;
+	mbox_cmd[1] = (uint32_t) fw_ddb_index;
+	mbox_cmd[2] = LSDW(fw_ddb_entry_dma);
+	mbox_cmd[3] = MSDW(fw_ddb_entry_dma);
+	if (qla4xxx_mailbox_command(ha, 4, 7, &mbox_cmd[0], &mbox_sts[0]) ==
+	    QLA_ERROR) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_GET_DATABASE_ENTRY failed "
+		    "with status 0x%04X\n", ha->host_no, __func__,
+		    mbox_sts[0]));
+		goto exit_get_fwddb;
+	}
+	if (fw_ddb_index != mbox_sts[1]) {
+		DEBUG2(printk("scsi%ld: %s: index mismatch [%d] != [%d].\n",
+		    ha->host_no, __func__, fw_ddb_index, mbox_sts[1]));
+		goto exit_get_fwddb;
+	}
+	if (fw_ddb_entry) {
+		ql4_printk(KERN_INFO, ha, "DDB[%d] MB0 %04x Tot %d Next %d "
+		    "State %04x ConnErr %08x %d.%d.%d.%d:%04d \"%s\"\n",
+		    fw_ddb_index, mbox_sts[0], mbox_sts[2], mbox_sts[3],
+		    mbox_sts[4], mbox_sts[5], fw_ddb_entry->ipAddr[0],
+		    fw_ddb_entry->ipAddr[1], fw_ddb_entry->ipAddr[2],
+		    fw_ddb_entry->ipAddr[3],
+		    le16_to_cpu(fw_ddb_entry->portNumber),
+		    fw_ddb_entry->iscsiName);
+	}
+	if (num_valid_ddb_entries)
+		*num_valid_ddb_entries = mbox_sts[2];
+	if (next_ddb_index)
+		*next_ddb_index = mbox_sts[3];
+	if (fw_ddb_device_state)
+		*fw_ddb_device_state = mbox_sts[4];
+
+	/*
+	 * RA: This mailbox has been changed to pass connection error and
+	 * details.  Its true for ISP4010 as per Version E - Not sure when it
+	 * was changed.  Get the time2wait from the fw_dd_entry field :
+	 * default_time2wait which we call it as minTime2Wait DEV_DB_ENTRY
+	 * struct.
+	 */
+	if (conn_err_detail)
+		*conn_err_detail = mbox_sts[5];
+	if (tcp_source_port_num)
+		*tcp_source_port_num = (uint16_t) mbox_sts[6] >> 16;
+	if (connection_id)
+		*connection_id = (uint16_t) mbox_sts[6] & 0x00FF;
+	status = QLA_SUCCESS;
+
+exit_get_fwddb:
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_set_fwddb_entry
+ *	This routine initializes or updates the adapter's device database
+ *	entry for the specified device.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *      fw_ddb_index - Firmware's device database index
+ *      fw_ddb_entry - Pointer to firmware's device database entry
+ *		       structure, or NULL.
+ *
+ * Output:
+ *	None
+ *
+ * Remarks:
+ *	This routine also triggers a login for the specified device.
+ *	Therefore, it may also be used as a secondary login routine when
+ *	a NULL pointer is specified for the fw_ddb_entry.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully set ddb_entry in firmware
+ *	QLA_ERROR   - Failed to set ddb_entry in firmware
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_set_ddb_entry(scsi_qla_host_t * ha, uint16_t fw_ddb_index,
+    DEV_DB_ENTRY * fw_ddb_entry, dma_addr_t fw_ddb_entry_dma)
+{
+	int status = QLA_ERROR;
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	/* Do not wait for completion. The firmware will send us an
+	 * ASTS_DATABASE_CHANGED (0x8014) to notify us of the login status.
+	 */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_SET_DATABASE_ENTRY;
+	mbox_cmd[1] = (uint32_t) fw_ddb_index;
+	mbox_cmd[2] = LSDW(fw_ddb_entry_dma);
+	mbox_cmd[3] = MSDW(fw_ddb_entry_dma);
+	status = qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]);
+	return status;
+}
+
+int
+qla4xxx_conn_open_session_login(scsi_qla_host_t * ha, uint16_t fw_ddb_index)
+{
+	int status = QLA_ERROR;
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	/* Do not wait for completion. The firmware will send us an
+	 * ASTS_DATABASE_CHANGED (0x8014) to notify us of the login status.
+	 */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_CONN_OPEN_SESS_LOGIN;
+	mbox_cmd[1] = (uint32_t) fw_ddb_index;
+	mbox_cmd[2] = 0;
+	mbox_cmd[3] = 0;
+	mbox_cmd[4] = 0;
+	status = qla4xxx_mailbox_command(ha, 4, 0, &mbox_cmd[0], &mbox_sts[0]);
+	DEBUG2(printk("%s fw_ddb_index=%d status=%d mbx0_1=0x%x :0x%x\n",
+	    __func__, fw_ddb_index, status, mbox_sts[0], mbox_sts[1]);)
+
+	return status;
+}
+
+/**************************************************************************
+ * qla4xxx_get_crash_record
+ *	This routine retrieves a crash record from the QLA4010 after an
+ *	8002h aen.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+void
+qla4xxx_get_crash_record(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	CRASH_RECORD *crash_record = NULL;
+	dma_addr_t crash_record_dma = 0;
+	uint32_t crash_record_size = 0;
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_cmd));
+
+	/* Get size of crash record. */
+	mbox_cmd[0] = MBOX_CMD_GET_CRASH_RECORD;
+	if (qla4xxx_mailbox_command(ha, 5, 5, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: ERROR: Unable to retrieve size!\n",
+		    ha->host_no, __func__));
+		goto exit_get_crash_record;
+	}
+	crash_record_size = mbox_sts[4];
+	if (crash_record_size == 0) {
+		DEBUG2(printk("scsi%ld: %s: ERROR: Crash record size is 0!\n",
+		    ha->host_no, __func__));
+		goto exit_get_crash_record;
+	}
+
+	/* Alloc Memory for Crash Record. */
+	crash_record = dma_alloc_coherent(&ha->pdev->dev, crash_record_size,
+	    &crash_record_dma, GFP_KERNEL);
+	if (crash_record == NULL)
+		goto exit_get_crash_record;
+
+	/* Get Crash Record. */
+	mbox_cmd[0] = MBOX_CMD_GET_CRASH_RECORD;
+	mbox_cmd[2] = LSDW(crash_record_dma);
+	mbox_cmd[3] = MSDW(crash_record_dma);
+	mbox_cmd[4] = crash_record_size;
+	if (qla4xxx_mailbox_command(ha, 5, 5, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS)
+		goto exit_get_crash_record;
+
+	/* Dump Crash Record. */
+
+exit_get_crash_record:
+	if (crash_record)
+		dma_free_coherent(&ha->pdev->dev, crash_record_size,
+		    crash_record, crash_record_dma);
+}
+
+/**************************************************************************
+ * qla4xxx_get_conn_event_log
+ *	This routine retrieves the connection event log
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ *	None
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+void
+qla4xxx_get_conn_event_log(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	CONN_EVENT_LOG_ENTRY *event_log = NULL;
+	dma_addr_t event_log_dma = 0;
+	uint32_t event_log_size = 0;
+	uint32_t num_valid_entries;
+	/*uint32_t      oldest_entry = 0; */
+
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_cmd));
+
+	/* Get size of crash record. */
+	mbox_cmd[0] = MBOX_CMD_GET_CONN_EVENT_LOG;
+	if (qla4xxx_mailbox_command(ha, 4, 5, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS)
+		goto exit_get_event_log;
+
+	event_log_size = mbox_sts[4];
+	if (event_log_size == 0)
+		goto exit_get_event_log;
+
+	/* Alloc Memory for Crash Record. */
+	event_log = dma_alloc_coherent(&ha->pdev->dev, event_log_size,
+	    &event_log_dma, GFP_KERNEL);
+	if (event_log == NULL)
+		goto exit_get_event_log;
+
+	/* Get Crash Record. */
+	mbox_cmd[0] = MBOX_CMD_GET_CONN_EVENT_LOG;
+	mbox_cmd[2] = LSDW(event_log_dma);
+	mbox_cmd[3] = MSDW(event_log_dma);
+	if (qla4xxx_mailbox_command(ha, 4, 5, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: ERROR: Unable to retrieve event "
+		    "log!\n", ha->host_no, __func__));
+		goto exit_get_event_log;
+	}
+
+	/* Dump Event Log. */
+	num_valid_entries = mbox_sts[1];
+
+	//FIXME:  Code wraparound entries later ...
+	//if (num_valid_entries > MAX_CONN_EVENT_LOG_ENTRIES)
+	//      oldest_entry = num_valid_entries % MAX_CONN_EVENT_LOG_ENTRIES;
+exit_get_event_log:
+	if (event_log)
+		dma_free_coherent(&ha->pdev->dev, event_log_size, event_log,
+		    event_log_dma);
+}
+
+/**************************************************************************
+ * qla4xxx_reset_lun
+ *	This routine performs a LUN RESET on the specified target/lun.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *	ddb_entry - Pointer to device database entry
+ *	lun_entry - Pointer to lun entry structure
+ *
+ * Remarks:
+ *	The caller must ensure that the ddb_entry and lun_entry pointers
+ *	are valid before calling this routine.
+ *
+ * Returns:
+ *	QLA_SUCCESS - lun reset completed successfully
+ *	QLA_ERROR   - lun reset failed
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_reset_lun(scsi_qla_host_t * ha, ddb_entry_t * ddb_entry, int lun)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+	int status = QLA_SUCCESS;
+
+	DEBUG2(printk("scsi%ld:%d:%d: lun reset issued\n", ha->host_no,
+	    ddb_entry->os_target_id, lun));
+
+	/*
+	 * Send lun reset command to ISP, so that the ISP will return all
+	 * outstanding requests with RESET status
+	 */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_LUN_RESET;
+	mbox_cmd[1] = ddb_entry->fw_ddb_index;
+	mbox_cmd[2] = lun << 8;
+	mbox_cmd[5] = 0x01;	/* Immediate Command Enable */
+	qla4xxx_mailbox_command(ha, 6, 1, &mbox_cmd[0], &mbox_sts[0]);
+	if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE &&
+	    mbox_sts[0] != MBOX_STS_COMMAND_ERROR)
+		status = QLA_ERROR;
+/*FIXME*/
+	/*spin_lock_irq(ha->host->host_lock);*/
+	return status;
+}
+
+int
+qla4xxx_isns_enable(scsi_qla_host_t * ha, uint32_t isns_ip_addr,
+    uint16_t isns_server_port_num)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	qla4xxx_isns_build_entity_id(ha);
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_SET_ISNS_SERVICE;
+	mbox_cmd[1] = ISNS_ENABLE;
+	mbox_cmd[2] = isns_ip_addr;
+	mbox_cmd[3] = isns_server_port_num;
+	if (qla4xxx_mailbox_command(ha, 4, 6, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS)
+		return (QLA_ERROR);
+
+	DEBUG2(printk("scsi%ld: Start iSNS Service %d.%d.%d.%d Port %04d...\n",
+	    ha->host_no, (isns_ip_addr & 0x000000FF),
+	    (isns_ip_addr & 0x0000FF00) >> 8,
+	    (isns_ip_addr & 0x00FF0000) >> 16,
+	    (isns_ip_addr & 0xFF000000) >> 24, isns_server_port_num));
+
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_disable(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	if (test_bit(ISNS_FLAG_ISNS_SRV_ENABLED, &ha->isns_flags)) {
+		memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+		memset(&mbox_sts, 0, sizeof(mbox_sts));
+		mbox_cmd[0] = MBOX_CMD_SET_ISNS_SERVICE;
+		mbox_cmd[1] = ISNS_DISABLE;
+		if (qla4xxx_mailbox_command(ha, 2, 2, &mbox_cmd[0],
+		    &mbox_sts[0]) != QLA_SUCCESS) {
+			DEBUG2(printk("scsi%ld: %s: MBOX_CMD_SET_ISNS_SERVICE "
+			    "failed w/ status %04X %04X\n", ha->host_no,
+			    __func__, mbox_sts[0], mbox_sts[1]));
+			return QLA_ERROR;
+		}
+	}
+	clear_bit(ISNS_FLAG_ISNS_SRV_ENABLED, &ha->isns_flags);
+	ISNS_CLEAR_FLAGS(ha);
+	ha->isns_connection_id = 0;
+
+	//ha->isns_scn_conn_id     = 0;
+	//ha->isns_esi_conn_id     = 0;
+	//ha->isns_nsh_conn_id     = 0;
+	ha->isns_remote_port_num = 0;
+	ha->isns_scn_port_num = 0;
+	ha->isns_esi_port_num = 0;
+	ha->isns_nsh_port_num = 0;
+	ha->isns_num_discovered_targets = 0;
+	memset(ha->isns_entity_id, 0, sizeof(ha->isns_entity_id));
+
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_isns_status(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_SET_ISNS_SERVICE;
+	mbox_cmd[1] = ISNS_STATUS;
+	if (qla4xxx_mailbox_command(ha, 2, 2, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_SET_ISNS_SERVICE failed "
+		    "w/ status %04X %04X\n", ha->host_no, __func__,
+		    mbox_sts[0], mbox_sts[1]));
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+int
+qla4xxx_get_flash(scsi_qla_host_t * ha, dma_addr_t dma_addr, uint32_t offset,
+    uint32_t len)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_READ_FLASH;
+	mbox_cmd[1] = LSDW(dma_addr);
+	mbox_cmd[2] = MSDW(dma_addr);
+	mbox_cmd[3] = offset;
+	mbox_cmd[4] = len;
+	if (qla4xxx_mailbox_command(ha, 5, 2, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_READ_FLASH, failed w/ "
+		    "status %04X %04X, offset %08x, len %08x\n", ha->host_no,
+		    __func__, mbox_sts[0], mbox_sts[1], offset, len));
+		return QLA_ERROR;
+	}
+	return QLA_SUCCESS;
+}
+
+/**************************************************************************
+ * qla4xxx_get_fw_version
+ *	This routine retrieves the firmware version for the specified adapter.
+ *
+ * Input:
+ * 	ha - Pointer to host adapter structure.
+ *
+ * Output:
+ *	None
+ *
+ * Remarks:
+ *	In QLA4010, mailboxes 2 & 3 may hold an address for data.  Make sure
+ *	that we write 0 to those mailboxes, if unused.
+ *
+ * Returns:
+ *	QLA_SUCCESS - Successfully retrieved firmware version
+ *	QLA_ERROR   - Failed to retrieve firmware version
+ *
+ * Context:
+ *	Kernel context.
+ **************************************************************************/
+int
+qla4xxx_get_fw_version(scsi_qla_host_t * ha)
+{
+	uint32_t mbox_cmd[MBOX_REG_COUNT];
+	uint32_t mbox_sts[MBOX_REG_COUNT];
+
+	/* Get firmware version. */
+	memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+	memset(&mbox_sts, 0, sizeof(mbox_sts));
+	mbox_cmd[0] = MBOX_CMD_ABOUT_FW;
+	if (qla4xxx_mailbox_command(ha, 4, 5, &mbox_cmd[0], &mbox_sts[0]) !=
+	    QLA_SUCCESS) {
+		DEBUG2(printk("scsi%ld: %s: MBOX_CMD_ABOUT_FW failed w/ "
+		    "status %04X\n", ha->host_no, __func__, mbox_sts[0]));
+		return QLA_ERROR;
+	}
+
+	/* Save firmware version information. */
+	ha->firmware_version[0] = mbox_sts[1];
+	ha->firmware_version[1] = mbox_sts[2];
+	ha->patch_number = mbox_sts[3];
+	ha->build_number = mbox_sts[4];
+
+	return QLA_SUCCESS;
+}
Signature · 1 line
Andrew Vasquez
#7 lore raw

PATCH 7/8 qla4xxx: Support routines

In reply to Andrew Vasquez

ISP4xxx driver support routines.

---

 drivers/scsi/qla4xxx/ql4_dbg.c   |  268 ++++++++++++++++++++++++++++++
 drivers/scsi/qla4xxx/ql4_dbg.h   |  119 +++++++++++++
 drivers/scsi/qla4xxx/ql4_nvram.c |  264 +++++++++++++++++++++++++++++
 drivers/scsi/qla4xxx/ql4_nvram.h |  341 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 992 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/ql4_dbg.c
 create mode 100644 drivers/scsi/qla4xxx/ql4_dbg.h
 create mode 100644 drivers/scsi/qla4xxx/ql4_nvram.c
 create mode 100644 drivers/scsi/qla4xxx/ql4_nvram.h

d5ea85dc8fdf7b10bd1807f64f952c6a986b82e8

drivers/scsi/qla4xxx/ql4_dbg.c new file +268 -0
new file mode 100644
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:
+ *
+ * 	1. Redistribution of source code must retain the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission.
+ *
+ * You may redistribute the hardware specific firmware binary file
+ * under the following terms:
+ *
+ * 	1. Redistribution of source code (only if applicable),
+ * 	   must retain the above copyright notice, this list of
+ * 	   conditions and the following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission
+ *
+ * REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+ * THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+ * CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+ * OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+ * TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ * ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+ * COMBINATION WITH THIS PROGRAM.
+ */
+
+#include "ql4_def.h"
+
+/*
+ * qla4xxx_print_srb_cmd
+ *	This routine displays the SRB command
+ */
+static void qla4xxx_print_srb_info(srb_t * srb)
+{
+	printk("%s: srb = 0x%p, flags=0x%02x\n", __func__, srb, srb->flags);
+	printk("%s: entry_count = 0x%02x, active_array_index=0x%04x\n",
+	       __func__, srb->iocb_cnt, srb->active_array_index);
+	printk("%s: cmd = 0x%p, saved_dma_handle = 0x%lx\n",
+	       __func__, srb->cmd, (unsigned long) srb->dma_handle);
+	printk("%s: fw_ddb_index = %d, lun = %d\n",
+	       __func__, srb->fw_ddb_index, srb->cmd->device->lun);
+	printk("%s: iocb_tov = %d\n",
+	       __func__, srb->iocb_tov);
+	printk("%s: cc_stat = 0x%x, r_start = 0x%lx, u_start = 0x%lx\n\n",
+	       __func__, srb->cc_stat, srb->r_start, srb->u_start);
+}
+
+/*
+ * qla4xxx_print_scsi_cmd
+ *	This routine displays the SCSI command
+ */
+void qla4xxx_print_scsi_cmd(struct scsi_cmnd *cmd)
+{
+	int i;
+	printk("SCSI Command = 0x%p, Handle=0x%p\n", cmd, cmd->host_scribble);
+	printk("  b=%d, t=%02xh, l=%02xh, cmd_len = %02xh\n",
+	       cmd->device->channel, cmd->device->id, cmd->device->lun,
+	       cmd->cmd_len);
+	printk("  CDB = ");
+	for (i = 0; i < cmd->cmd_len; i++)
+		printk("%02x ", cmd->cmnd[i]);
+	printk("  seg_cnt = %d\n", cmd->use_sg);
+	printk("  request buffer = 0x%p, request buffer len = 0x%x\n",
+	       cmd->request_buffer, cmd->request_bufflen);
+	if (cmd->use_sg) {
+		struct scatterlist *sg;
+		sg = (struct scatterlist *)cmd->request_buffer;
+		printk("  SG buffer: \n");
+		qla4xxx_dump_buffer((caddr_t) sg,
+				    (cmd->use_sg * sizeof(struct scatterlist)));
+	}
+	printk("  tag = %d, transfersize = 0x%x \n", cmd->tag,
+	       cmd->transfersize);
+	printk("  Pid = %d, SP = 0x%p\n", (int)cmd->pid, CMD_SP(cmd));
+	printk("  underflow size = 0x%x, direction=0x%x\n", cmd->underflow,
+	       cmd->sc_data_direction);
+	printk("  Current time (jiffies) = 0x%lx, "
+	       "timeout expires = 0x%lx\n", jiffies, cmd->eh_timeout.expires);
+	qla4xxx_print_srb_info((srb_t *) CMD_SP(cmd));
+}
+
+void __dump_registers(scsi_qla_host_t * ha)
+{
+	uint8_t i;
+	for (i = 0; i < MBOX_REG_COUNT; i++) {
+		printk(KERN_INFO "0x%02X mailbox[%d]      = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, mailbox[i]), i,
+		       RD_REG_DWORD(&ha->reg->mailbox[i]));
+	}
+	printk(KERN_INFO "0x%02X flash_address   = 0x%08X\n",
+	       (uint8_t) offsetof(isp_reg_t, flash_address),
+	       RD_REG_DWORD(&ha->reg->flash_address));
+	printk(KERN_INFO "0x%02X flash_data      = 0x%08X\n",
+	       (uint8_t) offsetof(isp_reg_t, flash_data),
+	       RD_REG_DWORD(&ha->reg->flash_data));
+	printk(KERN_INFO "0x%02X ctrl_status     = 0x%08X\n",
+	       (uint8_t) offsetof(isp_reg_t, ctrl_status),
+	       RD_REG_DWORD(&ha->reg->ctrl_status));
+	if (IS_QLA4010(ha)) {
+		printk(KERN_INFO "0x%02X nvram           = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u1.isp4010.nvram),
+		       RD_REG_DWORD(&ha->reg->u1.isp4010.nvram));
+	}
+
+	else if (IS_QLA4022(ha)) {
+		printk(KERN_INFO "0x%02X intr_mask       = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u1.isp4022.intr_mask),
+		       RD_REG_DWORD(&ha->reg->u1.isp4022.intr_mask));
+		printk(KERN_INFO "0x%02X nvram           = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u1.isp4022.nvram),
+		       RD_REG_DWORD(&ha->reg->u1.isp4022.nvram));
+		printk(KERN_INFO "0x%02X semaphore       = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u1.isp4022.semaphore),
+		       RD_REG_DWORD(&ha->reg->u1.isp4022.semaphore));
+	}
+	printk(KERN_INFO "0x%02X req_q_in        = 0x%08X\n",
+	       (uint8_t) offsetof(isp_reg_t, req_q_in),
+	       RD_REG_DWORD(&ha->reg->req_q_in));
+	printk(KERN_INFO "0x%02X rsp_q_out       = 0x%08X\n",
+	       (uint8_t) offsetof(isp_reg_t, rsp_q_out),
+	       RD_REG_DWORD(&ha->reg->rsp_q_out));
+	if (IS_QLA4010(ha)) {
+		printk(KERN_INFO "0x%02X ext_hw_conf     = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4010.ext_hw_conf),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.ext_hw_conf));
+		printk(KERN_INFO "0x%02X port_ctrl       = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4010.port_ctrl),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.port_ctrl));
+		printk(KERN_INFO "0x%02X port_status     = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4010.port_status),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.port_status));
+		printk(KERN_INFO "0x%02X req_q_out       = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4010.req_q_out),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.req_q_out));
+		printk(KERN_INFO "0x%02X gp_out          = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4010.gp_out),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.gp_out));
+		printk(KERN_INFO "0x%02X gp_in           = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4010.gp_in),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.gp_in));
+		printk(KERN_INFO "0x%02X port_err_status = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t,
+					  u2.isp4010.port_err_status),
+		       RD_REG_DWORD(&ha->reg->u2.isp4010.port_err_status));
+	}
+
+	else if (IS_QLA4022(ha)) {
+		printk(KERN_INFO "Page 0 Registers:\n");
+		printk(KERN_INFO "0x%02X ext_hw_conf     = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t,
+					  u2.isp4022.p0.ext_hw_conf),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p0.ext_hw_conf));
+		printk(KERN_INFO "0x%02X port_ctrl       = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t,
+					  u2.isp4022.p0.port_ctrl),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p0.port_ctrl));
+		printk(KERN_INFO "0x%02X port_status     = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t,
+					  u2.isp4022.p0.port_status),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p0.port_status));
+		printk(KERN_INFO "0x%02X gp_out          = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4022.p0.gp_out),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p0.gp_out));
+		printk(KERN_INFO "0x%02X gp_in           = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t, u2.isp4022.p0.gp_in),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p0.gp_in));
+		printk(KERN_INFO "0x%02X port_err_status = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t,
+					  u2.isp4022.p0.port_err_status),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p0.port_err_status));
+		printk(KERN_INFO "Page 1 Registers:\n");
+		WRT_REG_DWORD(&ha->reg->ctrl_status,
+			      HOST_MEM_CFG_PAGE &
+			      SET_RMASK(CSR_SCSI_PAGE_SELECT));
+		printk(KERN_INFO "0x%02X req_q_out       = 0x%08X\n",
+		       (uint8_t) offsetof(isp_reg_t,
+					  u2.isp4022.p1.req_q_out),
+		       RD_REG_DWORD(&ha->reg->u2.isp4022.p1.req_q_out));
+		WRT_REG_DWORD(&ha->reg->ctrl_status,
+			      PORT_CTRL_STAT_PAGE &
+			      SET_RMASK(CSR_SCSI_PAGE_SELECT));
+	}
+}
+
+/*
+ * qla4xxx_dump_registers
+ *	This routine displays ISP registers
+ *
+ * Input:
+ *	ha       - adapter structure pointer
+ *
+ */
+void qla4xxx_dump_mbox_registers(scsi_qla_host_t * ha)
+{
+	unsigned long flags = 0;
+	int i = 0;
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	for (i = 1; i < MBOX_REG_COUNT; i++)
+		printk(KERN_INFO "  Mailbox[%d] = %08x\n", i,
+		       RD_REG_DWORD(&ha->reg->mailbox[i]));
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void qla4xxx_dump_registers(scsi_qla_host_t * ha)
+{
+	unsigned long flags = 0;
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	__dump_registers(ha);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void qla4xxx_dump_buffer(uint8_t * b, uint32_t size)
+{
+	uint32_t cnt;
+	uint8_t c;
+	printk(" 0   1   2   3   4   5   6   7   8   9  Ah  Bh  Ch  Dh  Eh  "
+	    "Fh\n");
+	printk("------------------------------------------------------------"
+	    "--\n");
+	for (cnt = 0; cnt < size;) {
+		c = *b++;
+		printk("%02x", (uint32_t) c);
+		cnt++;
+		if (!(cnt % 16))
+			printk("\n");
+
+		else
+			printk("  ");
+	}
+	if (cnt % 16)
+		printk("\n");
+}
drivers/scsi/qla4xxx/ql4_dbg.h new file +119 -0

119 lines not on this page · raw message

drivers/scsi/qla4xxx/ql4_nvram.c new file +264 -0

264 lines not on this page · raw message

drivers/scsi/qla4xxx/ql4_nvram.h new file +341 -0

341 lines not on this page · raw message

Signature · 1 line
Andrew Vasquez
#8 lore raw

PATCH 8/8 qla4xxx: Settings

In reply to Andrew Vasquez

ISP4xxx driver settings.

---

 drivers/scsi/qla4xxx/ql4_settings.h |   67 ++++++++++++++++++++++++++++++++++
 drivers/scsi/qla4xxx/ql4_version.h  |   68 +++++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla4xxx/ql4_settings.h
 create mode 100644 drivers/scsi/qla4xxx/ql4_version.h

d7b9a2cde15c79a2908a32a3f80366d3d99767a4

drivers/scsi/qla4xxx/ql4_settings.h new file +67 -0
new file mode 100644
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c)  2003-2005 QLogic Corporation
+ * QLogic Linux iSCSI Driver
+ *
+ * This program includes a device driver for Linux 2.6 that may be
+ * distributed with QLogic hardware specific firmware binary file.
+ * You may modify and redistribute the device driver code under the
+ * GNU General Public License as published by the Free Software
+ * Foundation (version 2 or a later version) and/or under the
+ * following terms, as applicable:
+ *
+ * 	1. Redistribution of source code must retain the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission.
+ *
+ * You may redistribute the hardware specific firmware binary file
+ * under the following terms:
+ *
+ * 	1. Redistribution of source code (only if applicable),
+ * 	   must retain the above copyright notice, this list of
+ * 	   conditions and the following disclaimer.
+ *
+ * 	2. Redistribution in binary form must reproduce the above
+ * 	   copyright notice, this list of conditions and the
+ * 	   following disclaimer in the documentation and/or other
+ * 	   materials provided with the distribution.
+ *
+ * 	3. The name of QLogic Corporation may not be used to
+ * 	   endorse or promote products derived from this software
+ * 	   without specific prior written permission
+ *
+ * REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE,
+ * THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT
+ * CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR
+ * OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT,
+ * TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN
+ * ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN
+ * COMBINATION WITH THIS PROGRAM.
+ */
+
+/*
+ * Compile time Options:
+ *     0 - Disable and 1 - Enable
+ */
+#define	DISABLE_HBA_RESETS	0
drivers/scsi/qla4xxx/ql4_version.h new file +68 -0

68 lines not on this page · raw message

Signature · 1 line
Andrew Vasquez
#9 lore raw
In reply to Andrew Vasquez
Quoted 22 lines · Andrew Vasquez

All,

Here's a first-pass run for submission-review of the qla4xxx driver for QLogic's ISP4010 and ISP4022 products.

The patchset breaks-down as follows:

o Driver definitions
o initialisation routines
o OS integration files
o iSNS routines
o ISR routines
o Mailbox routines
o Support routines
o Settings

There's still quite a bit of work to do:

- All command-queueing has been removed, so cable-pulls cannot be tolerated. We need to complete the iSCSI transport integration (which seems to no be present).

Quoted 8 lines

Are the iSCSI block()/unblock() routines moving forward similarly to FC and now SAS? Mike Christie had submitted patches:

	[PATCH] add block/unblock to iscsi class
	http://marc.theaimsgroup.com/?l=linux-scsi&m=111201856631844&w=2

some time back. Why were the patches dropped?

The class we have today in mainline and that patch was for the iscsi_sfnet driver. The open-iscsi driver in James's iscsi tree has a updated class. It is geared twords open-iscsi's design and so it has some things that may not work so well for qla4xxx off the bat due to what could live in userspace for us and what you do in firmware and where the scsi_host is allocated etc.

#11 lore raw
In reply to Andrew Vasquez
Quoted 5 lines · Andrew Vasquez

Here's a first-pass run for submission-review of the qla4xxx driver for QLogic's ISP4010 and ISP4022 products.

The patchset breaks-down as follows:

Quoted 8 lines

o Driver definitions
o initialisation routines
o OS integration files
o iSNS routines
o ISR routines
o Mailbox routines
o Support routines
o Settings

Seems the mailing-list dropped [patch 1/8] which you can download here:

ftp://ftp.qlogic.com/outgoing/linux/iSCSI/upstream/5.00.03b15-k/0001-ISP4xxx-driver-definitions.diff

I've also uploaded a complete tarball of the driver for easier review:

ftp://ftp.qlogic.com/outgoing/linux/iSCSI/upstream/5.00.03b15-k/qla4xxx-src-v5.00.03b15-k_submit.tar.bz2

Thanks,
Andrew

#12 lore raw
In reply to Andrew Vasquez
Quoted 34 lines · Andrew Vasquez

All,

Here's a first-pass run for submission-review of the qla4xxx driver for QLogic's ISP4010 and ISP4022 products.

The patchset breaks-down as follows:

o Driver definitions
o initialisation routines
o OS integration files
o iSNS routines
o ISR routines
o Mailbox routines
o Support routines
o Settings

There's still quite a bit of work to do:

- All command-queueing has been removed, so cable-pulls cannot be tolerated. We need to complete the iSCSI transport integration (which seems to no be present).

Are the iSCSI block()/unblock() routines moving forward similarly to FC and now SAS? Mike Christie had submitted patches:

	[PATCH] add block/unblock to iscsi class
	http://marc.theaimsgroup.com/?l=linux-scsi&m=111201856631844&w=2

some time back. Why were the patches dropped?

- Request-queue management cleanup

- iSNS code rework

Quoted 8 lines

- Additional cruft removal (unused structures and functions)

All patches can be found at the following URL:

	ftp://ftp.qlogic.com/outgoing/linux/iSCSI/upstream/5.00.03b15-k/

Comments and criticisms welcomed.

Any new patches in the pipeline?

Looking for volunteers?

++doug

#13 lore raw
Quoted 5 lines · Doug Maxey

Any new patches in the pipeline?

Looking for volunteers?

If you are looking for something to do, you could fix up scsi_transport_iscsi.c in scsi-misc so HW and software iscsi could use it. I told Andrew I would do it, but I have been swamped doing things like qla2xxx and mpt updates and multipath stuff at work so I have not gotton to do much iscsi stuff lately :)

#14 lore raw
Quoted 4 lines · Mike Christie
Quoted 3 lines · Doug Maxey

Any new patches in the pipeline?

Quoted 9 lines
Quoted 2 lines

Looking for volunteers?

If you are looking for something to do, you could fix up scsi_transport_iscsi.c in scsi-misc so HW and software iscsi could use it. I told Andrew I would do it, but I have been swamped doing things like qla2xxx and mpt updates and multipath stuff at work so I have not gotton to do much iscsi stuff lately :)

Is there an outline of the work needed?

++doug