* [PATCH 0/8] qla4xxx: new driver -- request for review.
@ 2005-09-06 22:08 Andrew Vasquez
2005-09-06 22:09 ` [PATCH 2/8] qla4xxx: initialization routines Andrew Vasquez
` (10 more replies)
0 siblings, 11 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:08 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: 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
- 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
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/8] qla4xxx: initialization routines
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
@ 2005-09-06 22:09 ` Andrew Vasquez
2005-09-07 6:46 ` Rolf Eike Beer
2005-09-06 22:09 ` [PATCH 3/8] qla4xxx: OS integration files Andrew Vasquez
` (9 subsequent siblings)
10 siblings, 1 reply; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:09 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver initialization routines.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
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
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_init.c
@@ -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;
+
+}
diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_iocb.c
@@ -0,0 +1,595 @@
+/*
+ * 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 <scsi/scsi_tcq.h>
+
+/**************************************************************************
+ * qla4xxx_get_req_pkt
+ * This routine performs the following tasks:
+ * - returns the current request_in pointer (if queue not full)
+ * - advances the request_in pointer
+ * - checks for queue full
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * queue_entry - Pointer to pointer to queue entry structure
+ *
+ * Output:
+ * queue_entry - Return pointer to next available request packet
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully retrieved request packet
+ * QLA_ERROR - Failed to retrieve request packet
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_get_req_pkt(scsi_qla_host_t *ha, QUEUE_ENTRY **queue_entry)
+{
+ uint16_t request_in;
+ uint8_t status = QLA_SUCCESS;
+
+ *queue_entry = ha->request_ptr;
+
+ /* get the latest request_in and request_out index */
+ request_in = ha->request_in;
+ ha->request_out = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out);
+
+ /* Advance request queue pointer and check for queue full */
+ if (request_in == (REQUEST_QUEUE_DEPTH - 1)) {
+ request_in = 0;
+ ha->request_ptr = ha->request_ring;
+ } else {
+ request_in++;
+ ha->request_ptr++;
+ }
+
+ /* request queue is full, try again later */
+ if ((ha->iocb_cnt + 1) >= ha->iocb_hiwat) {
+ /* restore request pointer */
+ ha->request_ptr = *queue_entry;
+ status = QLA_ERROR;
+ } else {
+ ha->request_in = request_in;
+ memset(*queue_entry, 0, sizeof(**queue_entry));
+ }
+
+ return status;
+}
+
+/**************************************************************************
+ * qla4xxx_send_marker_iocb
+ * This routine issues a marker IOCB.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * ddb_entry - Pointer to device database entry
+ * lun - SCSI LUN
+ * marker_type - marker identifier
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully sent marker IOCB
+ * QLA_ERROR - Failed to send marker IOCB
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_send_marker_iocb(scsi_qla_host_t *ha, ddb_entry_t *ddb_entry, int lun)
+{
+ MARKER_ENTRY *marker_entry;
+ unsigned long flags = 0;
+ uint8_t status = QLA_SUCCESS;
+
+ /* Acquire hardware specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Get pointer to the queue entry for the marker */
+ if (qla4xxx_get_req_pkt(ha, (QUEUE_ENTRY **) &marker_entry) !=
+ QLA_SUCCESS) {
+ status = QLA_ERROR;
+ goto exit_send_marker;
+ }
+
+ /* Put the marker in the request queue */
+ marker_entry->hdr.entryType = ET_MARKER;
+ marker_entry->hdr.entryCount = 1;
+ marker_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index);
+ marker_entry->modifier = cpu_to_le16(MM_LUN_RESET);
+ marker_entry->lun[1] = LSB(lun); /*SAMII compliant lun */
+ marker_entry->lun[2] = MSB(lun);
+ wmb();
+
+ /* Tell ISP it's got a new I/O request */
+ WRT_REG_DWORD(&ha->reg->req_q_in, ha->request_in);
+ PCI_POSTING(&ha->reg->req_q_in);
+
+exit_send_marker:
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return status;
+}
+
+PDU_ENTRY *
+qla4xxx_get_pdu(scsi_qla_host_t * ha, uint32_t length)
+{
+ PDU_ENTRY *pdu;
+ PDU_ENTRY *free_pdu_top;
+ PDU_ENTRY *free_pdu_bottom;
+ uint16_t pdu_active;
+
+ if (ha->free_pdu_top == NULL)
+ return NULL;
+
+ /* Save current state */
+ free_pdu_top = ha->free_pdu_top;
+ free_pdu_bottom = ha->free_pdu_bottom;
+ pdu_active = ha->pdu_active + 1;
+
+ /* get next available pdu */
+ pdu = free_pdu_top;
+ free_pdu_top = pdu->Next;
+ if (free_pdu_top == NULL)
+ free_pdu_bottom = NULL;
+
+ /* round up to nearest page */
+ length = (length + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+
+ /* Allocate pdu buffer PDU */
+ pdu->Buff = dma_alloc_coherent(&ha->pdev->dev, length, &pdu->DmaBuff,
+ GFP_KERNEL);
+ if (pdu->Buff == NULL)
+ return NULL;
+
+ memset(pdu->Buff, 0, length);
+
+ /* Fill in remainder of PDU */
+ pdu->BuffLen = length;
+ pdu->SendBuffLen = 0;
+ pdu->RecvBuffLen = 0;
+ pdu->Next = NULL;
+ ha->free_pdu_top = free_pdu_top;
+ ha->free_pdu_bottom = free_pdu_bottom;
+ ha->pdu_active = pdu_active;
+ return pdu;
+}
+
+void
+qla4xxx_free_pdu(scsi_qla_host_t * ha, PDU_ENTRY * pdu)
+{
+ if (ha->free_pdu_bottom == NULL) {
+ ha->free_pdu_top = pdu;
+ ha->free_pdu_bottom = pdu;
+ } else {
+ ha->free_pdu_bottom->Next = pdu;
+ ha->free_pdu_bottom = pdu;
+ }
+ dma_free_coherent(&ha->pdev->dev, pdu->BuffLen, pdu->Buff,
+ pdu->DmaBuff);
+ ha->pdu_active--;
+
+ /* Clear PDU */
+ pdu->Buff = NULL;
+ pdu->BuffLen = 0;
+ pdu->SendBuffLen = 0;
+ pdu->RecvBuffLen = 0;
+ pdu->Next = NULL;
+ pdu->DmaBuff = 0;
+}
+
+/**************************************************************************
+ * qla4xxx_send_passthru0_iocb
+ * This routine issues a passthru0 IOCB.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Remarks: hardware_lock acquired upon entry, interrupt context
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully sent marker IOCB
+ * QLA_ERROR - Failed to send marker IOCB
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_send_passthru0_iocb(scsi_qla_host_t * ha, uint16_t fw_ddb_index,
+ uint16_t connection_id, dma_addr_t pdu_dma_data, uint32_t send_len,
+ uint32_t recv_len, uint16_t control_flags, uint32_t handle)
+{
+ PASSTHRU0_ENTRY *passthru_entry;
+ uint8_t status = QLA_SUCCESS;
+
+ /* Get pointer to the queue entry for the marker */
+ if (qla4xxx_get_req_pkt(ha, (QUEUE_ENTRY **) &passthru_entry) !=
+ QLA_SUCCESS) {
+ status = QLA_ERROR;
+ goto exit_send_pt0;
+ }
+
+ /* Fill in the request queue */
+ passthru_entry->hdr.entryType = ET_PASSTHRU0;
+ passthru_entry->hdr.entryCount = 1;
+ passthru_entry->handle = cpu_to_le32(handle);
+ passthru_entry->target = cpu_to_le16(fw_ddb_index);
+ passthru_entry->connectionID = cpu_to_le16(connection_id);
+ passthru_entry->timeout = __constant_cpu_to_le16(PT_DEFAULT_TIMEOUT);
+ if (send_len) {
+ control_flags |= PT_FLAG_SEND_BUFFER;
+ passthru_entry->outDataSeg64.base.addrHigh =
+ cpu_to_le32(MSDW(pdu_dma_data));
+ passthru_entry->outDataSeg64.base.addrLow =
+ cpu_to_le32(LSDW(pdu_dma_data));
+ passthru_entry->outDataSeg64.count = cpu_to_le32(send_len);
+ }
+ if (recv_len) {
+ passthru_entry->inDataSeg64.base.addrHigh =
+ cpu_to_le32(MSDW(pdu_dma_data));
+ passthru_entry->inDataSeg64.base.addrLow =
+ cpu_to_le32(LSDW(pdu_dma_data));
+ passthru_entry->inDataSeg64.count = cpu_to_le32(recv_len);
+ }
+ passthru_entry->controlFlags = cpu_to_le16(control_flags);
+ wmb();
+
+ /* Tell ISP it's got a new I/O request */
+ WRT_REG_DWORD(&ha->reg->req_q_in, ha->request_in);
+ PCI_POSTING(&ha->reg->req_q_in);
+
+exit_send_pt0:
+ return status;
+}
+
+CONTINUE_ENTRY *
+qla4xxx_alloc_cont_entry(scsi_qla_host_t * ha)
+{
+ CONTINUE_ENTRY *cont_entry;
+
+ cont_entry = (CONTINUE_ENTRY *)ha->request_ptr;
+
+ /* Advance request queue pointer */
+ if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1)) {
+ ha->request_in = 0;
+ ha->request_ptr = ha->request_ring;
+ } else {
+ ha->request_in++;
+ ha->request_ptr++;
+ }
+
+ /* Load packet defaults */
+ cont_entry->hdr.entryType = ET_CONTINUE;
+ cont_entry->hdr.entryCount = 1;
+ cont_entry->hdr.systemDefined = (uint8_t) cpu_to_le16(ha->request_in);
+
+ return cont_entry;
+}
+
+uint16_t
+qla4xxx_calc_request_entries(uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > COMMAND_SEG) {
+ iocbs += (dsds - COMMAND_SEG) / CONTINUE_SEG;
+ if ((dsds - COMMAND_SEG) % CONTINUE_SEG)
+ iocbs++;
+ }
+ return iocbs;
+}
+
+void
+qla4xxx_build_scsi_iocbs(srb_t *srb, COMMAND_ENTRY *cmd_entry,
+ uint16_t tot_dsds)
+{
+ scsi_qla_host_t *ha;
+ uint16_t avail_dsds;
+ DATA_SEG_A64 *cur_dsd;
+ struct scsi_cmnd *cmd;
+
+ cmd = srb->cmd;
+ ha = srb->ha;
+
+ if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
+ /* No data being transferred */
+ cmd_entry->ttlByteCnt = __constant_cpu_to_le32(0);
+ return;
+ }
+
+ avail_dsds = COMMAND_SEG;
+ cur_dsd = (DATA_SEG_A64 *) & (cmd_entry->dataseg[0]);
+
+ /* Load data segments */
+ if (cmd->use_sg) {
+ struct scatterlist *cur_seg;
+ struct scatterlist *end_seg;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ end_seg = cur_seg + tot_dsds;
+ while (cur_seg < end_seg) {
+ dma_addr_t sle_dma;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ CONTINUE_ENTRY *cont_entry;
+
+ cont_entry = qla4xxx_alloc_cont_entry(ha);
+ cur_dsd =
+ (DATA_SEG_A64 *) &cont_entry->dataseg[0];
+ avail_dsds = CONTINUE_SEG;
+ }
+
+ sle_dma = sg_dma_address(cur_seg);
+ cur_dsd->base.addrLow = cpu_to_le32(LSDW(sle_dma));
+ cur_dsd->base.addrHigh = cpu_to_le32(MSDW(sle_dma));
+ cur_dsd->count = cpu_to_le32(sg_dma_len(cur_seg));
+ avail_dsds--;
+
+ cur_dsd++;
+ cur_seg++;
+ }
+ } else {
+ cur_dsd->base.addrLow = cpu_to_le32(LSDW(srb->dma_handle));
+ cur_dsd->base.addrHigh = cpu_to_le32(MSDW(srb->dma_handle));
+ cur_dsd->count = cpu_to_le32(cmd->request_bufflen);
+ }
+}
+
+/**************************************************************************
+ * qla4xxx_send_command_to_isp
+ * This routine is called by qla4xxx_queuecommand to build an ISP
+ * command and pass it to the ISP for execution.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * srb - pointer to SCSI Request Block to be sent to ISP
+ *
+ * Output:
+ * None
+ *
+ * Remarks:
+ * None
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully sent command to ISP
+ * QLA_ERROR - Failed to send command to ISP
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_send_command_to_isp(scsi_qla_host_t *ha, srb_t * srb)
+{
+ struct scsi_cmnd *cmd = srb->cmd;
+ ddb_entry_t *ddb_entry;
+ COMMAND_ENTRY *cmd_entry;
+ struct scatterlist *sg;
+
+ uint16_t tot_dsds;
+ uint16_t req_cnt;
+
+ unsigned long flags;
+ uint16_t cnt;
+ uint16_t i;
+ uint32_t index;
+ char tag[2];
+
+ /* Get real lun and adapter */
+ ddb_entry = srb->ddb;
+
+ /* Send marker(s) if needed. */
+ if (ha->marker_needed == 1) {
+ if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun) !=
+ QLA_SUCCESS) {
+ return QLA_ERROR;
+ }
+ ha->marker_needed = 0;
+ }
+ tot_dsds = 0;
+
+ /* Acquire hardware specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Check for room in active srb array */
+ index = ha->current_active_index;
+ for (i = 0; i < MAX_SRBS; i++) {
+ index++;
+ if (index == MAX_SRBS)
+ index = 1;
+ if (ha->active_srb_array[index] == 0) {
+ ha->current_active_index = index;
+ break;
+ }
+ }
+ if (i >= MAX_SRBS) {
+ printk(KERN_INFO "scsi%ld: %s: NO more SRB entries used "
+ "iocbs=%d, \n reqs remaining=%d\n", ha->host_no, __func__,
+ ha->iocb_cnt, ha->req_q_count);
+ goto queuing_error;
+ }
+
+ /* Calculate the number of request entries needed. */
+ if (cmd->use_sg) {
+ sg = (struct scatterlist *)cmd->request_buffer;
+ tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg,
+ cmd->sc_data_direction);
+ if (tot_dsds == 0)
+ goto queuing_error;
+ } else if (cmd->request_bufflen) {
+ dma_addr_t req_dma;
+
+ req_dma = pci_map_single(ha->pdev, cmd->request_buffer,
+ cmd->request_bufflen, cmd->sc_data_direction);
+ if (dma_mapping_error(req_dma))
+ goto queuing_error;
+
+ srb->dma_handle = req_dma;
+ tot_dsds = 1;
+ }
+ req_cnt = qla4xxx_calc_request_entries(tot_dsds);
+
+ if (ha->req_q_count < (req_cnt + 2)) {
+ cnt = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out);
+ if (ha->request_in < cnt)
+ ha->req_q_count = cnt - ha->request_in;
+ else
+ ha->req_q_count = REQUEST_QUEUE_DEPTH -
+ (ha->request_in - cnt);
+ }
+
+ if (ha->req_q_count < (req_cnt + 2))
+ goto queuing_error;
+
+ /* total iocbs active */
+ if ((ha->iocb_cnt + req_cnt) >= REQUEST_QUEUE_DEPTH)
+ goto queuing_error;
+
+ /* Build command packet */
+ cmd_entry = (COMMAND_ENTRY *) ha->request_ptr;
+ cmd_entry->hdr.entryType = ET_COMMAND;
+ cmd_entry->handle = cpu_to_le32(index);
+ cmd_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index);
+ cmd_entry->connection_id = cpu_to_le16(ddb_entry->connection_id);
+
+ cmd_entry->lun[1] = LSB(cmd->device->lun); /* SAMII compliant. */
+ cmd_entry->lun[2] = MSB(cmd->device->lun);
+ cmd_entry->cmdSeqNum = cpu_to_le32(ddb_entry->CmdSn);
+ cmd_entry->ttlByteCnt = cpu_to_le32(cmd->request_bufflen);
+ memcpy(cmd_entry->cdb, cmd->cmnd, min((unsigned char)MAX_COMMAND_SIZE,
+ cmd->cmd_len));
+ cmd_entry->dataSegCnt = cpu_to_le16(tot_dsds);
+ cmd_entry->hdr.entryCount = req_cnt;
+
+ /* Set data transfer direction control flags
+ * NOTE: Look at data_direction bits iff there is data to be
+ * transferred, as the data direction bit is sometimed filled
+ * in when there is no data to be transferred */
+ cmd_entry->control_flags = CF_NO_DATA;
+ if (cmd->request_bufflen) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ cmd_entry->control_flags = CF_WRITE;
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ cmd_entry->control_flags = CF_READ;
+ }
+
+ /* Set tagged queueing control flags */
+ cmd_entry->control_flags |= CF_SIMPLE_TAG;
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case MSG_HEAD_TAG:
+ cmd_entry->control_flags |= CF_HEAD_TAG;
+ break;
+ case MSG_ORDERED_TAG:
+ cmd_entry->control_flags |= CF_ORDERED_TAG;
+ break;
+ }
+ }
+
+ /* Advance request queue pointer */
+ ha->request_in++;
+ if (ha->request_in == REQUEST_QUEUE_DEPTH) {
+ ha->request_in = 0;
+ ha->request_ptr = ha->request_ring;
+ } else {
+ ha->request_ptr++;
+ }
+
+ qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds);
+ wmb();
+
+ /* put command in active array */
+ ha->active_srb_array[index] = srb;
+ srb->cmd->host_scribble = (unsigned char *)(unsigned long)index;
+ //srb->active_array_index = index;
+
+ /* update counters */
+ ha->active_srb_count++;
+ srb->state = SRB_ACTIVE_STATE;
+ srb->flags |= SRB_DMA_VALID;
+
+ /* Track IOCB used */
+ ha->iocb_cnt += req_cnt;
+ srb->iocb_cnt = req_cnt;
+ ha->req_q_count -= req_cnt;
+
+ /* Debug print statements */
+ WRT_REG_DWORD(&ha->reg->req_q_in, ha->request_in);
+ PCI_POSTING(&ha->reg->req_q_in);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_SUCCESS;
+
+queuing_error:
+ if (cmd->use_sg && tot_dsds) {
+ sg = (struct scatterlist *) cmd->request_buffer;
+ pci_unmap_sg(ha->pdev, sg, cmd->use_sg, cmd->sc_data_direction);
+ } else if (tot_dsds) {
+ pci_unmap_single(ha->pdev, srb->dma_handle,
+ cmd->request_bufflen, cmd->sc_data_direction);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_ERROR;
+}
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/8] qla4xxx: OS integration files
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
2005-09-06 22:09 ` [PATCH 2/8] qla4xxx: initialization routines Andrew Vasquez
@ 2005-09-06 22:09 ` Andrew Vasquez
2005-09-06 22:09 ` [PATCH 4/8] qla4xxx: iSNS routines Andrew Vasquez
` (8 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:09 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver OS files.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
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
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -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"
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -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
diff --git a/drivers/scsi/qla4xxx/Kconfig b/drivers/scsi/qla4xxx/Kconfig
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/Kconfig
@@ -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.
diff --git a/drivers/scsi/qla4xxx/Makefile b/drivers/scsi/qla4xxx/Makefile
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/Makefile
@@ -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
diff --git a/drivers/scsi/qla4xxx/ql4_attr.c b/drivers/scsi/qla4xxx/ql4_attr.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_attr.c
@@ -0,0 +1,328 @@
+/*
+ * 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 <scsi/scsi_transport_iscsi.h>
+
+/* Host attributes. */
+
+static void qla4xxx_get_initiator_name(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *ha = to_qla_host(shost);
+
+ /* &ha->iscsi_name[0]; */
+ /* ha->ip_address[0], ha->ip_address[1], ha->ip_address[2], ha->ip_address[3]) */
+ /* ha->subnet_mask[0], ha->subnet_mask[1], ha->subnet_mask[2], ha->subnet_mask[3]) */
+ /* init_fw_cb->GatewayIPAddr[0],
+ init_fw_cb->GatewayIPAddr[1],
+ init_fw_cb->GatewayIPAddr[2],
+ init_fw_cb->GatewayIPAddr[3]) */
+
+}
+
+static void qla4xxx_get_initiator_alias(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *ha = to_qla_host(shost);
+}
+
+/* Target attributes. */
+static void qla4xxx_get_isid(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct ddb_entry *ddb;
+}
+
+static void qla4xxx_get_tsih(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct ddb_entry *ddb = qla4xxx_lookup_ddb_by_fw_index(ha, starget->id);
+}
+
+static void qla4xxx_get_ip_address(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct ddb_entry *ddb = qla4xxx_lookup_ddb_by_fw_index(ha, starget->id);
+
+ if (ddb)
+ memcpy(&iscsi_sin_addr(starget), &ddb->ipaddress,
+ ISCSI_IPADDR_SIZE);
+}
+
+static void qla4xxx_get_port(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct ddb_entry *ddb = qla4xxx_lookup_ddb_by_fw_index(ha, starget->id);
+
+ struct sockaddr_in *addr = (struct sockaddr_in *)&ddb->addr;
+ iscsi_port(starget) = addr->sin_port;
+}
+
+static void qla4xxx_get_tpgt(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct ddb_entry *ddb = qla4xxx_lookup_ddb_by_fw_index(ha, starget->id);
+
+ iscsi_tpgt(starget) = ddb->portal_group_tag;
+}
+
+static void qla4xxx_get_isid(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct ddb_entry *ddb = qla4xxx_lookup_ddb_by_fw_index(ha, starget->id);
+
+ memcpy(iscsi_isid(starget), ddb->isid, sizeof(ddb->isid));
+}
+
+static void qla4xxx_get_initial_r2t(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_immediate_data(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_header_digest(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_data_digest(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_max_burst_len(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_first_burst_len(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_max_recv_data_segment_len(struct scsi_target *starget)
+{
+}
+
+static void qla4xxx_get_starget_loss_tmo(struct scsi_target *starget)
+{
+ struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(host);
+
+ iscsi_starget_dev_loss_tmo(starget) = ha->port_down_retry_count + 5;
+}
+
+static void
+qla4xxx_set_starget_loss_tmo(struct scsi_target *starget, uint32_t timeout)
+{
+ struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(host);
+
+ if (timeout)
+ ha->port_down_retry_count = timeout;
+ else
+ ha->port_down_retry_count = 1;
+}
+
+static struct fc_function_template qla4xxx_transport_functions = {
+ .get_isid = qla4xxx_get_isid,
+ .show_isid = 1,
+ .get_tsih = qla4xxx_get_tsih,
+ .show_tsih = 1,
+ .get_port = qla4xxx_get_target_port,
+ .show_port = 1,
+ .get_tpgt = qla4xxx_get_tpgt,
+ .show_tpgt = 1,
+ .get_ip_address = qla4xxx_get_target_ip_address,
+ .show_ip_address = 1,
+ .get_initial_r2t = qla4xxx_get_initial_r2t,
+ .show_initial_r2t = 1,
+ .get_immediate_data = qla4xxx_get_immediate_data,
+ .show_immediate_data = 1,
+ .get_header_digest = qla4xxx_get_header_digest,
+ .show_header_digest = 1,
+ .get_data_digest = qla4xxx_get_data_digest,
+ .show_data_digest = 1,
+ .get_max_burst_len = qla4xxx_get_max_burst_len,
+ .show_max_burst_len = 1,
+ .get_first_burst_len = qla4xxx_get_first_burst_len,
+ .show_first_burst_len = 1,
+ .get_max_recv_data_segment_len = qla4xxx_get_max_recv_data_segment_len,
+ .show_max_recv_data_segment_len = 1,
+ .get_target_name = qla4xxx_get_target_name,
+ .show_target_name = 1,
+ .get_target_alias = qla4xxx_get_target_alias,
+ .show_target_alias = 1,
+ .get_initiator_alias = qla4xxx_get_initiator_alias,
+ .show_initiator_alias = 1,
+ .get_initiator_name = qla4xxx_get_initiator_name,
+ .show_initiator_name = 1,
+
+ .get_starget_dev_loss_tmo = qla4xxx_get_starget_loss_tmo,
+ .set_starget_dev_loss_tmo = qla4xxx_set_starget_loss_tmo,
+ .show_starget_dev_loss_tmo = 1,
+
+};
+
+struct scsi_transport_template *qla4xxx_alloc_transport_tmpl(void)
+{
+ return (iscsi_attach_transport(&qla4xxx_transport_functions));
+}
+
+/* SYSFS attributes --------------------------------------------------------- */
+
+static ssize_t
+qla4xxx_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off,
+ size_t count)
+{
+ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ uint16_t *witer;
+ unsigned long flags;
+ uint16_t cnt;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != sizeof(nvram_t))
+ return 0;
+
+ /* Read NVRAM. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ qla4xxx_lock_nvram_access(ha);
+ witer = (uint16_t *) buf;
+ for (cnt = 0; cnt < count / 2; cnt++) {
+ *witer = cpu_to_le16(qla2x00_get_nvram_word(ha,
+ cnt + ha->nvram_base));
+ witer++;
+ }
+ qla4xxx_unlock_nvram_access(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (count);
+}
+
+static ssize_t
+qla4xxx_sysfs_write_nvram(struct kobject *kobj, char *buf, loff_t off,
+ size_t count)
+{
+ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ uint8_t *iter;
+ uint16_t *witer;
+ unsigned long flags;
+ uint16_t cnt;
+ uint8_t chksum;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != sizeof(nvram_t))
+ return 0;
+
+ /* Checksum NVRAM. */
+ iter = (uint8_t *) buf;
+ chksum = 0;
+ for (cnt = 0; cnt < count - 1; cnt++)
+ chksum += *iter++;
+ chksum = ~chksum + 1;
+ *iter = chksum;
+
+ /* Write NVRAM. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ qla4xxx_lock_nvram_access(ha);
+ qla4xxx_release_nvram_protection(ha);
+ witer = (uint16_t *) buf;
+ for (cnt = 0; cnt < count / 2; cnt++) {
+ qla4xxx_write_nvram_word(ha, cnt + ha->nvram_base,
+ cpu_to_le16(*witer));
+ witer++;
+ }
+ qla4xxx_unlock_nvram_access(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (count);
+}
+
+static struct bin_attribute sysfs_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUSR | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(nvram_t),
+ .read = qla4xxx_sysfs_read_nvram,
+ .write = qla4xxx_sysfs_write_nvram,
+};
+
+void qla4xxx_alloc_sysfs_attr(scsi_qla_host_t * ha)
+{
+ struct Scsi_Host *host = ha->host;
+
+ sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
+ sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
+}
+
+void qla4xxx_free_sysfs_attr(scsi_qla_host_t * ha)
+{
+ struct Scsi_Host *host = ha->host;
+
+ sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
+ sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
+}
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -0,0 +1,2311 @@
+/*
+ * 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>
+#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+/*
+ * Driver version
+ */
+char qla4xxx_version_str[40];
+
+/*
+ * SRB allocation cache
+ */
+kmem_cache_t *srb_cachep;
+
+/*
+ * List of host adapters
+ */
+struct list_head qla4xxx_hostlist = LIST_HEAD_INIT(qla4xxx_hostlist);
+rwlock_t qla4xxx_hostlist_lock = RW_LOCK_UNLOCKED;
+int qla4xxx_hba_count = 0;
+
+/*
+ * Module parameter information and variables
+ */
+int ql4xdiscoverywait = 60;
+module_param(ql4xdiscoverywait, int, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(ql4xdiscoverywait, "Discovery wait time");
+int ql4xdontresethba = 0;
+module_param(ql4xdontresethba, int, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(ql4xdontresethba,
+ "Dont reset the HBA when the driver gets 0x8002 AEN "
+ " default it will reset hba :0"
+ " set to 1 to avoid resetting HBA");
+int ql4xcmdretrycount = 20;
+module_param(ql4xcmdretrycount, int, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(ql4xcmdretrycount,
+ "Maximum number of mid-layer retries allowed for a command. "
+ "Default value is 20");
+int ql4xmaxqdepth = 0;
+module_param(ql4xmaxqdepth, int, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(ql4xmaxqdepth,
+ "Maximum queue depth to report for target devices.");
+
+int extended_error_logging = 0; /* 0 = off, 1 = log errors */
+module_param(extended_error_logging, int, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(extended_error_logging,
+ "Option to enable extended error logging, "
+ "Default is 0 - no logging, 1 - debug " "logging");
+
+int displayConfig = 0;
+module_param(displayConfig, int, S_IRUGO | S_IRUSR);
+MODULE_PARM_DESC(displayConfig,
+ "If 1 then display the configuration used in "
+ "/etc/modules.conf.");
+
+char *ql4xdevconf = NULL;
+
+/*
+ * SCSI host template entry points
+ */
+
+static int qla4xxx_mem_alloc(scsi_qla_host_t * ha);
+static void qla4xxx_mem_free(scsi_qla_host_t * ha);
+static void qla4xxx_timer(scsi_qla_host_t *ha);
+static int qla4xxx_do_dpc(void *data);
+void qla4xxx_display_config(void);
+void qla4xxx_add_timer_to_cmd(srb_t * srb, int timeout);
+static void qla4xxx_flush_active_srbs(scsi_qla_host_t * ha);
+int qla4xxx_reset_target(scsi_qla_host_t * ha, ddb_entry_t * ddb_entry);
+int qla4xxx_recover_adapter(scsi_qla_host_t * ha, uint8_t renew_ddb_list);
+void qla4xxx_config_dma_addressing(scsi_qla_host_t * ha);
+
+CONTINUE_ENTRY *qla4xxx_alloc_cont_entry(scsi_qla_host_t * ha);
+
+static int qla4xxx_iospace_config(scsi_qla_host_t * ha);
+
+static int __devinit qla4xxx_probe_adapter(struct pci_dev *,
+ const struct pci_device_id *);
+static void __devexit qla4xxx_remove_adapter(struct pci_dev *);
+static void qla4xxx_free_adapter(scsi_qla_host_t * ha);
+
+/*
+ * SCSI host template entry points
+ */
+static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
+ void (*done) (struct scsi_cmnd *));
+static int qla4xxx_eh_abort(struct scsi_cmnd *cmd);
+static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
+static int qla4xxx_eh_bus_reset(struct scsi_cmnd *cmd);
+static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
+static int qla4xxx_slave_alloc(struct scsi_device *device);
+static int qla4xxx_slave_configure(struct scsi_device *device);
+static void qla4xxx_slave_destroy(struct scsi_device *device);
+
+static struct scsi_host_template qla4xxx_driver_template = {
+ .module = THIS_MODULE,
+ .name = "qla4xxx",
+ .queuecommand = qla4xxx_queuecommand,
+
+ .eh_abort_handler = qla4xxx_eh_abort,
+ .eh_device_reset_handler = qla4xxx_eh_device_reset,
+ .eh_bus_reset_handler = qla4xxx_eh_bus_reset,
+ .eh_host_reset_handler = qla4xxx_eh_host_reset,
+
+ .slave_configure = qla4xxx_slave_configure,
+ .slave_alloc = qla4xxx_slave_alloc,
+ .slave_destroy = qla4xxx_slave_destroy,
+
+ .this_id = -1,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = SG_ALL,
+
+ .max_sectors = 0xFFFF,
+};
+
+/* static struct scsi_transport_template *qla4xxx_transport_template = NULL; */
+
+/*
+ * Timer routines
+ */
+static void
+qla4xxx_start_timer(scsi_qla_host_t * ha, void *func, unsigned long interval)
+{
+ DEBUG(printk("scsi: %s: Starting timer thread for adapter %d\n",
+ __func__, ha->instance));
+ init_timer(&ha->timer);
+ ha->timer.expires = jiffies + interval * HZ;
+ ha->timer.data = (unsigned long)ha;
+ ha->timer.function = (void (*)(unsigned long))func;
+ add_timer(&ha->timer);
+ ha->timer_active = 1;
+}
+
+static void
+qla4xxx_stop_timer(scsi_qla_host_t * ha)
+{
+ del_timer_sync(&ha->timer);
+ ha->timer_active = 0;
+}
+
+/**************************************************************************
+ * qla4xxx_mark_device_missing
+ * This routine marks a device missing and resets the relogin retry count.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * ddb_entry - Pointer to device database entry
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+void
+qla4xxx_mark_device_missing(scsi_qla_host_t * ha, ddb_entry_t * ddb_entry)
+{
+ atomic_set(&ddb_entry->state, DDB_STATE_MISSING);
+ DEBUG3(printk(KERN_INFO "scsi%d:%d:%d: index [%d] marked MISSING\n",
+ ha->host_no, ddb_entry->bus, ddb_entry->target,
+ ddb_entry->fw_ddb_index));
+}
+
+static inline srb_t *
+qla4xxx_get_new_srb(scsi_qla_host_t *ha, struct ddb_entry *ddb_entry,
+ struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ srb_t *srb;
+
+ srb = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
+ if (!srb)
+ return srb;
+
+ atomic_set(&srb->ref_count, 1);
+ srb->ha = ha;
+ srb->ddb = ddb_entry;
+ srb->cmd = cmd;
+ srb->flags = 0;
+ CMD_SP(cmd) = (void *)srb;
+ cmd->scsi_done = done;
+
+ return srb;
+}
+
+static void
+qla4xxx_srb_free_dma(scsi_qla_host_t *ha, srb_t *srb)
+{
+ struct scsi_cmnd *cmd = srb->cmd;
+
+ if (srb->flags & SRB_DMA_VALID) {
+ if (cmd->use_sg) {
+ dma_unmap_sg(&ha->pdev->dev, cmd->request_buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ } else if (cmd->request_bufflen) {
+ dma_unmap_single(&ha->pdev->dev, srb->dma_handle,
+ cmd->request_bufflen, cmd->sc_data_direction);
+ }
+ srb->flags &= ~SRB_DMA_VALID;
+ }
+ CMD_SP(cmd) = NULL;
+}
+
+void
+qla4xxx_srb_compl(scsi_qla_host_t *ha, srb_t *srb)
+{
+ struct scsi_cmnd *cmd = srb->cmd;
+
+ qla4xxx_srb_free_dma(ha, srb);
+
+ mempool_free(srb, ha->srb_mempool);
+
+ cmd->scsi_done(cmd);
+}
+
+/**************************************************************************
+ * qla4xxx_queuecommand
+ * This routine is invoked by Linux to send a SCSI command to the driver.
+ *
+ * Input:
+ * cmd - Pointer to Linux's SCSI command structure
+ * done_fn - Function that the driver calls to notify the SCSI mid-layer
+ * that the command has been processed.
+ *
+ * Remarks:
+ * The mid-level driver tries to ensure that queuecommand never gets
+ * invoked concurrently with itself or the interrupt handler (although
+ * the interrupt handler may call this routine as part of request-
+ * completion handling). Unfortunely, it sometimes calls the scheduler
+ * in interrupt context which is a big NO! NO!.
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+ struct ddb_entry *ddb_entry = (struct ddb_entry *)cmd->device->hostdata;
+ srb_t *srb;
+ int rval;
+
+ if (!ddb_entry) {
+ cmd->result = DID_NO_CONNECT << 16;
+ goto qc_fail_command;
+ }
+
+ if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
+ if (atomic_read(&ddb_entry->state) == DDB_STATE_DEAD) {
+ cmd->result = DID_NO_CONNECT << 16;
+ goto qc_fail_command;
+ }
+ goto qc_host_busy;
+ }
+
+ spin_unlock_irq(ha->host->host_lock);
+
+ srb = qla4xxx_get_new_srb(ha, ddb_entry, cmd, done);
+ if (!srb)
+ goto qc_host_busy_lock;
+
+ rval = qla4xxx_send_command_to_isp(ha, srb);
+ if (rval != QLA_SUCCESS)
+ goto qc_host_busy_free_sp;
+
+ spin_lock_irq(ha->host->host_lock);
+ return 0;
+
+qc_host_busy_free_sp:
+ qla4xxx_srb_free_dma(ha, srb);
+ mempool_free(srb, ha->srb_mempool);
+
+qc_host_busy_lock:
+ spin_lock_irq(ha->host->host_lock);
+
+qc_host_busy:
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+qc_fail_command:
+ done(cmd);
+
+ return 0;
+}
+
+/**************************************************************************
+ * qla4xxx_probe_adapter
+ * This routine will probe for Qlogic 4010 iSCSI host adapters.
+ * It returns the number of host adapters of a particular
+ * type that were found. It also initializes all data necessary for
+ * the driver. It is passed-in the host number, so that it
+ * knows where its first entry is in the scsi_hosts[] array.
+ *
+ * Input:
+ *
+ * Returns:
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int __devinit
+qla4xxx_probe_adapter(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int ret = -ENODEV, status;
+ struct Scsi_Host *host;
+ scsi_qla_host_t *ha;
+ uint8_t init_retry_count = 0;
+ unsigned long flags;
+
+ if (pci_enable_device(pdev))
+ return -1;
+
+ host = scsi_host_alloc(&qla4xxx_driver_template,
+ sizeof(scsi_qla_host_t));
+ if (host == NULL) {
+ printk(KERN_WARNING
+ "qla4xxx: Couldn't allocate host from scsi layer!\n");
+ goto probe_disable_device;
+ }
+
+ /* Clear our data area */
+ ha = (scsi_qla_host_t *) host->hostdata;
+ memset(ha, 0, sizeof(scsi_qla_host_t));
+
+ /* Save the information from PCI BIOS. */
+ ha->pdev = pdev;
+ ha->host = host;
+ ha->host_no = host->host_no;
+
+ /* Configure PCI I/O space. */
+ ret = qla4xxx_iospace_config(ha);
+ if (ret)
+ goto probe_failed;
+
+ ql4_printk(KERN_INFO, ha,
+ "Found an ISP%04x, irq %d, iobase 0x%p\n", pdev->device, pdev->irq,
+ ha->reg);
+
+ qla4xxx_config_dma_addressing(ha);
+
+ /* Initialize lists and spinlocks. */
+ INIT_LIST_HEAD(&ha->ddb_list);
+ INIT_LIST_HEAD(&ha->free_srb_q);
+
+ init_MUTEX(&ha->mbox_sem);
+ init_waitqueue_head(&ha->mailbox_wait_queue);
+
+ spin_lock_init(&ha->hardware_lock);
+ spin_lock_init(&ha->list_lock);
+
+ ha->dpc_pid = -1;
+ init_completion(&ha->dpc_inited);
+ init_completion(&ha->dpc_exited);
+
+ /* Verify iSCSI PCI Funcion Number */
+ if (IS_QLA4010(ha)) {
+ ha->function_number = ISP4010_ISCSI_FUNCTION;
+ } else if (IS_QLA4022(ha)) {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->function_number = (RD_REG_DWORD(&ha->reg->ctrl_status) &
+ CSR_PCI_FUNC_NUM_MASK) >> 8;
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+ if (PCI_FUNC(pdev->devfn) != ha->function_number) {
+ ql4_printk(KERN_WARNING, ha, "HA function number (0x%x) does "
+ "not match PCI function number (0x%x)\n",
+ ha->function_number, PCI_FUNC(pdev->devfn));
+ goto probe_disable_device;
+ }
+
+ /* Allocate dma buffers */
+ if (qla4xxx_mem_alloc(ha)) {
+ ql4_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to allocate memory for adapter\n");
+
+ ret = -ENOMEM;
+ goto probe_disable_device;
+ }
+
+ /*
+ * Initialize the Host adapter request/response queues and
+ * firmware
+ * NOTE: interrupts enabled upon successful completion
+ */
+ status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST);
+ while (status == QLA_ERROR && init_retry_count++ < MAX_INIT_RETRIES) {
+ DEBUG2(printk("scsi: %s: retrying adapter initialization "
+ "(%d)\n", __func__, init_retry_count));
+ qla4xxx_soft_reset(ha);
+ status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST);
+ }
+ if (status == QLA_ERROR) {
+ ql4_printk(KERN_WARNING, ha, "Failed to initialize adapter\n");
+
+ ret = -ENODEV;
+ goto probe_failed;
+ }
+
+ host->cmd_per_lun = 3;
+ host->max_channel = 0;
+ host->max_lun = MAX_LUNS - 1;
+ host->max_id = MAX_TARGETS;
+ host->unique_id = ha->instance;
+ host->max_cmd_len = IOCB_MAX_CDB_LEN;
+ host->can_queue = REQUEST_QUEUE_DEPTH + 128;
+/* FIXME */
+/* host->transportt = qla4xxx_transport_template; */
+
+ /* Startup the kernel thread for this host adapter. */
+ DEBUG2(printk("scsi: %s: Starting kernel thread for "
+ "qla4xxx_dpc\n", __func__));
+ ha->dpc_should_die = 0;
+ ha->dpc_pid = kernel_thread(qla4xxx_do_dpc, ha, 0);
+ if (ha->dpc_pid < 0) {
+ ql4_printk(KERN_WARNING, ha, "Unable to start DPC thread!\n");
+
+ ret = -ENODEV;
+ goto probe_failed;
+ }
+ wait_for_completion(&ha->dpc_inited);
+
+ ret = request_irq(pdev->irq, qla4xxx_intr_handler,
+ SA_INTERRUPT|SA_SHIRQ, "qla4xxx", ha);
+ if (ret) {
+ ql4_printk(KERN_WARNING, ha,
+ "Failed to reserve interrupt %d already in use.\n",
+ pdev->irq);
+ goto probe_failed;
+ }
+ set_bit(AF_IRQ_ATTACHED, &ha->flags);
+ host->irq = pdev->irq;
+ DEBUG(printk("scsi%d: irq %d attached\n", ha->host_no, ha->pdev->irq));
+
+ qla4xxx_enable_intrs(ha);
+
+ /* Start timer thread. */
+ qla4xxx_start_timer(ha, qla4xxx_timer, 1);
+
+ /* Insert new entry into the list of adapters. */
+ write_lock(&qla4xxx_hostlist_lock);
+ list_add_tail(&ha->list, &qla4xxx_hostlist);
+ write_unlock(&qla4xxx_hostlist_lock);
+
+ qla4xxx_display_config();
+
+ set_bit(AF_INIT_DONE, &ha->flags);
+ qla4xxx_hba_count++;
+
+ pci_set_drvdata(pdev, ha);
+
+ ret = scsi_add_host(host, &pdev->dev);
+ if (ret)
+ goto probe_failed;
+
+/*FIXME*/
+/* qla4x00_alloc_sysfs_attr(ha); */
+
+ printk(KERN_INFO
+ " QLogic iSCSI HBA Driver version: %s\n"
+ " QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
+ qla4xxx_version_str, ha->pdev->device, pci_name(ha->pdev),
+ ha->host_no, ha->firmware_version[0], ha->firmware_version[1],
+ ha->patch_number, ha->build_number);
+
+ scsi_scan_host(host);
+
+ return 0;
+
+probe_failed:
+ qla4xxx_free_adapter(ha);
+
+probe_disable_device:
+ pci_disable_device(pdev);
+
+ return ret;
+}
+
+/**************************************************************************
+ * qla4xxx_remove_adapter
+ *
+ * Input:
+ * pci_dev - PCI device pointer
+ *
+ * Returns:
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static void __devexit
+qla4xxx_remove_adapter(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *ha;
+
+ ha = pci_get_drvdata(pdev);
+ write_lock(&qla4xxx_hostlist_lock);
+ list_del_init(&ha->list);
+ write_unlock(&qla4xxx_hostlist_lock);
+
+ scsi_remove_host(ha->host);
+
+ qla4xxx_free_adapter(ha);
+
+ scsi_host_put(ha->host);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+/**************************************************************************
+ * qla4xxx_free_adapter
+ *
+ * Input:
+ * pci_dev - PCI device pointer
+ *
+ * Returns:
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static void
+qla4xxx_free_adapter(scsi_qla_host_t * ha)
+{
+ int ret;
+ unsigned long flags;
+
+ set_bit(AF_HA_GOING_AWAY, &ha->flags);
+
+ /* Deregister with the iSNS Server */
+ if (test_bit(ISNS_FLAG_ISNS_ENABLED_IN_ISP, &ha->isns_flags)) {
+ qla4xxx_isns_disable(ha);
+ }
+
+ if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) {
+ /* Turn-off interrupts on the card. */
+ qla4xxx_disable_intrs(ha);
+ }
+
+ /* Issue Soft Reset to put firmware in unknown state */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(&ha->reg->ctrl_status, SET_RMASK(CSR_SOFT_RESET));
+ PCI_POSTING(&ha->reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Remove timer thread, if present */
+ if (ha->timer_active)
+ qla4xxx_stop_timer(ha);
+
+ /* Kill the kernel thread for this host */
+ if (ha->dpc_pid >= 0) {
+ ha->dpc_should_die = 1;
+ wmb();
+ ret = kill_proc(ha->dpc_pid, SIGHUP, 1);
+ if (ret) {
+ ql4_printk(KERN_ERR, ha,
+ "Unable to signal DPC thread -- (%d)\n", ret);
+ } else
+ wait_for_completion(&ha->dpc_exited);
+ }
+
+ /* free extra memory */
+ qla4xxx_mem_free(ha);
+
+ /* Detach interrupts */
+ if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags))
+ free_irq(ha->pdev->irq, ha);
+
+ pci_disable_device(ha->pdev);
+}
+
+/**************************************************************************
+ * qla4xxx_iospace_config
+ * This routine
+ *
+ * Input:
+ *
+ * Returns:
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_iospace_config(scsi_qla_host_t * ha)
+{
+ unsigned long pio, pio_len, pio_flags;
+ unsigned long mmio, mmio_len, mmio_flags;
+
+ /* We only need PIO for Flash operations on ISP2312 v2 chips. */
+ pio = pci_resource_start(ha->pdev, 0);
+ pio_len = pci_resource_len(ha->pdev, 0);
+ pio_flags = pci_resource_flags(ha->pdev, 0);
+ if (pio_flags & IORESOURCE_IO) {
+ if (pio_len < MIN_IOBASE_LEN) {
+ ql4_printk(KERN_WARNING, ha,
+ "Invalid PCI I/O region size (%s)...\n",
+ pci_name(ha->pdev));
+ pio = 0;
+ }
+ } else {
+ ql4_printk(KERN_WARNING, ha,
+ "region #0 not a PIO resource (%s)...\n",
+ pci_name(ha->pdev));
+ pio = 0;
+ }
+
+ /* Use MMIO operations for all accesses. */
+ mmio = pci_resource_start(ha->pdev, 1);
+ mmio_len = pci_resource_len(ha->pdev, 1);
+ mmio_flags = pci_resource_flags(ha->pdev, 1);
+
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ ql4_printk(KERN_ERR, ha,
+ "region #0 not an MMIO resource (%s), aborting\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+ if (mmio_len < MIN_IOBASE_LEN) {
+ ql4_printk(KERN_ERR, ha,
+ "Invalid PCI mem region size (%s), aborting\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ if (pci_request_regions(ha->pdev, DRIVER_NAME)) {
+ ql4_printk(KERN_WARNING, ha,
+ "Failed to reserve PIO/MMIO regions (%s)\n",
+ pci_name(ha->pdev));
+
+ goto iospace_error_exit;
+ }
+
+ ha->pio_address = pio;
+ ha->pio_length = pio_len;
+ ha->reg = ioremap(mmio, MIN_IOBASE_LEN);
+ if (!ha->reg) {
+ ql4_printk(KERN_ERR, ha,
+ "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev));
+
+ goto iospace_error_exit;
+ }
+
+ return 0;
+
+iospace_error_exit:
+ return -ENOMEM;
+}
+
+/**************************************************************************
+ * qla4xxx_display_config
+ * This routine displays the configuration information to be used in
+ * modules.conf.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure
+ *
+ * Output:
+ * None
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+void
+qla4xxx_display_config(void)
+{
+ scsi_qla_host_t *ha, *htemp;
+
+ read_lock(&qla4xxx_hostlist_lock);
+ list_for_each_entry_safe(ha, htemp, &qla4xxx_hostlist, list) {
+ /* Display the M.A.C. Address for adapter */
+ printk(KERN_INFO
+ "scsi-qla%ld-mac=%02x%02x%02x%02x%02x%02x\\;\n",
+ ha->instance, ha->my_mac[0], ha->my_mac[1], ha->my_mac[2],
+ ha->my_mac[3], ha->my_mac[4], ha->my_mac[5]);
+ }
+ read_unlock(&qla4xxx_hostlist_lock);
+}
+
+/**
+ * qla4xxx_config_dma_addressing() - Configure OS DMA addressing method.
+ * @ha: HA context
+ *
+ * At exit, the @ha's flags.enable_64bit_addressing set to indicated
+ * supported addressing method.
+ */
+void
+qla4xxx_config_dma_addressing(scsi_qla_host_t * ha)
+{
+ /* Update our PCI device dma_mask for full 64 bit mask */
+ if (pci_set_dma_mask(ha->pdev, DMA_64BIT_MASK) == 0) {
+ if (pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) {
+ ql4_printk(KERN_DEBUG, ha,
+ "Failed to set 64 bit PCI consistent mask; "
+ "using 32 bit.\n");
+ pci_set_consistent_dma_mask(ha->pdev, DMA_32BIT_MASK);
+ }
+ } else
+ pci_set_dma_mask(ha->pdev, DMA_32BIT_MASK);
+}
+
+/**************************************************************************
+ * qla4xxx_mem_alloc
+ * This routine allocates memory use by the adapter.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully allocated adapter memory
+ * QLA_ERROR - Failed to allocate adapter memory
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_mem_alloc(scsi_qla_host_t * ha)
+{
+ unsigned long align;
+
+ /* Allocate contiguous block of DMA memory for queues. */
+ ha->queues_len = ((REQUEST_QUEUE_DEPTH * QUEUE_SIZE) +
+ (RESPONSE_QUEUE_DEPTH * QUEUE_SIZE) + sizeof(shadow_regs_t) +
+ MEM_ALIGN_VALUE + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+ ha->queues = dma_alloc_coherent(&ha->pdev->dev, ha->queues_len,
+ &ha->queues_dma, GFP_KERNEL);
+ if (ha->queues == NULL) {
+ ql4_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - queues.\n");
+
+ goto mem_alloc_error_exit;
+ }
+ memset(ha->queues, 0, ha->queues_len);
+
+ /*
+ * As per RISC alignment requirements -- the bus-address must be a
+ * multiple of the request-ring size (in bytes).
+ */
+ align = 0;
+ if ((unsigned long)ha->queues_dma & (MEM_ALIGN_VALUE - 1))
+ align = MEM_ALIGN_VALUE -
+ ((unsigned long)ha->queues_dma & (MEM_ALIGN_VALUE - 1));
+
+ /* Update request and response queue pointers. */
+ ha->request_dma = ha->queues_dma + align;
+ ha->request_ring = (QUEUE_ENTRY *) (ha->queues + align);
+ ha->response_dma = ha->queues_dma + align +
+ (REQUEST_QUEUE_DEPTH * QUEUE_SIZE);
+ ha->response_ring = (QUEUE_ENTRY *) (ha->queues + align +
+ (REQUEST_QUEUE_DEPTH * QUEUE_SIZE));
+ ha->shadow_regs_dma = ha->queues_dma + align +
+ (REQUEST_QUEUE_DEPTH * QUEUE_SIZE) +
+ (RESPONSE_QUEUE_DEPTH * QUEUE_SIZE);
+ ha->shadow_regs = (shadow_regs_t *) (ha->queues + align +
+ (REQUEST_QUEUE_DEPTH * QUEUE_SIZE) +
+ (RESPONSE_QUEUE_DEPTH * QUEUE_SIZE));
+
+ /* Allocate iSNS Discovered Target Database. */
+ ha->isns_disc_tgt_database_size = sizeof(ISNS_DISCOVERED_TARGET) *
+ MAX_ISNS_DISCOVERED_TARGETS;
+ ha->isns_disc_tgt_databasev = dma_alloc_coherent(&ha->pdev->dev,
+ ha->isns_disc_tgt_database_size, &ha->isns_disc_tgt_databasep,
+ GFP_KERNEL);
+ if (ha->isns_disc_tgt_databasev == NULL) {
+ ql4_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - iSNS DB.\n");
+
+ goto mem_alloc_error_exit;
+ }
+ memset(ha->isns_disc_tgt_databasev, 0, ha->isns_disc_tgt_database_size);
+
+ /* Allocate memory for srb pool. */
+ ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab,
+ mempool_free_slab, srb_cachep);
+ if (ha->srb_mempool == NULL) {
+ ql4_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - SRB Pool.\n");
+
+ goto mem_alloc_error_exit;
+ }
+
+ return QLA_SUCCESS;
+
+mem_alloc_error_exit:
+ qla4xxx_mem_free(ha);
+ return QLA_ERROR;
+}
+
+/**************************************************************************
+ * qla4xxx_mem_free
+ * This routine frees adapter allocated memory
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static void
+qla4xxx_mem_free(scsi_qla_host_t * ha)
+{
+ if (ha->queues)
+ dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues,
+ ha->queues_dma);
+
+ ha->queues_len = 0;
+ ha->queues = NULL;
+ ha->queues_dma = 0;
+ ha->request_ring = NULL;
+ ha->request_dma = 0;
+ ha->response_ring = NULL;
+ ha->response_dma = 0;
+ ha->shadow_regs = NULL;
+ ha->shadow_regs_dma = 0;
+
+ if (ha->isns_disc_tgt_databasev)
+ dma_free_coherent(&ha->pdev->dev,
+ ha->isns_disc_tgt_database_size,
+ ha->isns_disc_tgt_databasev, ha->isns_disc_tgt_databasep);
+
+ ha->isns_disc_tgt_database_size = 0;
+ ha->isns_disc_tgt_databasev = 0;
+ ha->isns_disc_tgt_databasep = 0;
+
+ /* Free srb pool. */
+ if (ha->srb_mempool)
+ mempool_destroy(ha->srb_mempool);
+
+ ha->srb_mempool = NULL;
+
+ /* Free ddb list. */
+ if (!list_empty(&ha->ddb_list))
+ qla4xxx_free_ddb_list(ha);
+
+ /* release io space registers */
+ if (ha->reg)
+ iounmap(ha->reg);
+ pci_release_regions(ha->pdev);
+}
+
+/*FIXME */
+#ifdef DG_FIX_ME
+/*
+ * qla4xxx_add_lun
+ * Adds LUN to device database
+ *
+ * Input:
+ * ddb: ddb structure pointer.
+ * lun: LUN number.
+ *
+ * Context:
+ * Kernel context.
+ */
+static lun_entry_t *
+qla4xxx_add_lun(struct ddb_entry *ddb, uint16_t lun)
+{
+ int found;
+ lun_entry_t *lp;
+
+ if (ddb == NULL) {
+ DEBUG2(printk
+ ("scsi: %s: Unable to add lun to NULL port\n",
+ __func__));
+ return (NULL);
+ }
+
+ /* Allocate LUN if not already allocated. */
+ found = 0;
+ list_for_each_entry(lp, &ddb->luns, list) {
+ if (lp->lun == lun) {
+ found++;
+ break;
+ }
+ }
+ if (found) {
+ return (lp);
+ }
+
+ lp = kmalloc(sizeof(lun_entry_t), GFP_ATOMIC);
+ if (lp == NULL) {
+ printk(KERN_WARNING
+ "%s(): Memory Allocation failed - LUN\n", __func__);
+ return (NULL);
+ }
+
+ /* Setup LUN structure. */
+ memset(lp, 0, sizeof(lun_entry_t));
+ DEBUG2(printk("Allocating Lun %d @ %p \n", lun, lp);
+ )
+ lp->lun = lun;
+ lp->ddb = ddb;
+ lp->lun_state = LS_LUN_READY;
+ spin_lock_init(&lp->lun_lock);
+ list_add_tail(&lp->list, &ddb->luns);
+ return (lp);
+}
+#endif
+
+static int
+qla4xxx_slave_alloc(struct scsi_device *sdev)
+{
+ struct ddb_entry *ddb;
+ scsi_qla_host_t *ha = to_qla_host(sdev->host);
+
+ ddb = qla4xxx_lookup_ddb_by_fw_index(ha, sdev->id);
+ if (!ddb)
+ return -ENXIO;
+
+ sdev->hostdata = ddb;
+#ifdef DG_FIX_ME
+ /* Needed for target resets because it translate to lun resets */
+ qla4xxx_add_lun(ddb, sdev->lun);
+#endif
+ return 0;
+}
+
+static void
+qla4xxx_slave_destroy(struct scsi_device *sdev)
+{
+ sdev->hostdata = NULL;
+}
+
+static int
+qla4xxx_slave_configure(struct scsi_device *sdev)
+{
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, 32);
+ else
+ scsi_deactivate_tcq(sdev, 32);
+
+ //iscsi_port
+
+ return 0;
+}
+
+/**************************************************************************
+ * del_from_active_array
+ * This routine removes and returns the srb at the specified index
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * index - index into to the active_array
+ *
+ * Returns:
+ * Pointer to corresponding SCSI Request Block
+ *
+ * Context:
+ * Kernel/Interrupt context.
+ **************************************************************************/
+srb_t *
+del_from_active_array(scsi_qla_host_t * ha, uint32_t index)
+{
+ srb_t *srb = NULL;
+
+ /* validate handle and remove from active array */
+ if (index >= MAX_SRBS)
+ return srb;
+
+ srb = ha->active_srb_array[index];
+ ha->active_srb_array[index] = 0;
+ if (!srb)
+ return srb;
+
+ /* update counters */
+ if (srb->flags & SRB_DMA_VALID) {
+ ha->req_q_count += srb->iocb_cnt;
+ ha->iocb_cnt -= srb->iocb_cnt;
+ if (ha->active_srb_count)
+ ha->active_srb_count--;
+ /* FIXMEdg: Is this needed ???? */
+ srb->active_array_index = INVALID_ENTRY;
+ if (srb->cmd)
+ srb->cmd->host_scribble = NULL;
+ }
+ return srb;
+}
+
+/**************************************************************************
+ * qla4xxx_timer
+ * This routine is scheduled to be invoked every second to search for
+ * work to do.
+ *
+ * Input:
+ * p - Pointer to host adapter structure.
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Interrupt context.
+ **************************************************************************/
+void
+qla4xxx_timer(scsi_qla_host_t *ha)
+{
+ ddb_entry_t *ddb_entry, *dtemp;
+ int start_dpc = 0;
+
+ /* Search for relogin's to time-out and port down retry. */
+ list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) {
+ /*
+ * First check to see if the device has exhausted the port
+ * down retry count.
+ */
+ if (atomic_read(&ddb_entry->state) == DDB_STATE_MISSING) {
+ if (atomic_read(&ddb_entry->port_down_timer) == 0)
+ continue;
+
+ if (atomic_dec_and_test(&ddb_entry->port_down_timer)) {
+ DEBUG2(printk("scsi%ld: %s: index [%d] port "
+ "down retry count of (%d) secs exhausted, "
+ "marking device DEAD.\n", ha->host_no,
+ __func__, ddb_entry->fw_ddb_index,
+ ha->port_down_retry_count));
+
+ atomic_set(&ddb_entry->state,
+ DDB_STATE_DEAD);
+
+ DEBUG2(printk("scsi%ld: %s: index [%d] marked "
+ "DEAD\n", ha->host_no, __func__,
+ ddb_entry->fw_ddb_index));
+ start_dpc++;
+ }
+ }
+
+ /* Count down time between sending relogins */
+ if (ADAPTER_UP(ha) &&
+ !test_bit(DF_RELOGIN, &ddb_entry->flags) &&
+ atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
+ if (atomic_read(&ddb_entry->retry_relogin_timer) !=
+ INVALID_ENTRY) {
+ if (atomic_read(&ddb_entry->retry_relogin_timer)
+ == 0) {
+ atomic_set(
+ &ddb_entry->retry_relogin_timer,
+ INVALID_ENTRY);
+ set_bit(DPC_RELOGIN_DEVICE,
+ &ha->dpc_flags);
+ set_bit(DF_RELOGIN, &ddb_entry->flags);
+ DEBUG2(printk("scsi%ld: %s: index [%d] "
+ "login device\n", ha->host_no,
+ __func__, ddb_entry->fw_ddb_index));
+ } else
+ atomic_dec(
+ &ddb_entry->retry_relogin_timer);
+ }
+ }
+
+ /* Wait for relogin to timeout */
+ if (atomic_read(&ddb_entry->relogin_timer) &&
+ (atomic_dec_and_test(&ddb_entry->relogin_timer) != 0)) {
+ /*
+ * If the relogin times out and the device is
+ * still NOT ONLINE then try and relogin again.
+ */
+ if (atomic_read(&ddb_entry->state) !=
+ DDB_STATE_ONLINE &&
+ ddb_entry->fw_ddb_device_state ==
+ DDB_DS_SESSION_FAILED) {
+ /* Reset retry relogin timer */
+ atomic_inc(&ddb_entry->relogin_retry_count);
+ DEBUG2(printk("scsi%ld: index[%d] relogin "
+ "timed out-retrying relogin (%d)\n",
+ ha->host_no, ddb_entry->fw_ddb_index,
+ atomic_read(
+ &ddb_entry->relogin_retry_count)));
+ start_dpc++;
+ DEBUG(printk("scsi%ld:%d:%d: index [%d] "
+ "initate relogin after %d seconds\n",
+ ha->host_no, ddb_entry->bus,
+ ddb_entry->target, ddb_entry->fw_ddb_index,
+ ddb_entry->default_time2wait + 4));
+
+ atomic_set(&ddb_entry->retry_relogin_timer,
+ ddb_entry->default_time2wait + 4);
+ }
+ }
+ }
+
+ /* Check for heartbeat interval. */
+ if (ha->firmware_options & FWOPT_HEARTBEAT_ENABLE &&
+ ha->heartbeat_interval != 0) {
+ ha->seconds_since_last_heartbeat++;
+ if (ha->seconds_since_last_heartbeat >
+ ha->heartbeat_interval + 2)
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+ }
+
+ /* Check for iSNS actions. */
+ if (test_bit(ISNS_FLAG_RESTART_SERVICE, &ha->isns_flags)) {
+ if (atomic_read(&ha->isns_restart_timer)) {
+ if (!atomic_dec_and_test(&ha->isns_restart_timer) &&
+ test_bit(ISNS_FLAG_ISNS_SRV_ENABLED,
+ &ha->isns_flags) &&
+ !IPAddrIsZero(ha->isns_ip_address) &&
+ ha->isns_server_port_number) {
+ set_bit(DPC_ISNS_RESTART_COMPLETION,
+ &ha->dpc_flags);
+ }
+ } else
+ clear_bit(ISNS_FLAG_RESTART_SERVICE, &ha->isns_flags);
+ }
+
+ /* Wakeup the dpc routine for this adapter, if needed. */
+ if ((start_dpc ||
+ test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
+ test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags) ||
+ test_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags) ||
+ test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) ||
+ test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
+ test_bit(DPC_ISNS_RESTART, &ha->dpc_flags) ||
+ test_bit(DPC_ISNS_RESTART_COMPLETION, &ha->dpc_flags) ||
+ test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) ||
+ test_bit(DPC_AEN, &ha->dpc_flags)) &&
+ !test_bit(AF_DPC_SCHEDULED, &ha->flags) &&
+ !ha->dpc_active && ha->dpc_wait) {
+ DEBUG2(printk("scsi%ld: %s: scheduling dpc routine - dpc flags "
+ "= 0x%lx\n", ha->host_no, __func__, ha->dpc_flags));
+ set_bit(AF_DPC_SCHEDULED, &ha->flags);
+ up(ha->dpc_wait);
+ }
+
+ /* Reschedule timer thread to call us back in one second */
+ mod_timer(&ha->timer, jiffies + HZ);
+
+ DEBUG2(ha->seconds_since_last_intr++;)
+}
+
+int
+qla4xxx_check_for_disable_hba_reset(scsi_qla_host_t * ha)
+{
+#if DISABLE_HBA_RESETS
+ clear_bit(DPC_RESET_HA, &ha->dpc_flags);
+ clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
+ clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags);
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+/**************************************************************************
+ * qla4xxx_do_dpc
+ * This routine is a task that is schedule by the interrupt handler
+ * to perform the background processing for interrupts. We put it
+ * on a task queue that is consumed whenever the scheduler runs; that's
+ * so you can do anything (i.e. put the process to sleep etc). In fact,
+ * the mid-level tries to sleep when it reaches the driver threshold
+ * "host->can_queue". This can cause a panic if we were in our interrupt
+ * code.
+ *
+ * Input:
+ * p - Pointer to host adapter structure.
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int qla4xxx_do_dpc(void *data)
+{
+ DECLARE_MUTEX_LOCKED(sem);
+ scsi_qla_host_t *ha = (scsi_qla_host_t *) data;
+ ddb_entry_t *ddb_entry, *dtemp;
+
+ lock_kernel();
+
+ daemonize("qla4xxx_%d_dpc", ha->host_no);
+ allow_signal(SIGHUP);
+
+ ha->dpc_wait = &sem;
+
+ set_user_nice(current, -20);
+
+ unlock_kernel();
+
+ complete(&ha->dpc_inited);
+
+ while (1) {
+ DEBUG2(printk("scsi%ld: %s: DPC handler sleeping.\n",
+ ha->host_no, __func__));
+
+ if (down_interruptible(&sem))
+ break;
+
+ if (ha->dpc_should_die)
+ break;
+
+ DEBUG2(printk("scsi%ld: %s: DPC handler waking up.\n",
+ ha->host_no, __func__));
+
+ DEBUG2(printk("scsi%ld: %s: ha->flags = 0x%08lx\n", ha->host_no,
+ __func__, ha->flags));
+ DEBUG2(printk("scsi%ld: %s: ha->dpc_flags = 0x%08lx\n",
+ ha->host_no, __func__, ha->dpc_flags));
+
+ /* Initialization not yet finished. Don't do anything yet. */
+ if (!test_bit(AF_INIT_DONE, &ha->flags) || ha->dpc_active)
+ continue;
+
+ ha->dpc_active = 1;
+ clear_bit(AF_DPC_SCHEDULED, &ha->flags);
+
+ if (ADAPTER_UP(ha) ||
+ test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
+ test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
+ test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) {
+ if (qla4xxx_check_for_disable_hba_reset(ha)) {
+ if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST,
+ &ha->dpc_flags))
+ /*
+ * dg 09/23 Never initialize ddb list
+ * once we up and running
+ * qla4xxx_recover_adapter(ha,
+ * REBUILD_DDB_LIST);
+ */
+ qla4xxx_recover_adapter(ha,
+ PRESERVE_DDB_LIST);
+
+ if (test_bit(DPC_RESET_HA, &ha->dpc_flags))
+ qla4xxx_recover_adapter(ha,
+ PRESERVE_DDB_LIST);
+
+ if (test_bit(DPC_RESET_HA_INTR,
+ &ha->dpc_flags)) {
+ uint8_t wait_time = RESET_INTR_TOV;
+ unsigned long flags = 0;
+
+ qla4xxx_flush_active_srbs(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock,
+ flags);
+ while ((RD_REG_DWORD(
+ ISP_PORT_STATUS(ha)) &
+ PSR_INIT_COMPLETE) == 0) {
+ if (wait_time-- == 0)
+ break;
+
+ spin_unlock_irqrestore(
+ &ha->hardware_lock, flags);
+
+ set_current_state(
+ TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 * HZ);
+
+ spin_lock_irqsave(
+ &ha->hardware_lock, flags);
+ }
+ spin_unlock_irqrestore(
+ &ha->hardware_lock, flags);
+
+ if (wait_time == 0)
+ DEBUG2(printk(
+ "scsi%ld: %s: IC bit not "
+ "set\n", ha->host_no,
+ __func__));
+
+ if (!test_bit(AF_HA_GOING_AWAY,
+ &ha->flags)) {
+ qla4xxx_initialize_adapter(ha,
+ PRESERVE_DDB_LIST);
+ clear_bit(DPC_RESET_HA_INTR,
+ &ha->dpc_flags);
+ qla4xxx_enable_intrs(ha);
+ }
+ }
+ }
+ }
+
+ /* ---- process AEN? --- */
+ if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags))
+ qla4xxx_process_aen(ha, PROCESS_ALL_AENS);
+
+ /* ---- Get DHCP IP Address? --- */
+ if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags))
+ qla4xxx_get_dhcp_ip_address(ha);
+
+ /* ---- relogin device? --- */
+ if (ADAPTER_UP(ha) &&
+ test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) {
+ list_for_each_entry_safe(ddb_entry, dtemp,
+ &ha->ddb_list, list) {
+ if (test_and_clear_bit(DF_RELOGIN,
+ &ddb_entry->flags) &&
+ atomic_read(&ddb_entry->state) !=
+ DDB_STATE_ONLINE) {
+ qla4xxx_relogin_device(ha, ddb_entry);
+ }
+ }
+ }
+
+ /* ---- restart iSNS server? --- */
+ if (ADAPTER_UP(ha) && test_and_clear_bit(DPC_ISNS_RESTART,
+ &ha->dpc_flags))
+ qla4xxx_isns_restart_service(ha);
+
+ if (ADAPTER_UP(ha) &&
+ test_and_clear_bit(DPC_ISNS_RESTART_COMPLETION,
+ &ha->dpc_flags)) {
+ uint32_t ip_addr = 0;
+
+ IPAddr2Uint32(ha->isns_ip_address, &ip_addr);
+
+ if (qla4xxx_isns_restart_service_completion(ha,
+ ip_addr, ha->isns_server_port_number) !=
+ QLA_SUCCESS) {
+ DEBUG2(printk(KERN_WARNING "scsi%ld: %s: "
+ "restart service failed\n", ha->host_no,
+ __func__));
+ }
+ }
+
+ ha->dpc_active = 0;
+ }
+
+ /* Make sure that nobody tries to wake us up again. */
+ ha->dpc_wait = NULL;
+ ha->dpc_active = 0;
+
+ complete_and_exit(&ha->dpc_exited, 0);
+}
+
+/**************************************************************************
+ * qla4010_soft_reset
+ * This routine performs a SOFT RESET.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully reset the firmware
+ * QLA_ERROR - Failed to reset the firmware
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4010_soft_reset(scsi_qla_host_t * ha)
+{
+ uint32_t max_wait_time;
+ unsigned long flags = 0;
+ int status = QLA_ERROR;
+ uint32_t ctrl_status;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /*
+ * If the SCSI Reset Interrupt bit is set, clear it.
+ * Otherwise, the Soft Reset won't work.
+ */
+ ctrl_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+ if ((ctrl_status & CSR_SCSI_RESET_INTR) != 0)
+ WRT_REG_DWORD(&ha->reg->ctrl_status,
+ SET_RMASK(CSR_SCSI_RESET_INTR));
+
+ /* Issue Soft Reset */
+ WRT_REG_DWORD(&ha->reg->ctrl_status, SET_RMASK(CSR_SOFT_RESET));
+ PCI_POSTING(&ha->reg->ctrl_status);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Wait until the Network Reset Intr bit is cleared */
+ max_wait_time = RESET_INTR_TOV;
+ do {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ctrl_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if ((ctrl_status & CSR_NET_RESET_INTR) == 0)
+ break;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 * HZ);
+ } while ((--max_wait_time));
+
+ if ((ctrl_status & CSR_NET_RESET_INTR) != 0) {
+ DEBUG2(printk(KERN_WARNING
+ "scsi%ld: Network Reset Intr not cleared by Network "
+ "function, clearing it now!\n", ha->host_no));
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(&ha->reg->ctrl_status,
+ SET_RMASK(CSR_NET_RESET_INTR));
+ PCI_POSTING(&ha->reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+
+ /* Wait until the firmware tells us the Soft Reset is done */
+ max_wait_time = SOFT_RESET_TOV;
+ do {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ctrl_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if ((ctrl_status & CSR_SOFT_RESET) == 0) {
+ status = QLA_SUCCESS;
+ break;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 * HZ);
+ } while ((--max_wait_time));
+
+ /*
+ * Also, make sure that the SCSI Reset Interrupt bit has been cleared
+ * after the soft reset has taken place.
+ */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ctrl_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+ if ((ctrl_status & CSR_SCSI_RESET_INTR) != 0) {
+ WRT_REG_DWORD(&ha->reg->ctrl_status,
+ SET_RMASK(CSR_SCSI_RESET_INTR));
+ PCI_POSTING(&ha->reg->ctrl_status);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* If soft reset fails then most probably the bios on other
+ * function is also enabled.
+ * Since the initialization is sequential the other fn
+ * wont be able to acknowledge the soft reset.
+ * Issue a force soft reset to workaround this scenario.
+ */
+ if (max_wait_time == 0) {
+ /* Issue Force Soft Reset */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(&ha->reg->ctrl_status,
+ SET_RMASK(CSR_FORCE_SOFT_RESET));
+ PCI_POSTING(&ha->reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ /* Wait until the firmware tells us the Soft Reset is done */
+ max_wait_time = SOFT_RESET_TOV;
+ do {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ctrl_status = RD_REG_DWORD(&ha->reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if ((ctrl_status & CSR_FORCE_SOFT_RESET) == 0) {
+ status = QLA_SUCCESS;
+ break;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 * HZ);
+ } while ((--max_wait_time));
+ }
+
+ return (status);
+}
+
+/**************************************************************************
+ * qla4xxx_topcat_reset
+ * This routine performs a HARD RESET of the TopCat chip.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully reset the firmware
+ * QLA_ERROR - Failed to reset the firmware
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_topcat_reset(scsi_qla_host_t * ha)
+{
+ unsigned long flags;
+
+ QL4XXX_LOCK_NVRAM(ha);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(ISP_GP_OUT(ha), SET_RMASK(GPOR_TOPCAT_RESET));
+ PCI_POSTING(ISP_GP_OUT(ha));
+ do {
+ mdelay(1);
+ } while (0);
+ WRT_REG_DWORD(ISP_GP_OUT(ha), CLR_RMASK(GPOR_TOPCAT_RESET));
+ PCI_POSTING(ISP_GP_OUT(ha));
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ do {
+ mdelay(2523);
+ } while (0);
+ QL4XXX_UNLOCK_NVRAM(ha);
+ return (QLA_SUCCESS);
+}
+
+/**************************************************************************
+ * qla4xxx_soft_reset
+ * This routine performs a SOFT RESET.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully reset the firmware
+ * QLA_ERROR - Failed to reset the firmware
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_soft_reset(scsi_qla_host_t * ha)
+{
+
+ DEBUG2(printk(KERN_WARNING "scsi%ld: %s: chip reset!\n", ha->host_no,
+ __func__));
+ if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) {
+ int status = QLA_ERROR;
+
+ if (qla4010_soft_reset(ha) == QLA_SUCCESS) {
+ if (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) {
+ if (qla4010_soft_reset(ha) == QLA_SUCCESS) {
+ status = QLA_SUCCESS;
+ }
+ }
+ }
+ return (status);
+ } else
+ return qla4010_soft_reset(ha);
+}
+
+/**************************************************************************
+ * qla4xxx_hard_reset
+ * This routine performs a HARD RESET.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully reset the firmware
+ * QLA_ERROR - Failed to reset the firmware
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_hard_reset(scsi_qla_host_t * ha)
+{
+ /* The QLA4010 really doesn't have an equivalent to a hard reset */
+ qla4xxx_flush_active_srbs(ha);
+ if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) {
+ int status = QLA_ERROR;
+
+ if (qla4010_soft_reset(ha) == QLA_SUCCESS) {
+ if (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) {
+ if (qla4010_soft_reset(ha) == QLA_SUCCESS) {
+ status = QLA_SUCCESS;
+ }
+ }
+ }
+ return status;
+ } else
+ return qla4010_soft_reset(ha);
+}
+
+/**************************************************************************
+ * qla4xxx_cmd_wait
+ * This routine stalls the driver until all outstanding commands are
+ * returned.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Remarks:
+ * Caller must release the Hardware Lock prior to calling this routine.
+ *
+ * Returns:
+ * QLA_SUCCESS - All outstanding commands completed
+ * QLA_ERROR - All outstanding commands did not complete
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_cmd_wait(scsi_qla_host_t * ha)
+{
+ uint32_t index = 0;
+ int stat = QLA_SUCCESS;
+ unsigned long flags;
+ int wait_cnt = WAIT_CMD_TOV; /*
+ * Initialized for 30 seconds as we
+ * expect all commands to retuned
+ * ASAP.
+ */
+
+ while (wait_cnt) {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ /* Find a command that hasn't completed. */
+ for (index = 1; index < MAX_SRBS; index++) {
+ if (ha->active_srb_array[index] != NULL)
+ break;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* If No Commands are pending, wait is complete */
+ if (index == MAX_SRBS) {
+ break;
+ }
+
+ /* If we timed out on waiting for commands to come back
+ * return ERROR.
+ */
+ wait_cnt--;
+ if (wait_cnt == 0)
+ stat = QLA_ERROR;
+ else {
+ /* sleep a second */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 * HZ);
+ }
+ } /* End of While (wait_cnt) */
+
+ return (stat);
+}
+
+/**************************************************************************
+ * qla4xxx_recover_adapter
+ * This routine recovers that adapter from a fatal state.
+ *
+ * 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 recovered adapter
+ * QLA_ERROR - Failed to recover adapter
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_recover_adapter(scsi_qla_host_t * ha, uint8_t renew_ddb_list)
+{
+ int status = QLA_SUCCESS;
+
+ /* Stall incoming I/O until we are done */
+ clear_bit(AF_ONLINE, &ha->flags);
+ DEBUG2(printk("scsi%ld: %s calling qla4xxx_cmd_wait\n", ha->host_no,
+ __func__));
+
+ /* Wait for outstanding commands to complete.
+ * Stalls the driver for max 30 secs
+ */
+ status = qla4xxx_cmd_wait(ha);
+
+ qla4xxx_disable_intrs(ha);
+
+ /* Flush any pending ddb changed AENs */
+ qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+
+ /* Reset the firmware. If successful, function
+ * returns with ISP interrupts enabled.
+ */
+ if (status == QLA_SUCCESS) {
+ DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n",
+ ha->host_no, __func__));
+ status = qla4xxx_soft_reset(ha);
+ }
+ /* FIXMEkaren: Do we want to keep interrupts enabled and process
+ AENs after soft reset */
+
+ /* If firmware (SOFT) reset failed, or if all outstanding
+ * commands have not returned, then do a HARD reset.
+ */
+ if (status == QLA_ERROR) {
+ DEBUG2(printk("scsi%ld: %s - Performing hard reset..\n",
+ ha->host_no, __func__));
+ status = qla4xxx_hard_reset(ha);
+ }
+
+ /* Flush any pending ddb changed AENs */
+ qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+
+ /* Re-initialize firmware. If successful, function returns
+ * with ISP interrupts enabled */
+ if (status == QLA_SUCCESS) {
+ DEBUG2(printk("scsi%ld: %s - Initializing adapter..\n",
+ ha->host_no, __func__));
+
+ /* If successful, AF_ONLINE flag set in
+ * qla4xxx_initialize_adapter */
+ status = qla4xxx_initialize_adapter(ha, renew_ddb_list);
+ }
+
+ /* Failed adapter initialization?
+ * Retry reset_ha only if invoked via DPC (DPC_RESET_HA) */
+ if ((test_bit(AF_ONLINE, &ha->flags) == 0) &&
+ (test_bit(DPC_RESET_HA, &ha->dpc_flags))) {
+ /* Adapter initialization failed, see if we can retry
+ * resetting the ha */
+ if (!test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags)) {
+ ha->retry_reset_ha_cnt = MAX_RESET_HA_RETRIES;
+ DEBUG2(printk("scsi%ld: recover adapter - retrying "
+ "(%d) more times\n", ha->host_no,
+ ha->retry_reset_ha_cnt));
+ set_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
+ status = QLA_ERROR;
+ } else {
+ if (ha->retry_reset_ha_cnt > 0) {
+ /* Schedule another Reset HA--DPC will retry */
+ ha->retry_reset_ha_cnt--;
+ DEBUG2(printk("scsi%ld: recover adapter - "
+ "retry remaining %d\n", ha->host_no,
+ ha->retry_reset_ha_cnt));
+ status = QLA_ERROR;
+ }
+
+ if (ha->retry_reset_ha_cnt == 0) {
+ /* Recover adapter retries have been exhausted.
+ * Adapter DEAD */
+ DEBUG2(printk("scsi%ld: recover adapter "
+ "failed - board disabled\n", ha->host_no));
+ qla4xxx_flush_active_srbs(ha);
+ clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
+ clear_bit(DPC_RESET_HA, &ha->dpc_flags);
+ clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST,
+ &ha->dpc_flags);
+ status = QLA_ERROR;
+ }
+ }
+ } else {
+ clear_bit(DPC_RESET_HA, &ha->dpc_flags);
+ clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags);
+ clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
+ }
+
+ ha->adapter_error_count++;
+
+ if (status == QLA_SUCCESS)
+ qla4xxx_enable_intrs(ha);
+
+ DEBUG2(printk("scsi%ld: recover adapter .. DONE\n", ha->host_no));
+ return (status);
+}
+
+
+/**************************************************************************
+ * qla4xxx_eh_wait_on_command
+ * This routine waits for the command to be returned by the Firmware
+ * for some max time.
+ *
+ * Input:
+ * ha = actual ha whose done queue will contain the command
+ * returned by firmware.
+ * cmd = Scsi Command to wait on.
+ *
+ * Returns:
+ * Not Found : 0
+ * Found : 1
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static int
+qla4xxx_eh_wait_on_command(scsi_qla_host_t * ha, struct scsi_cmnd *cmd)
+{
+ int done = 0;
+ srb_t *rp;
+ uint32_t max_wait_time = EH_WAIT_CMD_TOV;
+
+ do {
+ /* Checking to see if its returned to OS */
+ rp = (srb_t *) CMD_SP(cmd);
+ if (rp == NULL) {
+ done++;
+ break;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(2 * HZ);
+ } while (max_wait_time--);
+
+ return done;
+}
+
+/**************************************************************************
+ * qla4xxx_wait_for_hba_online
+ * This routine
+ *
+ * Input:
+ * ha - Pointer to host adapter structure
+ *
+ * Remarks:
+ *
+ * Returns:
+ * SUCCESS - Adapter is ONLINE
+ * FAILED - Adapter is DEAD
+ *
+ * Context:
+ * Kernel context. Assume io_request_lock LOCKED upon entry
+ **************************************************************************/
+int qla4xxx_wait_for_hba_online(scsi_qla_host_t * ha)
+{
+ unsigned long wait_online;
+
+ wait_online = jiffies + (30 * HZ);
+ while (time_before(jiffies, wait_online)) {
+ if (ADAPTER_UP(ha))
+ return QLA_SUCCESS;
+
+ if (!ADAPTER_UP(ha) && (ha->retry_reset_ha_cnt == 0)) {
+ return QLA_ERROR;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(2 * HZ);
+ }
+
+ return QLA_ERROR;
+}
+
+/**************************************************************************
+ * qla4xxx_eh_abort
+ * This routine aborts commands that currently held in the adapter's
+ * internal queues. Commands that are active are NOT aborted.
+ *
+ * Input:
+ * cmd - Pointer to Linux's SCSI command structure
+ *
+ * Remarks:
+ * Aborts get translated to "device resets" by the scsi switch
+ * which will return a RESET status and not ABORT. Since the
+ * mid-level is expecting an ABORT status during an abort(),
+ * we always elevate to device reset.
+ *
+ * Returns:
+ * SUCCESS - Successfully aborted non-active command
+ * FAILED - Command not found, or command currently active
+ *
+ * Context:
+ * Kernel context. io_request_lock LOCKED
+ **************************************************************************/
+static int
+qla4xxx_eh_abort(struct scsi_cmnd *cmd)
+{
+ if (!CMD_SP(cmd))
+ return FAILED;
+
+ /*
+ * Aborts get translated to "device resets" by some scsi switches which
+ * will return a RESET status and not ABORT. Since the mid-level is
+ * expecting an ABORT status during an abort(), we always elevate to
+ * device reset.
+ */
+ return FAILED;
+}
+
+/**************************************************************************
+ * qla4xxx_eh_wait_for_active_target_commands
+ * This routine
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * target - SCSI target ID
+ *
+ * Returns:
+ * 0 - All pending commands returned
+ * non-zero - All pending commands did not return
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_eh_wait_for_active_target_commands(scsi_qla_host_t * ha, int t, int l)
+{
+ int cnt;
+ int status;
+ srb_t *sp;
+ struct scsi_cmnd *cmd;
+
+ /*
+ * Waiting for all commands for the designated target in the active
+ * array
+ */
+ status = 0;
+ for (cnt = 1; cnt < MAX_SRBS; cnt++) {
+ spin_lock(&ha->hardware_lock);
+ sp = ha->active_srb_array[cnt];
+ if (sp) {
+ cmd = sp->cmd;
+ spin_unlock(&ha->hardware_lock);
+ if (cmd->device->id == t && cmd->device->lun == l) {
+ if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
+ status++;
+ break;
+ }
+ }
+ } else {
+ spin_unlock(&ha->hardware_lock);
+ }
+ }
+ return status;
+}
+
+/**************************************************************************
+ * qla4xxx_eh_device_reset
+ * This routine is called by the Linux OS to reset all luns on the
+ * specified target.
+ *
+ * Input:
+ * cmd - Pointer to Linux's SCSI command structure
+ *
+ * Output:
+ * None
+ *
+ * Remarks:
+ * None
+ *
+ * Returns:
+ * SUCCESS - Successfully reset target/lun
+ * FAILED - Failed to reset target/lun
+ *
+ * Context:
+ * Kernel context. io_request_lock LOCKED
+ **************************************************************************/
+static int
+qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+ struct ddb_entry *ddb_entry = (struct ddb_entry *)cmd->device->hostdata;
+ srb_t *sp;
+ int ret = FAILED, stat;
+
+ sp = (srb_t *) CMD_SP(cmd);
+ if (!sp || !ddb_entry)
+ return ret;
+
+ ql4_printk(KERN_INFO, ha,
+ "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no,
+ cmd->device->channel, cmd->device->id, cmd->device->lun);
+
+ DEBUG2(printk(KERN_INFO
+ "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, timeout=%x, "
+ "dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no,
+ cmd, jiffies, cmd->timeout_per_command / HZ, ha->dpc_flags,
+ cmd->result, cmd->allowed));
+
+ /* FIXme: wait for hba to go online */
+ stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun);
+ if (stat != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "DEVICE RESET FAILED. %d\n", stat);
+
+ goto eh_dev_reset_done;
+ }
+
+ /* Send marker. */
+ ha->marker_needed = 1;
+
+ /*
+ * If we are coming down the EH path, wait for all commands to complete
+ * for the device.
+ */
+ if (cmd->device->host->eh_active) {
+ if (qla4xxx_eh_wait_for_active_target_commands(ha,
+ cmd->device->id, cmd->device->lun)) {
+ ql4_printk(KERN_INFO, ha,
+ "DEVICE RESET FAILED - waiting for commands.\n");
+ goto eh_dev_reset_done;
+ }
+ }
+
+ ql4_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no,
+ cmd->device->channel, cmd->device->id, cmd->device->lun);
+
+ ret = SUCCESS;
+
+eh_dev_reset_done:
+
+ return ret;
+}
+
+/**************************************************************************
+ * qla4xxx_eh_bus_reset
+ * This routine is called by the Linux OS to reset the specified
+ * adapter/bus.
+ *
+ * Input:
+ * cmd - Pointer to Linux's SCSI command structure
+ *
+ * Returns:
+ * SUCCESS - Successfully reset adapter/bus
+ * FAILED - Failed to reset adapter/bus
+ *
+ * Context:
+ * Kernel context. io_request_lock LOCKED
+ **************************************************************************/
+static int
+qla4xxx_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+ int status = QLA_SUCCESS;
+ int return_status = FAILED;
+ scsi_qla_host_t *ha = to_qla_host(cmd->device->host);
+ struct ddb_entry *ddb_entry = (struct ddb_entry *)cmd->device->hostdata;
+ ddb_entry_t *dtemp;
+
+ ha = (scsi_qla_host_t *) cmd->device->host->hostdata;
+
+ ql4_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): BUS RESET ISSUED.\n", ha->host_no,
+ cmd->device->channel, cmd->device->id, cmd->device->lun);
+
+ if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ DEBUG2(printk("scsi%ld:%d: %s: Unable to reset bus. Adapter "
+ "DEAD.\n", ha->host_no, cmd->device->channel, __func__));
+
+ return FAILED;
+ }
+
+ /* Attempt to reset all valid targets with outstanding commands */
+ list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) {
+ /* Issue a reset */
+ status |= qla4xxx_reset_target(ha, ddb_entry);
+ }
+
+ /*
+ * Status is QLA_SUCCESS if target resets for ALL devices completed
+ * successfully. Otherwise the status is QLA_ERROR.
+ */
+ if (status == QLA_SUCCESS)
+ return_status = SUCCESS;
+
+ ql4_printk(KERN_INFO, ha, "BUS RESET %s.\n",
+ return_status == FAILED ? "FAILED" : "SUCCEDED");
+
+ return return_status;
+}
+
+/**************************************************************************
+ * qla4xxx_reset_target
+ * This routine issues either a warm or cold target reset to the
+ * specified device.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ * ddb_entry - Pointer to device database entry
+ *
+ * Remarks:
+ * The caller must ensure that the ddb_entry pointer is valid before
+ * calling this routine.
+ *
+ * Returns:
+ * QLA_SUCCESS - Successfully reset target
+ * QLA_ERROR - Failed to reset target
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+int
+qla4xxx_reset_target(scsi_qla_host_t * ha, ddb_entry_t * ddb_entry)
+{
+ int status = QLA_SUCCESS;
+#if 0
+ uint8_t stat;
+
+ /* Reset all LUNs on this target */
+ list_for_each_entry(fclun, &fcport->fcluns, list) {
+ stat = qla4xxx_reset_lun(ha, ddb_entry, fclun);
+ if (stat == QLA_SUCCESS) {
+ /* Send marker. */
+ ha->marker_needed = 1;
+
+ /*
+ * Waiting for all active commands to complete for the
+ * device.
+ */
+ status |=
+ qla4xxx_eh_wait_for_active_target_commands(ha,
+ ddb_entry->
+ target,
+ fclun->
+ lun);
+ } else {
+ status |= QLA_ERROR;
+ }
+ }
+#endif
+
+ if (status == QLA_SUCCESS) {
+ DEBUG2(printk("scsi%ld:%d: device reset SUCCEEDED.\n",
+ ha->host_no, ddb_entry->os_target_id));
+ } else {
+ DEBUG2(printk("scsi%ld:%d: device reset FAILED.\n",
+ ha->host_no, ddb_entry->os_target_id));
+
+ status = QLA_ERROR;
+ }
+
+ return status;
+}
+
+/**************************************************************************
+ * qla4xxx_flush_active_srbs
+ * This routine is called just prior to a HARD RESET to return all
+ * outstanding commands back to the Operating System.
+ *
+ * Input:
+ * ha - Pointer to host adapter structure.
+ *
+ * Remarks:
+ * Caller should make sure that the following locks are released
+ * before this calling routine:
+ * Hardware lock, io_request_lock, adapter_lock, and lun_lock.
+ *
+ * Returns:
+ * None
+ *
+ * Context:
+ * Kernel context.
+ **************************************************************************/
+static void
+qla4xxx_flush_active_srbs(scsi_qla_host_t * ha)
+{
+ srb_t *srb;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (i = 1; i < MAX_SRBS; i++) {
+ if ((srb = ha->active_srb_array[i]) != NULL) {
+ del_from_active_array(ha, i);
+ srb->cmd->result = DID_RESET << 16;
+ qla4xxx_srb_compl(ha, srb);
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+}
+
+/**************************************************************************
+ * qla4xxx_eh_host_reset
+ * This routine is invoked by the Linux kernel to perform fatal error
+ * recovery on the specified adapter.
+ *
+ * Input:
+ * cmd - Pointer to Linux's SCSI command structure
+ *
+ * Returns:
+ * SUCCESS - Successfully recovered host adapter
+ * FAILED - Failed to recover host adapter
+ *
+ * Context:
+ * Kernel context. io_request_lock LOCKED
+ **************************************************************************/
+static int
+qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
+{
+ int return_status = FAILED;
+ scsi_qla_host_t *ha;
+
+ ha = (scsi_qla_host_t *) cmd->device->host->hostdata;
+
+ ql4_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no,
+ cmd->device->channel, cmd->device->id, cmd->device->lun);
+
+ if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ DEBUG2(printk("scsi%ld:%d: %s: Unable to reset host. Adapter "
+ "DEAD.\n", ha->host_no, cmd->device->channel, __func__));
+
+ return FAILED;
+ }
+
+ if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS) {
+ return_status = SUCCESS;
+ }
+
+ ql4_printk(KERN_INFO, ha, "HOST RESET %s.\n",
+ return_status == FAILED ? "FAILED" : "SUCCEDED");
+
+ return return_status;
+}
+
+
+static struct pci_device_id qla4xxx_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP4010,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP4022,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl);
+
+struct pci_driver qla4xxx_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = qla4xxx_pci_tbl,
+ .probe = qla4xxx_probe_adapter,
+ .remove = qla4xxx_remove_adapter,
+};
+
+static int __init
+qla4xxx_module_init(void)
+{
+ int ret;
+
+ /* Allocate cache for SRBs. */
+ srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(srb_t), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (srb_cachep == NULL) {
+ printk(KERN_ERR
+ "qla4xxx: Unable to allocate SRB cache...Failing load!\n");
+ return -ENOMEM;
+ }
+
+ /* Derive version string. */
+ strcpy(qla4xxx_version_str, QLA4XXX_DRIVER_VERSION);
+ if (extended_error_logging)
+ strcat(qla4xxx_version_str, "-debug");
+
+ printk(KERN_INFO "QLogic iSCSI HBA Driver\n");
+ ret = pci_module_init(&qla4xxx_pci_driver);
+ if (ret)
+ kmem_cache_destroy(srb_cachep);
+
+ return ret;
+}
+
+static void __exit
+qla4xxx_module_exit(void)
+{
+ pci_unregister_driver(&qla4xxx_pci_driver);
+ kmem_cache_destroy(srb_cachep);
+}
+
+module_init(qla4xxx_module_init);
+module_exit(qla4xxx_module_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic iSCSI HBA Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA4XXX_DRIVER_VERSION);
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/8] qla4xxx: iSNS routines
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
2005-09-06 22:09 ` [PATCH 2/8] qla4xxx: initialization routines Andrew Vasquez
2005-09-06 22:09 ` [PATCH 3/8] qla4xxx: OS integration files Andrew Vasquez
@ 2005-09-06 22:09 ` Andrew Vasquez
2005-09-06 22:09 ` [PATCH 5/8] qla4xxx: ISR routines Andrew Vasquez
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:09 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver iSNS routines and definitions.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
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
diff --git a/drivers/scsi/qla4xxx/ql4_isns.c b/drivers/scsi/qla4xxx/ql4_isns.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_isns.c
@@ -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;
+}
diff --git a/drivers/scsi/qla4xxx/ql4_isns.h b/drivers/scsi/qla4xxx/ql4_isns.h
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_isns.h
@@ -0,0 +1,401 @@
+/*
+ * 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.
+ */
+
+#define ISNSP_VERSION 0x0001 // Current iSNS version as defined by
+// the latest spec that we support
+
+/* Swap Macros
+ *
+ * These are designed to be used on constants (such as the function codes
+ * below) such that the swapping is done by the compiler at compile time
+ * and not at run time. Of course, they should also work on variables
+ * in which case the swapping will occur at run time.
+ */
+#define WSWAP(x) (uint16_t)(((((uint16_t)x)<<8)&0xFF00) | \
+ ((((uint16_t)x)>>8)&0x00FF))
+#define DWSWAP(x) (uint32_t)(((((uint32_t)x)<<24)&0xFF000000) | \
+ ((((uint32_t)x)<<8)&0x00FF0000) | \
+ ((((uint32_t)x)>>8)&0x0000FF00) | \
+ ((((uint32_t)x)>>24)&0x000000FF))
+
+/*
+ * Timeout Values
+ *******************/
+#define ISNS_RESTART_TOV 5
+
+#define IOCB_ISNS_PT_PDU_TYPE(x) ((x) & 0x0F000000)
+#define IOCB_ISNS_PT_PDU_INDEX(x) ((x) & (MAX_PDU_ENTRIES-1))
+
+#define ISNS_ASYNCH_REQ_PDU 0x01000000
+#define ISNS_ASYNCH_RSP_PDU 0x02000000
+#define ISNS_REQ_RSP_PDU 0x03000000
+
+// Fake device indexes. Used internally by the driver for indexing to other than a DDB entry
+#define ISNS_DEVICE_INDEX MAX_DEV_DB_ENTRIES + 0
+
+#define ISNS_CLEAR_FLAGS(ha) do {clear_bit(ISNS_FLAG_SCN_IN_PROGRESS | \
+ ISNS_FLAG_SCN_RESTART | \
+ ISNS_FLAG_QUERY_SINGLE_OBJECT, \
+ &ha->isns_flags);} while(0);
+
+// iSNS Message Function ID codes
+
+#define ISNS_FCID_DevAttrReg 0x0001 // Device Attribute Registration Request
+#define ISNS_FCID_DevAttrQry 0x0002 // Device Attribute Query Request
+#define ISNS_FCID_DevGetNext 0x0003 // Device Get Next Request
+#define ISNS_FCID_DevDereg 0x0004 // Device Deregister Request
+#define ISNS_FCID_SCNReg 0x0005 // SCN Register Request
+#define ISNS_FCID_SCNDereg 0x0006 // SCN Deregister Request
+#define ISNS_FCID_SCNEvent 0x0007 // SCN Event
+#define ISNS_FCID_SCN 0x0008 // State Change Notification
+#define ISNS_FCID_DDReg 0x0009 // DD Register
+#define ISNS_FCID_DDDereg 0x000A // DD Deregister
+#define ISNS_FCID_DDSReg 0x000B // DDS Register
+#define ISNS_FCID_DDSDereg 0x000C // DDS Deregister
+#define ISNS_FCID_ESI 0x000D // Entity Status Inquiry
+#define ISNS_FCID_Heartbeat 0x000E // Name Service Heartbeat
+//NOT USED 0x000F-0x0010
+#define ISNS_FCID_RqstDomId 0x0011 // Request FC_DOMAIN_ID
+#define ISNS_FCID_RlseDomId 0x0012 // Release FC_DOMAIN_ID
+#define ISNS_FCID_GetDomId 0x0013 // Get FC_DOMAIN_IDs
+//RESERVED 0x0014-0x00FF
+//Vendor Specific 0x0100-0x01FF
+//RESERVED 0x0200-0x8000
+
+// iSNS Response Message Function ID codes
+
+#define ISNS_FCID_DevAttrRegRsp 0x8001 // Device Attribute Registration Response
+#define ISNS_FCID_DevAttrQryRsp 0x8002 // Device Attribute Query Response
+#define ISNS_FCID_DevGetNextRsp 0x8003 // Device Get Next Response
+#define ISNS_FCID_DevDeregRsp 0x8004 // Deregister Device Response
+#define ISNS_FCID_SCNRegRsp 0x8005 // SCN Register Response
+#define ISNS_FCID_SCNDeregRsp 0x8006 // SCN Deregister Response
+#define ISNS_FCID_SCNEventRsp 0x8007 // SCN Event Response
+#define ISNS_FCID_SCNRsp 0x8008 // SCN Response
+#define ISNS_FCID_DDRegRsp 0x8009 // DD Register Response
+#define ISNS_FCID_DDDeregRsp 0x800A // DD Deregister Response
+#define ISNS_FCID_DDSRegRsp 0x800B // DDS Register Response
+#define ISNS_FCID_DDSDeregRsp 0x800C // DDS Deregister Response
+#define ISNS_FCID_ESIRsp 0x800D // Entity Status Inquiry Response
+//NOT USED 0x800E-0x8010
+#define ISNS_FCID_RqstDomIdRsp 0x8011 // Request FC_DOMAIN_ID Response
+#define ISNS_FCID_RlseDomIdRsp 0x8012 // Release FC_DOMAIN_ID Response
+#define ISNS_FCID_GetDomIdRsp 0x8013 // Get FC_DOMAIN_IDs Response
+//RESERVED 0x8014-0x80FF
+//Vendor Specific 0x8100-0x81FF
+//RESERVED 0x8200-0xFFFF
+
+// iSNS Error Codes
+
+#define ISNS_ERR_SUCCESS 0 // Successful
+#define ISNS_ERR_UNKNOWN 1 // Unknown Error
+#define ISNS_ERR_MSG_FORMAT 2 // Message Format Error
+#define ISNS_ERR_INVALID_REG 3 // Invalid Registration
+//RESERVED 4
+#define ISNS_ERR_INVALID_QUERY 5 // Invalid Query
+#define ISNS_ERR_SOURCE_UNKNOWN 6 // Source Unknown
+#define ISNS_ERR_SOURCE_ABSENT 7 // Source Absent
+#define ISNS_ERR_SOURCE_UNAUTHORIZED 8 // Source Unauthorized
+#define ISNS_ERR_NO_SUCH_ENTRY 9 // No Such Entry
+#define ISNS_ERR_VER_NOT_SUPPORTED 10 // Version Not Supported
+#define ISNS_ERR_INTERNAL_ERROR 11 // Internal Error
+#define ISNS_ERR_BUSY 12 // Busy
+#define ISNS_ERR_OPT_NOT_UNDERSTOOD 13 // Option Not Understood
+#define ISNS_ERR_INVALID_UPDATE 14 // Invalid Update
+#define ISNS_ERR_MSG_NOT_SUPPORTED 15 // Message (FUNCTION_ID) Not Supported
+#define ISNS_ERR_SCN_EVENT_REJECTED 16 // SCN Event Rejected
+#define ISNS_ERR_SCN_REG_REJECTED 17 // SCN Registration Rejected
+#define ISNS_ERR_ATTR_NOT_IMPLEMENTED 18 // Attribute Not Implemented
+#define ISNS_ERR_FC_DOMAIN_ID_NOT_AVAIL 19 // FC_DOMAIN_ID Not Available
+#define ISNS_ERR_FC_DOMAIN_ID_NOT_ALLOC 20 // FC_DOMAIN_ID Not Allocated
+#define ISNS_ERR_ESI_NOT_AVAILABLE 21 // ESI Not Available
+#define ISNS_ERR_INVALID_DEREG 22 // Invalid Deregistration
+#define ISNS_ERR_REG_FEATURES_NOT_SUPPORTED 23 // Registration Features Not Supported
+
+#define ISNS_ERROR_CODE_TBL() { \
+ "SUCCESSFUL" , \
+ "UNKNOWN ERROR" , \
+ "MESSAGE FORMAT ERROR" , \
+ "INVALID REGISTRATION" , \
+ "RESERVED" , \
+ "INVALID QUERY" , \
+ "SOURCE UNKNOWN" , \
+ "SOURCE ABSENT" , \
+ "SOURCE UNAUTHORIZED" , \
+ "NO SUCH ENTRY" , \
+ "VERSION NOT SUPPORTED" , \
+ "INTERNAL ERROR" , \
+ "BUSY" , \
+ "OPTION NOT UNDERSTOOD" , \
+ "INVALID UPDATE" , \
+ "MESSAGE (FUNCTION_ID) NOT SUPPORTED" , \
+ "SCN EVENT REJECTED" , \
+ "SCN REGISTRATION REJECTED" , \
+ "ATTRIBUTE NOT IMPLEMENTED" , \
+ "FC_DOMAIN_ID NOT AVAILABLE" , \
+ "FC_DOMAIN_ID NOT ALLOCATED" , \
+ "ESI NOT AVAILABLE" , \
+ "INVALID DEREGISTRATION" , \
+ "REGISTRATION FEATURES NOT SUPPORTED" , \
+ NULL \
+}
+
+// iSNS Protocol Structures
+
+typedef struct {
+ uint16_t isnsp_version;
+ uint16_t function_id;
+ uint16_t pdu_length;
+ uint16_t flags;
+ uint16_t transaction_id;
+ uint16_t sequence_id;
+ uint8_t payload[0];
+} ISNSP_MESSAGE_HEADER, *PISNSP_MESSAGE_HEADER;
+
+typedef struct {
+ uint32_t error_code;
+ uint8_t attributes[0];
+} ISNSP_RESPONSE_HEADER, *PISNSP_RESPONSE_HEADER;
+
+// iSNS Message Flags Definitions
+
+#define ISNSP_CLIENT_SENDER 0x8000
+#define ISNSP_SERVER_SENDER 0x4000
+#define ISNSP_AUTH_BLOCK_PRESENT 0x2000
+#define ISNSP_REPLACE_FLAG 0x1000
+#define ISNSP_LAST_PDU 0x0800
+#define ISNSP_FIRST_PDU 0x0400
+
+#define ISNSP_VALID_FLAGS_MASK (ISNSP_CLIENT_SENDER | \
+ ISNSP_SERVER_SENDER | \
+ ISNSP_AUTH_BLOCK_PRESENT | \
+ ISNSP_REPLACE_FLAG | \
+ ISNSP_LAST_PDU | \
+ ISNSP_FIRST_PDU)
+
+// iSNS Attribute Structure
+
+typedef struct {
+ uint32_t tag;
+ uint32_t length;
+ uint8_t value[0]; // Variable length data
+} ISNS_ATTRIBUTE, *PISNS_ATTRIBUTE;
+
+// The following macro assumes that the attribute is wholly contained within
+// the buffer in question and is valid (see VALIDATE_ATTR below).
+
+static inline
+PISNS_ATTRIBUTE NEXT_ATTR(PISNS_ATTRIBUTE pattr)
+{
+ return (PISNS_ATTRIBUTE) (&pattr->value[0] +
+ be32_to_cpu(pattr->length));
+}
+
+static inline int
+VALIDATE_ATTR(PISNS_ATTRIBUTE PAttr, uint8_t * buffer_end)
+{
+ // Ensure that the Length field of the current attribute is contained
+ // within the buffer before trying to read it, and then be sure that
+ // the entire attribute is contained within the buffer.
+
+ if ((((unsigned long)&PAttr->length + sizeof(PAttr->length)) <=
+ (unsigned long)buffer_end)
+ && (unsigned long)NEXT_ATTR(PAttr) <= (unsigned long)buffer_end) {
+ return 1;
+ }
+
+ return 0;
+}
+
+// iSNS-defined Attribute Tags
+
+#define ISNS_ATTR_TAG_DELIMITER 0
+#define ISNS_ATTR_TAG_ENTITY_IDENTIFIER 1
+#define ISNS_ATTR_TAG_ENTITY_PROTOCOL 2
+#define ISNS_ATTR_TAG_MGMT_IP_ADDRESS 3
+#define ISNS_ATTR_TAG_TIMESTAMP 4
+#define ISNS_ATTR_TAG_PROTOCOL_VERSION_RANGE 5
+#define ISNS_ATTR_TAG_REGISTRATION_PERIOD 6
+#define ISNS_ATTR_TAG_ENTITY_INDEX 7
+#define ISNS_ATTR_TAG_ENTITY_NEXT_INDEX 8
+#define ISNS_ATTR_TAG_ENTITY_ISAKMP_PHASE_1 11
+#define ISNS_ATTR_TAG_ENTITY_CERTIFICATE 12
+#define ISNS_ATTR_TAG_PORTAL_IP_ADDRESS 16
+#define ISNS_ATTR_TAG_PORTAL_PORT 17
+#define ISNS_ATTR_TAG_PORTAL_SYMBOLIC_NAME 18
+#define ISNS_ATTR_TAG_ESI_INTERVAL 19
+#define ISNS_ATTR_TAG_ESI_PORT 20
+#define ISNS_ATTR_TAG_PORTAL_GROUP 21
+#define ISNS_ATTR_TAG_PORTAL_INDEX 22
+#define ISNS_ATTR_TAG_SCN_PORT 23
+#define ISNS_ATTR_TAG_PORTAL_NEXT_INDEX 24
+#define ISNS_ATTR_TAG_PORTAL_SECURITY_BITMAP 27
+#define ISNS_ATTR_TAG_PORTAL_ISAKMP_PHASE_1 28
+#define ISNS_ATTR_TAG_PORTAL_ISAKMP_PHASE_2 29
+#define ISNS_ATTR_TAG_PORTAL_CERTIFICATE 31
+#define ISNS_ATTR_TAG_ISCSI_NAME 32
+#define ISNS_ATTR_TAG_ISCSI_NODE_TYPE 33
+#define ISNS_ATTR_TAG_ISCSI_ALIAS 34
+#define ISNS_ATTR_TAG_ISCSI_SCN_BITMAP 35
+#define ISNS_ATTR_TAG_ISCSI_NODE_INDEX 36
+#define ISNS_ATTR_TAG_WWNN_TOKEN 37
+#define ISNS_ATTR_TAG_ISCSI_NODE_NEXT_INDEX 38
+#define ISNS_ATTR_TAG_ISCSI_AUTH_METHOD 42
+#define ISNS_ATTR_TAG_ISCSI_NODE_CERTIFICATE 43
+#define ISNS_ATTR_TAG_PG_TAG 48
+#define ISNS_ATTR_TAG_PG_ISCSI_NAME 49
+#define ISNS_ATTR_TAG_PG_PORTAL_IP_ADDRESS 50
+#define ISNS_ATTR_TAG_PG_PORTAL_PORT 51
+#define ISNS_ATTR_TAG_PG_INDEX 52
+#define ISNS_ATTR_TAG_PG_NEXT_INDEX 53
+#define ISNS_ATTR_TAG_FC_PORT_NAME_WWPN 64
+#define ISNS_ATTR_TAG_PORT_ID 65
+#define ISNS_ATTR_TAG_FC_PORT_TYPE 66
+#define ISNS_ATTR_TAG_SYMBOLIC_PORT_NAME 67
+#define ISNS_ATTR_TAG_FABRIC_PORT_NAME 68
+#define ISNS_ATTR_TAG_HARD_ADDRESS 69
+#define ISNS_ATTR_TAG_PORT_IP_ADDRESS 70
+#define ISNS_ATTR_TAG_CLASS_OF_SERVICE 71
+#define ISNS_ATTR_TAG_FC4_TYPES 72
+#define ISNS_ATTR_TAG_FC4_DESCRIPTOR 73
+#define ISNS_ATTR_TAG_FC4_FEATURES 74
+#define ISNS_ATTR_TAG_IFCP_SCN_BITMAP 75
+#define ISNS_ATTR_TAG_PORT_ROLE 76
+#define ISNS_ATTR_TAG_PERMANENT_PORT_NAME 77
+#define ISNS_ATTR_TAG_PORT_CERTIFICATE 83
+#define ISNS_ATTR_TAG_FC4_TYPE_CODE 95
+#define ISNS_ATTR_TAG_FC_NODE_NAME_WWNN 96
+#define ISNS_ATTR_TAG_SYMBOLIC_NODE_NAME 97
+#define ISNS_ATTR_TAG_NODE_IP_ADDRESS 98
+#define ISNS_ATTR_TAG_NODE_IPA 99
+#define ISNS_ATTR_TAG_NODE_CERTIFICATE 100
+#define ISNS_ATTR_TAG_PROXY_ISCSI_NAME 101
+#define ISNS_ATTR_TAG_SWITCH_NAME 128
+#define ISNS_ATTR_TAG_PREFERRED_ID 129
+#define ISNS_ATTR_TAG_ASSIGNED_ID 130
+#define ISNS_ATTR_TAG_VIRTUAL_FABRIC_ID 131
+#define ISNS_ATTR_TAG_VENDOR_OUI 256
+//Vendor-specific iSNS Server 257-384
+//Vendor-specific Entity 385-512
+//Vendor-specific Portal 513-640
+//Vendor-specific iSCSI Node 641-768
+//Vendor-specific FC Port Name 769-896
+//Vendor-specific FC Node Name 897-1024
+//Vendor-specific DDS 1025-1280
+//Vendor-Specific DD 1281-1536
+//Vendor-specific (other) 1237-2048
+#define ISNS_ATTR_TAG_DD_SET_ID 2049
+#define ISNS_ATTR_TAG_DD_SET_SYMBOLIC_NAME 2050
+#define ISNS_ATTR_TAG_DD_SET_STATUS 2051
+#define ISNS_ATTR_TAG_DD_SET_NEXT_ID 2052
+#define ISNS_ATTR_TAG_DD_ID 2065
+#define ISNS_ATTR_TAG_DD_SYMBOLIC_NAME 2066
+#define ISNS_ATTR_TAG_DD_MEMBER_ISCSI_INDEX 2067
+#define ISNS_ATTR_TAG_DD_MEMBER_ISCSI_NAME 2068
+#define ISNS_ATTR_TAG_DD_MEMBER_IFCP_NODE 2069
+#define ISNS_ATTR_TAG_DD_MEMBER_PORTAL_INDEX 2070
+#define ISNS_ATTR_TAG_DD_MEMBER_PORTAL_IP_ADDRESS 2071
+#define ISNS_ATTR_TAG_DD_MEMBER_PORTAL_PORT 2072
+#define ISNS_ATTR_TAG_DD_FEATURES 2078
+#define ISNS_ATTR_TAG_DD_ID_NEXT_ID 2079
+
+// Definitions used for Entity Protocol
+
+#define ENTITY_PROTOCOL_NEUTRAL 1
+#define ENTITY_PROTOCOL_ISCSI 2
+#define ENTITY_PROTOCOL_IFCP 3
+
+// Definitions used for iSCSI Node Type
+
+#define ISCSI_NODE_TYPE_TARGET 0x00000001
+#define ISCSI_NODE_TYPE_INITIATOR 0x00000002
+#define ISCSI_NODE_TYPE_CONTROL 0x00000004
+
+// Definitions used for iSCSI Node SCN Bitmap
+
+#define ISCSI_SCN_DD_DDS_MEMBER_ADDED 0x00000001 // Management SCN only
+#define ISCSI_SCN_DD_DDS_MEMBER_REMOVED 0x00000002 // Management SCN only
+#define ISCSI_SCN_OBJECT_UPDATED 0x00000004
+#define ISCSI_SCN_OBJECT_ADDED 0x00000008
+#define ISCSI_SCN_OBJECT_REMOVED 0x00000010
+#define ISCSI_SCN_MANAGEMENT_SCN 0x00000020
+#define ISCSI_SCN_TARGET_AND_SELF_INFO_ONLY 0x00000040
+#define ISCSI_SCN_INITIATOR_AND_SELF_INFO_ONLY 0x00000080
+
+#define ISCSI_SCN_OBJECT_MASK (ISCSI_SCN_OBJECT_UPDATED | \
+ ISCSI_SCN_OBJECT_ADDED | \
+ ISCSI_SCN_OBJECT_REMOVED)
+
+// Definitions used for iSCSI Security Bitmap
+
+#define ISNS_SECURITY_BITMAP_VALID 0x00000001
+#define ISNS_SECURITY_IKE_IPSEC_ENABLED 0x00000002
+#define ISNS_SECURITY_MAIN_MODE_ENABLED 0x00000004
+#define ISNS_SECURITY_AGGRESSIVE_MODE_ENABLED 0x00000008
+#define ISNS_SECURITY_PFS_ENABLED 0x00000010
+#define ISNS_SECURITY_TRANSPORT_MODE_PREFERRED 0x00000020
+#define ISNS_SECURITY_TUNNEL_MODE_PREFERRED 0x00000040
+
+// Definitions used for Portal Port
+
+#define PORTAL_PORT_NUMBER_MASK 0x0000FFFF
+#define PORTAL_PORT_TYPE_UDP 0x00010000
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/8] qla4xxx: ISR routines
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (2 preceding siblings ...)
2005-09-06 22:09 ` [PATCH 4/8] qla4xxx: iSNS routines Andrew Vasquez
@ 2005-09-06 22:09 ` Andrew Vasquez
2005-09-06 22:09 ` [PATCH 6/8] qla4xxx: Mailbox routines Andrew Vasquez
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:09 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver ISR routines.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
drivers/scsi/qla4xxx/ql4_isr.c | 905 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 905 insertions(+), 0 deletions(-)
create mode 100644 drivers/scsi/qla4xxx/ql4_isr.c
e822048467375795703d23e45efe7c94c4a01bd9
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -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);
+
+}
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 6/8] qla4xxx: Mailbox routines
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (3 preceding siblings ...)
2005-09-06 22:09 ` [PATCH 5/8] qla4xxx: ISR routines Andrew Vasquez
@ 2005-09-06 22:09 ` Andrew Vasquez
2005-09-06 22:09 ` [PATCH 7/8] qla4xxx: Support routines Andrew Vasquez
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:09 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver Mailbox routines.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
drivers/scsi/qla4xxx/ql4_mbx.c | 1132 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 1132 insertions(+), 0 deletions(-)
create mode 100644 drivers/scsi/qla4xxx/ql4_mbx.c
f5b56bf482abb9d96f2d542fbef6957f2f52d545
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -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;
+}
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 7/8] qla4xxx: Support routines
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (4 preceding siblings ...)
2005-09-06 22:09 ` [PATCH 6/8] qla4xxx: Mailbox routines Andrew Vasquez
@ 2005-09-06 22:09 ` Andrew Vasquez
2005-09-06 22:10 ` [PATCH 8/8] qla4xxx: Settings Andrew Vasquez
` (4 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:09 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver support routines.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
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
diff --git a/drivers/scsi/qla4xxx/ql4_dbg.c b/drivers/scsi/qla4xxx/ql4_dbg.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_dbg.c
@@ -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");
+}
diff --git a/drivers/scsi/qla4xxx/ql4_dbg.h b/drivers/scsi/qla4xxx/ql4_dbg.h
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_dbg.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/*
+ * Driver debug definitions.
+ */
+/* #define QL_DEBUG */ /* DEBUG messages */
+/* #define QL_DEBUG_LEVEL_3 */ /* Output function tracing */
+/* #define QL_DEBUG_LEVEL_4 */
+/* #define QL_DEBUG_LEVEL_5 */
+/* #define QL_DEBUG_LEVEL_9 */
+
+#define QL_DEBUG_LEVEL_2 /* ALways enable error messagess */
+#if defined(QL_DEBUG)
+#define DEBUG(x) do {x;} while (0);
+#else
+#define DEBUG(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_2)
+#define DEBUG2(x) do {if(extended_error_logging == 2) x;} while (0);
+#define DEBUG2_3(x) do {x;} while (0);
+#else /* */
+#define DEBUG2(x) do {} while (0);
+#endif /* */
+
+#if defined(QL_DEBUG_LEVEL_3)
+#define DEBUG3(x) do {if(extended_error_logging == 3) x;} while (0);
+#else /* */
+#define DEBUG3(x) do {} while (0);
+#if !defined(QL_DEBUG_LEVEL_2)
+#define DEBUG2_3(x) do {} while (0);
+#endif /* */
+#endif /* */
+#if defined(QL_DEBUG_LEVEL_4)
+#define DEBUG4(x) do {x;} while (0);
+#else /* */
+#define DEBUG4(x) do {} while (0);
+#endif /* */
+
+#if defined(QL_DEBUG_LEVEL_5)
+#define DEBUG5(x) do {x;} while (0);
+#else /* */
+#define DEBUG5(x) do {} while (0);
+#endif /* */
+
+#if defined(QL_DEBUG_LEVEL_9)
+#define DEBUG9(x) do {x;} while (0);
+#else /* */
+#define DEBUG9(x) do {} while (0);
+#endif /* */
+
+/*
+ * Debug Print Routines.
+ */
+void qla4xxx_print_scsi_cmd(struct scsi_cmnd *cmd);
+void __dump_registers(scsi_qla_host_t * ha);
+void qla4xxx_dump_mbox_registers(scsi_qla_host_t * ha);
+void qla4xxx_dump_registers(scsi_qla_host_t * ha);
+void qla4xxx_dump_buffer(uint8_t * b, uint32_t size);
diff --git a/drivers/scsi/qla4xxx/ql4_nvram.c b/drivers/scsi/qla4xxx/ql4_nvram.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_nvram.c
@@ -0,0 +1,264 @@
+/*
+ * 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"
+
+#define EEPROM_SIZE(ha) \
+ (IS_QLA4022(ha) ? FM93C86A_SIZE_16 : FM93C66A_SIZE_16)
+#define EEPROM_NO_ADDR_BITS(ha) \
+ (IS_QLA4022(ha) ? FM93C86A_NO_ADDR_BITS_16 : FM93C56A_NO_ADDR_BITS_16)
+#define EEPROM_NO_DATA_BITS(ha) FM93C56A_DATA_BITS_16
+
+int eepromCmdData = 0;
+
+static int
+FM93C56A_Select(scsi_qla_host_t * ha)
+{
+ DEBUG5(printk(KERN_ERR "FM93C56A_Select:\n"));
+
+ eepromCmdData = AUBURN_EEPROM_CS_1 | 0x000f0000;
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData);
+ PCI_POSTING(ISP_NVRAM(ha));
+ return 1;
+}
+
+static int
+FM93C56A_Cmd(scsi_qla_host_t * ha, int cmd, int addr)
+{
+ int i;
+ int mask;
+ int dataBit;
+ int previousBit;
+
+ /* Clock in a zero, then do the start bit. */
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | AUBURN_EEPROM_DO_1);
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | AUBURN_EEPROM_DO_1 |
+ AUBURN_EEPROM_CLK_RISE);
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | AUBURN_EEPROM_DO_1 |
+ AUBURN_EEPROM_CLK_FALL);
+ PCI_POSTING(ISP_NVRAM(ha));
+ mask = 1 << (FM93C56A_CMD_BITS - 1);
+
+ /* Force the previous data bit to be different. */
+ previousBit = 0xffff;
+ for (i = 0; i < FM93C56A_CMD_BITS; i++) {
+ dataBit =
+ (cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
+ if (previousBit != dataBit) {
+
+ /*
+ * If the bit changed, then change the DO state to
+ * match.
+ */
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | dataBit);
+ previousBit = dataBit;
+ }
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | dataBit |
+ AUBURN_EEPROM_CLK_RISE);
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | dataBit |
+ AUBURN_EEPROM_CLK_FALL);
+ PCI_POSTING(ISP_NVRAM(ha));
+ cmd = cmd << 1;
+ }
+ mask = 1 << (EEPROM_NO_ADDR_BITS(ha) - 1);
+
+ /* Force the previous data bit to be different. */
+ previousBit = 0xffff;
+ for (i = 0; i < EEPROM_NO_ADDR_BITS(ha); i++) {
+ dataBit = addr & mask ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
+ if (previousBit != dataBit) {
+ /*
+ * If the bit changed, then change the DO state to
+ * match.
+ */
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | dataBit);
+ previousBit = dataBit;
+ }
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | dataBit |
+ AUBURN_EEPROM_CLK_RISE);
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData | dataBit |
+ AUBURN_EEPROM_CLK_FALL);
+ PCI_POSTING(ISP_NVRAM(ha));
+ addr = addr << 1;
+ }
+ return 1;
+}
+
+static int
+FM93C56A_Deselect(scsi_qla_host_t * ha)
+{
+ eepromCmdData = AUBURN_EEPROM_CS_0 | 0x000f0000;
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData);
+ PCI_POSTING(ISP_NVRAM(ha));
+ return 1;
+}
+
+static int
+FM93C56A_DataIn(scsi_qla_host_t * ha, unsigned short *value)
+{
+ int i;
+ int data = 0;
+ int dataBit;
+
+ /* Read the data bits
+ * The first bit is a dummy. Clock right over it. */
+ for (i = 0; i < EEPROM_NO_DATA_BITS(ha); i++) {
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData |
+ AUBURN_EEPROM_CLK_RISE);
+ WRT_REG_DWORD(ISP_NVRAM(ha), eepromCmdData |
+ AUBURN_EEPROM_CLK_FALL);
+ dataBit =
+ (RD_REG_DWORD(ISP_NVRAM(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0;
+ data = (data << 1) | dataBit;
+ }
+
+ *value = data;
+ return 1;
+}
+
+static int
+EEPROM_ReadWord(int eepromAddr, u16 * value, scsi_qla_host_t * ha)
+{
+ FM93C56A_Select(ha);
+ FM93C56A_Cmd(ha, FM93C56A_READ, eepromAddr);
+ FM93C56A_DataIn(ha, value);
+ FM93C56A_Deselect(ha);
+ return 1;
+}
+
+/* Hardware_lock must be set before calling */
+u16
+RD_NVRAM_WORD(scsi_qla_host_t * ha, int offset)
+{
+ u16 val;
+
+ /* NOTE: NVRAM uses half-word addresses */
+ EEPROM_ReadWord(offset, &val, ha);
+ return val;
+}
+
+int
+qla4xxx_is_NVRAM_configuration_valid(scsi_qla_host_t * ha)
+{
+ int status = QLA_ERROR;
+ uint16_t checksum = 0;
+ uint32_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (index = 0; index < EEPROM_SIZE(ha); index++)
+ checksum += RD_NVRAM_WORD(ha, index);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (checksum == 0)
+ status = QLA_SUCCESS;
+
+ return status;
+}
+
+/*************************************************************************
+ *
+ * Hardware Semaphore routines
+ *
+ *************************************************************************/
+int
+ql4xxx_sem_spinlock(scsi_qla_host_t * ha, u32 sem_mask, u32 sem_bits)
+{
+ uint32_t value;
+ DEBUG2(printk("scsi%ld : Trying to get SEM lock - mask= 0x%x, code = "
+ "0x%x\n", ha->host_no, sem_mask, sem_bits));
+ while (1) {
+ WRT_REG_DWORD(ISP_SEMAPHORE(ha), (sem_mask | sem_bits));
+ value = RD_REG_DWORD(ISP_SEMAPHORE(ha));
+ if ((value & (sem_mask >> 16)) == sem_bits) {
+ DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, "
+ "code = 0x%x\n", ha->host_no, sem_mask, sem_bits));
+ break;
+ }
+ }
+ return 1;
+}
+
+void
+ql4xxx_sem_unlock(scsi_qla_host_t * ha, u32 sem_mask)
+{
+ WRT_REG_DWORD(ISP_SEMAPHORE(ha), sem_mask);
+ PCI_POSTING(ISP_SEMAPHORE(ha));
+ DEBUG2(printk("scsi%ld : UNLOCK SEM - mask= 0x%x\n", ha->host_no,
+ sem_mask));
+}
+
+int
+ql4xxx_sem_lock(scsi_qla_host_t * ha, u32 sem_mask, u32 sem_bits)
+{
+ uint32_t value;
+
+ WRT_REG_DWORD(ISP_SEMAPHORE(ha), (sem_mask | sem_bits));
+ value = RD_REG_DWORD(ISP_SEMAPHORE(ha));
+ if ((value & (sem_mask >> 16)) == sem_bits) {
+ DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, code = "
+ "0x%x, sema code=0x%x\n", ha->host_no, sem_mask, sem_bits,
+ value));
+ return 1;
+ }
+ return 0;
+}
diff --git a/drivers/scsi/qla4xxx/ql4_nvram.h b/drivers/scsi/qla4xxx/ql4_nvram.h
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_nvram.h
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ */
+
+#ifndef _QL2XNVRM_H_
+#define _QL2XNVRM_H_
+
+//
+// AM29LV Flash definitions
+//
+#define FM93C56A_SIZE_8 0x100
+#define FM93C56A_SIZE_16 0x80
+#define FM93C66A_SIZE_8 0x200
+#define FM93C66A_SIZE_16 0x100 /* 4010 */
+#define FM93C86A_SIZE_16 0x400 /* 4022 */
+
+#define FM93C56A_START 0x1
+
+// Commands
+#define FM93C56A_READ 0x2
+#define FM93C56A_WEN 0x0
+#define FM93C56A_WRITE 0x1
+#define FM93C56A_WRITE_ALL 0x0
+#define FM93C56A_WDS 0x0
+#define FM93C56A_ERASE 0x3
+#define FM93C56A_ERASE_ALL 0x0
+
+// Command Extentions
+#define FM93C56A_WEN_EXT 0x3
+#define FM93C56A_WRITE_ALL_EXT 0x1
+#define FM93C56A_WDS_EXT 0x0
+#define FM93C56A_ERASE_ALL_EXT 0x2
+
+// Address Bits
+#define FM93C56A_NO_ADDR_BITS_16 8 /* 4010 */
+#define FM93C56A_NO_ADDR_BITS_8 9 /* 4010 */
+#define FM93C86A_NO_ADDR_BITS_16 10 /* 4022 */
+
+// Data Bits
+#define FM93C56A_DATA_BITS_16 16
+#define FM93C56A_DATA_BITS_8 8
+
+// Special Bits
+#define FM93C56A_READ_DUMMY_BITS 1
+#define FM93C56A_READY 0
+#define FM93C56A_BUSY 1
+#define FM93C56A_CMD_BITS 2
+
+// Auburn Bits
+#define AUBURN_EEPROM_DI 0x8
+#define AUBURN_EEPROM_DI_0 0x0
+#define AUBURN_EEPROM_DI_1 0x8
+#define AUBURN_EEPROM_DO 0x4
+#define AUBURN_EEPROM_DO_0 0x0
+#define AUBURN_EEPROM_DO_1 0x4
+#define AUBURN_EEPROM_CS 0x2
+#define AUBURN_EEPROM_CS_0 0x0
+#define AUBURN_EEPROM_CS_1 0x2
+#define AUBURN_EEPROM_CLK_RISE 0x1
+#define AUBURN_EEPROM_CLK_FALL 0x0
+
+//
+// EEPROM format
+//
+typedef struct _BIOS_PARAMS {
+ uint16_t SpinUpDelay:1;
+ uint16_t BIOSDisable:1;
+ uint16_t MMAPEnable:1;
+ uint16_t BootEnable:1;
+ uint16_t Reserved0:12;
+ uint8_t bootID0:7;
+ uint8_t bootID0Valid:1;
+ uint8_t bootLUN0[8];
+ uint8_t bootID1:7;
+ uint8_t bootID1Valid:1;
+ uint8_t bootLUN1[8];
+ uint16_t MaxLunsPerTarget;
+ uint8_t Reserved1[10];
+} BIOS_PARAMS, *PBIOS_PARAMS;
+typedef struct _EEPROM_PORT_CFG {
+
+ // MTU MAC 0
+ u16 etherMtu_mac;
+
+ // Flow Control MAC 0
+ u16 pauseThreshold_mac;
+ u16 resumeThreshold_mac;
+ u16 reserved[13];
+} EEPROM_PORT_CFG, *PEEPROM_PORT_CFG;
+typedef struct _EEPROM_FUNCTION_CFG {
+ u8 reserved[30];
+
+ // MAC ADDR
+ u8 macAddress[6];
+ u8 macAddressSecondary[6];
+ u16 subsysVendorId;
+ u16 subsysDeviceId;
+} EEPROM_FUNCTION_CFG;
+typedef struct {
+ union {
+ struct { /* isp4010 */
+ u8 asic_id[4]; // x00
+ u8 version; // x04
+ u8 reserved; // x05
+ u16 board_id; // x06
+# define EEPROM_BOARDID_ELDORADO 1
+# define EEPROM_BOARDID_PLACER 2
+
+# define EEPROM_SERIAL_NUM_SIZE 16
+ u8 serial_number[EEPROM_SERIAL_NUM_SIZE]; // x08
+
+ // ExtHwConfig:
+ // Offset = 24bytes
+ //
+ // | SSRAM Size| |ST|PD|SDRAM SZ| W| B| SP | |
+ // |15|14|13|12|11|10| 9| 8 | 7| 6| 5| 4| 3| 2| 1| 0|
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ u16 ext_hw_conf; // x18
+ u8 mac0[6]; // x1A
+ u8 mac1[6]; // x20
+ u8 mac2[6]; // x26
+ u8 mac3[6]; // x2C
+ u16 etherMtu; // x32
+ u16 macConfig; // x34
+#define MAC_CONFIG_ENABLE_ANEG 0x0001
+#define MAC_CONFIG_ENABLE_PAUSE 0x0002
+ u16 phyConfig; // x36
+#define PHY_CONFIG_PHY_ADDR_MASK 0x1f
+#define PHY_CONFIG_ENABLE_FW_MANAGEMENT_MASK 0x20
+ u16 topcat; // x38
+#define TOPCAT_PRESENT 0x0100
+#define TOPCAT_MASK 0xFF00
+
+# define EEPROM_UNUSED_1_SIZE 2
+ u8 unused_1[EEPROM_UNUSED_1_SIZE]; // x3A
+ u16 bufletSize; // x3C
+ u16 bufletCount; // x3E
+ u16 bufletPauseThreshold; // x40
+ u16 tcpWindowThreshold50; // x42
+ u16 tcpWindowThreshold25; // x44
+ u16 tcpWindowThreshold0; // x46
+ u16 ipHashTableBaseHi; // x48
+ u16 ipHashTableBaseLo; // x4A
+ u16 ipHashTableSize; // x4C
+ u16 tcpHashTableBaseHi; // x4E
+ u16 tcpHashTableBaseLo; // x50
+ u16 tcpHashTableSize; // x52
+ u16 ncbTableBaseHi; // x54
+ u16 ncbTableBaseLo; // x56
+ u16 ncbTableSize; // x58
+ u16 drbTableBaseHi; // x5A
+ u16 drbTableBaseLo; // x5C
+ u16 drbTableSize; // x5E
+
+# define EEPROM_UNUSED_2_SIZE 4
+ u8 unused_2[EEPROM_UNUSED_2_SIZE]; // x60
+ u16 ipReassemblyTimeout; // x64
+ u16 tcpMaxWindowSizeHi; // x66
+ u16 tcpMaxWindowSizeLo; // x68
+ u32 net_ip_addr0; // x6A /* Added for TOE functionality. */
+ u32 net_ip_addr1; // x6E
+ u32 scsi_ip_addr0; // x72
+ u32 scsi_ip_addr1; // x76
+# define EEPROM_UNUSED_3_SIZE 128 /* changed from 144 to account for ip addresses */
+ u8 unused_3[EEPROM_UNUSED_3_SIZE]; // x7A
+ u16 subsysVendorId_f0; // xFA
+ u16 subsysDeviceId_f0; // xFC
+
+ // Address = 0x7F
+# define FM93C56A_SIGNATURE 0x9356
+# define FM93C66A_SIGNATURE 0x9366
+ u16 signature; // xFE
+
+# define EEPROM_UNUSED_4_SIZE 250
+ u8 unused_4[EEPROM_UNUSED_4_SIZE]; // x100
+ u16 subsysVendorId_f1; // x1FA
+ u16 subsysDeviceId_f1; // x1FC
+ u16 checksum; // x1FE
+ } __attribute__ ((packed)) isp4010;
+ struct { /* isp4022 */
+ u8 asicId[4]; // x00
+ u8 version; // x04
+ u8 reserved_5; // x05
+ u16 boardId; // x06
+ u8 boardIdStr[16]; // x08
+ u8 serialNumber[16]; // x18
+
+ // External Hardware Configuration
+ u16 ext_hw_conf; // x28
+
+ // MAC 0 CONFIGURATION
+ EEPROM_PORT_CFG macCfg_port0; // x2A
+
+ // MAC 1 CONFIGURATION
+ EEPROM_PORT_CFG macCfg_port1; // x4A
+
+ // DDR SDRAM Configuration
+ u16 bufletSize; // x6A
+ u16 bufletCount; // x6C
+ u16 tcpWindowThreshold50; // x6E
+ u16 tcpWindowThreshold25; // x70
+ u16 tcpWindowThreshold0; // x72
+ u16 ipHashTableBaseHi; // x74
+ u16 ipHashTableBaseLo; // x76
+ u16 ipHashTableSize; // x78
+ u16 tcpHashTableBaseHi; // x7A
+ u16 tcpHashTableBaseLo; // x7C
+ u16 tcpHashTableSize; // x7E
+ u16 ncbTableBaseHi; // x80
+ u16 ncbTableBaseLo; // x82
+ u16 ncbTableSize; // x84
+ u16 drbTableBaseHi; // x86
+ u16 drbTableBaseLo; // x88
+ u16 drbTableSize; // x8A
+ u16 reserved_142[4]; // x8C
+
+ // TCP/IP Parameters
+ u16 ipReassemblyTimeout; // x94
+ u16 tcpMaxWindowSize; // x96
+ u16 ipSecurity; // x98
+ u8 reserved_156[294]; // x9A
+ u16 qDebug[8]; // QLOGIC USE ONLY x1C0
+ EEPROM_FUNCTION_CFG funcCfg_fn0; // x1D0
+ u16 reserved_510; // x1FE
+
+ // Address = 512
+ u8 oemSpace[432]; // x200
+ BIOS_PARAMS sBIOSParams_fn1; // x3B0
+ EEPROM_FUNCTION_CFG funcCfg_fn1; // x3D0
+ u16 reserved_1022; // x3FE
+
+ // Address = 1024
+ u8 reserved_1024[464]; // x400
+ EEPROM_FUNCTION_CFG funcCfg_fn2; // x5D0
+ u16 reserved_1534; // x5FE
+
+ // Address = 1536
+ u8 reserved_1536[432]; // x600
+ BIOS_PARAMS sBIOSParams_fn3; // x7B0
+ EEPROM_FUNCTION_CFG funcCfg_fn3; // x7D0
+ u16 checksum; // x7FE
+ } __attribute__ ((packed)) isp4022;
+ };
+} eeprom_data_t;
+
+#define EEPROM_EXT_HW_CONF_OFFSET() \
+ (IS_QLA4022(ha) ? \
+ offsetof(eeprom_data_t, isp4022.ext_hw_conf) / 2 : \
+ offsetof(eeprom_data_t, isp4010.ext_hw_conf) / 2)
+/*************************************************************************
+ *
+ * Hardware Semaphore
+ *
+ *************************************************************************/
+#if 0
+//
+// Semaphore register definitions
+//
+#define SEM_AVAILABLE 0x00
+#define SEM_OWNER_FIRMWARE 0x01
+#define SEM_OWNER_STORAGE 0x02
+#define SEM_OWNER_NETWORK 0x03
+
+//
+// Private Semaphore definitions
+//
+typedef enum { SEM_HW_LOCK, SEM_GPO, SEM_SDRAM_INIT, SEM_PHY_GBIC, SEM_NVRAM, SEM_FLASH, SEM_COUNT // Not a real semaphore, just indicates how many there are
+} ISP4XXX_SEMAPHORE;
+typedef struct {
+ uint32_t semId;
+ uint32_t semShift;
+} isp4xxxSemInfo_t;
+
+#define SEM_MASK 0x3
+
+/* Wait flag defines -- specifies type of wait to acquire semaphore */
+#define SEM_FLG_NO_WAIT 0
+#define SEM_FLG_WAIT_FOREVER 1
+#define SEM_FLG_TIMED_WAIT 2
+
+#endif /* */
+
+#endif // _QL2XNVRM_H_
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 8/8] qla4xxx: Settings
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (5 preceding siblings ...)
2005-09-06 22:09 ` [PATCH 7/8] qla4xxx: Support routines Andrew Vasquez
@ 2005-09-06 22:10 ` Andrew Vasquez
2005-09-06 22:26 ` [PATCH 0/8] qla4xxx: new driver -- request for review Mike Christie
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 22:10 UTC (permalink / raw)
To: Linux-SCSI Mailing List; +Cc: Andrew Vasquez
ISP4xxx driver settings.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
---
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
diff --git a/drivers/scsi/qla4xxx/ql4_settings.h b/drivers/scsi/qla4xxx/ql4_settings.h
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_settings.h
@@ -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
diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_version.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#define QLA4XXX_DRIVER_VERSION "5.00.03b15-k"
+
+#define QL4_DRIVER_MAJOR_VER 5
+#define QL4_DRIVER_MINOR_VER 0
+#define QL4_DRIVER_PATCH_VER 3
+#define QL4_DRIVER_BETA_VER 15
--
Andrew Vasquez
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] qla4xxx: new driver -- request for review.
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (6 preceding siblings ...)
2005-09-06 22:10 ` [PATCH 8/8] qla4xxx: Settings Andrew Vasquez
@ 2005-09-06 22:26 ` Mike Christie
2005-09-06 22:34 ` Christoph Hellwig
` (2 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Mike Christie @ 2005-09-06 22:26 UTC (permalink / raw)
To: Andrew Vasquez; +Cc: Linux-SCSI Mailing List
Andrew Vasquez wrote:
> 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?
>
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.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] qla4xxx: new driver -- request for review.
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (7 preceding siblings ...)
2005-09-06 22:26 ` [PATCH 0/8] qla4xxx: new driver -- request for review Mike Christie
@ 2005-09-06 22:34 ` Christoph Hellwig
2005-09-06 23:02 ` Andrew Vasquez
2005-10-27 18:31 ` Doug Maxey
10 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2005-09-06 22:34 UTC (permalink / raw)
To: Andrew Vasquez; +Cc: Linux-SCSI Mailing List
> o iSNS routines
Why do we need iSNS support in kernelspace anyway?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] qla4xxx: new driver -- request for review.
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (8 preceding siblings ...)
2005-09-06 22:34 ` Christoph Hellwig
@ 2005-09-06 23:02 ` Andrew Vasquez
2005-10-27 18:31 ` Doug Maxey
10 siblings, 0 replies; 15+ messages in thread
From: Andrew Vasquez @ 2005-09-06 23:02 UTC (permalink / raw)
To: Linux-SCSI Mailing List
On Tue, 06 Sep 2005, Andrew Vasquez wrote:
> 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
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
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/8] qla4xxx: initialization routines
2005-09-06 22:09 ` [PATCH 2/8] qla4xxx: initialization routines Andrew Vasquez
@ 2005-09-07 6:46 ` Rolf Eike Beer
0 siblings, 0 replies; 15+ messages in thread
From: Rolf Eike Beer @ 2005-09-07 6:46 UTC (permalink / raw)
To: Andrew Vasquez; +Cc: Linux-SCSI Mailing List
[-- Attachment #1: Type: text/plain, Size: 2959 bytes --]
Andrew Vasquez wrote:
>ISP4xxx driver initialization routines.
>
>Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
>---
>
> 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
>diff --git a/drivers/scsi/qla4xxx/ql4_init.c
> b/drivers/scsi/qla4xxx/ql4_init.c new file mode 100644
>--- /dev/null
>+++ b/drivers/scsi/qla4xxx/ql4_init.c
>@@ -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?
>+/**************************************************************************
>+ * 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.
>+
> **************************************************************************/
Please take a look at Documentation/kernel-doc-nano-HOWTO.txt. And kernel
context is pretty normal in a device driver.
>+/*
>+ * 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.
>+ */
And here you have a different style just 2 functions later
>+int
>+qla4xxx_init_rings(scsi_qla_host_t * ha)
>+{
[...]
>+ /* Initialize active array */
>+ for (i = 0; i < MAX_SRBS; i++)
>+ ha->active_srb_array[i] = 0;
>+ ha->active_srb_count = 0;
memset() ?
>+ 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
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] qla4xxx: new driver -- request for review.
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
` (9 preceding siblings ...)
2005-09-06 23:02 ` Andrew Vasquez
@ 2005-10-27 18:31 ` Doug Maxey
2005-10-27 23:37 ` Mike Christie
10 siblings, 1 reply; 15+ messages in thread
From: Doug Maxey @ 2005-10-27 18:31 UTC (permalink / raw)
To: Andrew Vasquez; +Cc: Linux-SCSI Mailing List
On Tue, 06 Sep 2005 15:08:41 PDT, Andrew Vasquez wrote:
>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.
Any new patches in the pipeline?
Looking for volunteers?
++doug
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] qla4xxx: new driver -- request for review.
2005-10-27 18:31 ` Doug Maxey
@ 2005-10-27 23:37 ` Mike Christie
2005-10-28 19:19 ` Doug Maxey
0 siblings, 1 reply; 15+ messages in thread
From: Mike Christie @ 2005-10-27 23:37 UTC (permalink / raw)
To: Doug Maxey; +Cc: Andrew Vasquez, Linux-SCSI Mailing List
Doug Maxey wrote:
>
> 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 :)
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] qla4xxx: new driver -- request for review.
2005-10-27 23:37 ` Mike Christie
@ 2005-10-28 19:19 ` Doug Maxey
0 siblings, 0 replies; 15+ messages in thread
From: Doug Maxey @ 2005-10-28 19:19 UTC (permalink / raw)
To: Mike Christie; +Cc: Doug Maxey, Andrew Vasquez, Linux-SCSI Mailing List
On Thu, 27 Oct 2005 18:37:34 CDT, Mike Christie wrote:
>Doug Maxey wrote:
>>
>> 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 :)
>
Is there an outline of the work needed?
++doug
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2005-10-28 19:20 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-09-06 22:08 [PATCH 0/8] qla4xxx: new driver -- request for review Andrew Vasquez
2005-09-06 22:09 ` [PATCH 2/8] qla4xxx: initialization routines Andrew Vasquez
2005-09-07 6:46 ` Rolf Eike Beer
2005-09-06 22:09 ` [PATCH 3/8] qla4xxx: OS integration files Andrew Vasquez
2005-09-06 22:09 ` [PATCH 4/8] qla4xxx: iSNS routines Andrew Vasquez
2005-09-06 22:09 ` [PATCH 5/8] qla4xxx: ISR routines Andrew Vasquez
2005-09-06 22:09 ` [PATCH 6/8] qla4xxx: Mailbox routines Andrew Vasquez
2005-09-06 22:09 ` [PATCH 7/8] qla4xxx: Support routines Andrew Vasquez
2005-09-06 22:10 ` [PATCH 8/8] qla4xxx: Settings Andrew Vasquez
2005-09-06 22:26 ` [PATCH 0/8] qla4xxx: new driver -- request for review Mike Christie
2005-09-06 22:34 ` Christoph Hellwig
2005-09-06 23:02 ` Andrew Vasquez
2005-10-27 18:31 ` Doug Maxey
2005-10-27 23:37 ` Mike Christie
2005-10-28 19:19 ` Doug Maxey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox