From: Luben Tuikov <luben_tuikov@adaptec.com>
To: SCSI Mailing List <linux-scsi@vger.kernel.org>
Subject: [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
Date: Thu, 17 Feb 2005 12:35:36 -0500 [thread overview]
Message-ID: <4214D5E8.9000907@adaptec.com> (raw)
This is the SAS domain discovery code. Part 1/3.
diff -Nru a/drivers/scsi/adp94xx/adp94xx_discover.c b/drivers/scsi/adp94xx/adp94xx_discover.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/scsi/adp94xx/adp94xx_discover.c 2005-02-16 16:08:12 -05:00
@@ -0,0 +1,2460 @@
+#define NO_VPD_WORKAROUND
+#define SMP_OVERRUN_WORKAROUND
+#define SMP_UNDERRUN_WORKAROUND
+/*
+ * Adaptec ADP94xx SAS HBA device driver for Linux.
+ *
+ * Copyright (c) 2004 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Adapted by : Robert Tarte <robt@PacificCodeWorks.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * The source in this file is adapted from: SASDiscoverSimulation.cpp,
+ * from the SAS-1.1 draft, sas1r07.pdf, project T10/1601-D,
+ * ISO/IEC 14776-151:200x.
+ *
+ * This is an implementation of the initiator based expander discovery
+ * and configuration. Structure names used are equivalent to those
+ * referenced in the SAS document.
+ * Basic assumptions:
+ *
+ * 1. Change primitives will initiate a rediscovery/configuration sequence.
+ * 2. Table locations for SASAddresses are deterministic for a specific
+ * topology only, when the topology changes, the location of a SASAddress
+ * in an ASIC table cannot be assumed.
+ * 3. A complete discovery level occurs before the configuration of the
+ * level begins, multiple passes are required as the levels of expanders
+ * encountered between the initiator and the end devices is increased.
+ * 4. Configuration of a single expander occurs before proceeding to
+ * subsequent expanders attached.
+ * 5. The Attached structure is filled in following OOB and is available
+ * from the initialization routines.
+ *
+ * $Id: //depot/razor/linux/src/adp94xx_discover.c#65 $
+ *
+ */
+#define KDB_ENABLE 0
+#define DISCOVER_DEBUG 0
+
+#include "adp94xx_osm.h"
+#include "adp94xx_inline.h"
+#include "adp94xx_sata.h"
+#if KDB_ENABLE
+#include "linux/kdb.h"
+#endif
+
+/*
+ * this defines the type of algorithm used for discover
+ */
+#if 0
+int DiscoverAlgorithm = SAS_SIMPLE_LEVEL_DESCENT;
+#else
+int DiscoverAlgorithm = SAS_UNIQUE_LEVEL_DESCENT;
+#endif
+
+#define ROUTE_ENTRY(expander, i, j) \
+ (expander->RouteTable + \
+ ((expander->num_phys * (i) * SAS_ADDR_LEN) + \
+ (j) * SAS_ADDR_LEN))
+
+#define DUMP_EXPANDER(s, expander) \
+ asd_dump_expander((uint8_t *)__FUNCTION__, __LINE__, (s), expander)
+
+#define NEW_STATE(new_state) new_state(sm_contextp, new_state)
+
+struct state_machine asd_DiscoverySM = {
+ asd_DiscoverySM_Initialize,
+ asd_DiscoverySM_StateMachine,
+ asd_DiscoverySM_Finish,
+ asd_DiscoverySM_Abort,
+ ASD_STATE_DISCOVER_START
+};
+
+struct state_machine asd_DiscoverExpanderSM = {
+ asd_DiscoverExpanderSM_Initialize,
+ asd_DiscoverExpanderSM_StateMachine,
+ asd_DiscoverExpanderSM_Finish,
+ asd_DiscoverExpanderSM_Abort,
+ ASD_STATE_REPORT_AND_DISCOVER_START
+};
+
+struct state_machine asd_DiscoverFindBoundarySM = {
+ asd_DiscoverFindBoundarySM_Initialize,
+ asd_DiscoverFindBoundarySM_StateMachine,
+ asd_DiscoverFindBoundarySM_Finish,
+ asd_DiscoverFindBoundarySM_Abort,
+ ASD_STATE_FIND_BOUNDARY_START
+};
+
+struct state_machine asd_DiscoverConfigSetSM = {
+ asd_DiscoverConfigSetSM_Initialize,
+ asd_DiscoverConfigSetSM_StateMachine,
+ asd_DiscoverConfigSetSM_Finish,
+ asd_DiscoverConfigSetSM_Abort,
+ ASD_STATE_CONFIG_SET_START
+};
+
+struct state_machine asd_ConfigureExpanderSM = {
+ asd_ConfigureExpanderSM_Initialize,
+ asd_ConfigureExpanderSM_StateMachine,
+ asd_ConfigureExpanderSM_Finish,
+ asd_ConfigureExpanderSM_Abort,
+ ASD_STATE_CONFIG_EXPANDER_START
+};
+
+struct state_machine asd_ConfigureATA_SM = {
+ asd_ConfigureATA_SM_Initialize,
+ asd_ConfigureATA_SM_StateMachine,
+ asd_ConfigureATA_SM_Finish,
+ asd_ConfigureATA_SM_Abort,
+ ASD_STATE_CONFIGURE_ATA_START
+};
+
+struct state_machine asd_InitSATA_SM = {
+ asd_InitSATA_SM_Initialize,
+ asd_InitSATA_SM_StateMachine,
+ asd_InitSATA_SM_Finish,
+ asd_InitSATA_SM_Abort,
+ ASD_STATE_INIT_SATA_START
+};
+
+struct state_machine asd_SATA_SpinHoldSM = {
+ asd_SATA_SpinHoldSM_Initialize,
+ asd_SATA_SpinHoldSM_StateMachine,
+ asd_SATA_SpinHoldSM_Finish,
+ asd_SATA_SpinHoldSM_Abort,
+ ASD_STATE_SATA_SPINHOLD_START
+};
+
+struct state_machine asd_InitSAS_SM = {
+ asd_InitSAS_SM_Initialize,
+ asd_InitSAS_SM_StateMachine,
+ asd_InitSAS_SM_Finish,
+ asd_InitSAS_SM_Abort,
+ ASD_STATE_INIT_SAS_START
+};
+
+struct state_machine asd_InitSMP_SM = {
+ asd_InitSMP_SM_Initialize,
+ asd_InitSMP_SM_StateMachine,
+ asd_InitSMP_SM_Finish,
+ asd_InitSMP_SM_Abort,
+ ASD_STATE_INIT_SMP_START
+};
+
+extern void
+asd_scb_internal_done(struct asd_softc *asd, struct scb *scb,
+ struct asd_done_list *dl);
+extern void asd_run_device_queues(struct asd_softc *asd);
+
+struct asd_target *asd_discover_get_target(struct state_machine_context
+ *sm_contextp,
+ uint8_t * dest_sas_address,
+ struct list_head *old_discover_listp,
+ struct list_head *found_listp,
+ unsigned conn_rate,
+ TRANSPORT_TYPE transport_type);
+
+struct asd_DiscoverySM_Context;
+
+static void asd_invalidate_targets(struct asd_softc *asd,
+ struct asd_port *port);
+static void asd_validate_targets_hotplug(struct asd_softc *asd,
+ struct asd_port *port,
+ struct asd_DiscoverySM_Context *ctx);
+static void asd_validate_targets_init(struct asd_softc *asd);
+static void asd_apply_conn_mask(struct asd_softc *asd,
+ struct list_head *discover_list);
+static int asd_discovery_queue_cmd(struct asd_softc *asd,
+ struct scb *scb, struct asd_target *targ,
+ struct asd_device *dev);
+
+#if DISCOVER_DEBUG
+static void asd_dump_tree(struct asd_softc *asd, struct asd_port *port);
+#endif
+
+void asd_print_conn_rate(unsigned conn_rate, char *s)
+{
+ switch (conn_rate) {
+ case SAS_RATE_30GBPS:
+ printk(" 3.0-GBPS");
+ break;
+ case SAS_RATE_15GBPS:
+ printk(" 1.5-GBPS");
+ break;
+ default:
+ printk(" \?\?-GBPS");
+ break;
+ }
+ printk("%s", s);
+}
+
+void asd_print_state(unsigned state, char *s)
+{
+ switch (state) {
+ case ASD_STATE_DISCOVER_START:
+ printk("ASD_STATE_DISCOVER_START");
+ break;
+
+ case ASD_STATE_DISCOVER_ATTACHED:
+ printk("ASD_STATE_DISCOVER_ATTACHED");
+ break;
+
+ case ASD_STATE_FIND_BOUNDARY:
+ printk("ASD_STATE_FIND_BOUNDARY");
+ break;
+
+ case ASD_STATE_CONFIG_BOUNDARY_SET:
+ printk("ASD_STATE_CONFIG_BOUNDARY_SET");
+ break;
+
+ case ASD_STATE_CONFIG_ATTACHED_SET:
+ printk("ASD_STATE_CONFIG_ATTACHED_SET");
+ break;
+
+ case ASD_STATE_FINISHED:
+ printk("ASD_STATE_FINISHED");
+ break;
+
+ case ASD_STATE_SATA_SPINHOLD:
+ printk("ASD_STATE_SATA_SPINHOLD");
+ break;
+
+ case ASD_STATE_INIT_SATA:
+ printk("ASD_STATE_INIT_SATA");
+ break;
+
+ case ASD_STATE_INIT_SAS:
+ printk("ASD_STATE_INIT_SAS");
+ break;
+
+ case ASD_STATE_INIT_SMP:
+ printk("ASD_STATE_INIT_SMP");
+ break;
+
+ case ASD_STATE_FAILED:
+ printk("ASD_STATE_FAILED");
+ break;
+
+ case ASD_STATE_REPORT_AND_DISCOVER_START:
+ printk("ASD_STATE_REPORT_AND_DISCOVER_START");
+ break;
+
+ case ASD_STATE_ISSUE_REPORT_GENERAL:
+ printk("ASD_STATE_ISSUE_REPORT_GENERAL");
+ break;
+
+ case ASD_STATE_ISSUE_DISCOVER_LOOP:
+ printk("ASD_STATE_ISSUE_DISCOVER_LOOP");
+ break;
+
+ case ASD_STATE_REPORT_AND_DISCOVER_FINISHED:
+ printk("ASD_STATE_REPORT_AND_DISCOVER_FINISHED");
+ break;
+
+ case ASD_STATE_REPORT_AND_DISCOVER_FAILED:
+ printk("ASD_STATE_REPORT_AND_DISCOVER_FAILED");
+ break;
+
+ case ASD_STATE_FIND_BOUNDARY_START:
+ printk("ASD_STATE_FIND_BOUNDARY_START");
+ break;
+
+ case ASD_STATE_FIND_BOUNDARY_LOOP:
+ printk("ASD_STATE_FIND_BOUNDARY_LOOP");
+ break;
+
+ case ASD_STATE_FIND_BOUNDARY_FINISHED:
+ printk("ASD_STATE_FIND_BOUNDARY_FINISHED");
+ break;
+
+ case ASD_STATE_FIND_BOUNDARY_FAILED:
+ printk("ASD_STATE_FIND_BOUNDARY_FAILED");
+ break;
+
+ case ASD_STATE_CONFIG_SET_START:
+ printk("ASD_STATE_CONFIG_SET_START");
+ break;
+
+ case ASD_STATE_CONFIG_SET_ISSUE_DISCOVER:
+ printk("ASD_STATE_CONFIG_SET_ISSUE_DISCOVER");
+ break;
+
+ case ASD_STATE_CONFIG_SET_CONFIGURE_EXPANDER:
+ printk("ASD_STATE_CONFIG_SET_CONFIGURE_EXPANDER");
+ break;
+
+ case ASD_STATE_CONFIG_SET_FINISHED:
+ printk("ASD_STATE_CONFIG_SET_FINISHED");
+ break;
+
+ case ASD_STATE_CONFIG_SET_FAILED:
+ printk("ASD_STATE_CONFIG_SET_FAILED");
+ break;
+
+ case ASD_STATE_CONFIG_EXPANDER_START:
+ printk("ASD_STATE_CONFIG_EXPANDER_START");
+ break;
+
+ case ASD_STATE_CONFIG_EXPANDER_ROUTE:
+ printk("ASD_STATE_CONFIG_EXPANDER_ROUTE");
+ break;
+
+ case ASD_STATE_CONFIG_EXPANDER_ROUTE_LOOP:
+ printk("ASD_STATE_CONFIG_EXPANDER_ROUTE_LOOP");
+ break;
+
+ case ASD_STATE_CONFIG_EXPANDER_FINISHED:
+ printk("ASD_STATE_CONFIG_EXPANDER_FINISHED");
+ break;
+
+ case ASD_STATE_CONFIG_EXPANDER_FAILED:
+ printk("ASD_STATE_CONFIG_EXPANDER_FAILED");
+ break;
+
+ case ASD_STATE_INIT_SATA_START:
+ printk("ASD_STATE_INIT_SATA_START");
+ break;
+
+ case ASD_STATE_INIT_SATA_REPORT_PHY:
+ printk("ASD_STATE_INIT_SATA_REPORT_PHY");
+ break;
+
+ case ASD_STATE_INIT_SATA_IDENTIFY:
+ printk("ASD_STATE_INIT_SATA_IDENTIFY");
+ break;
+
+ case ASD_STATE_INIT_SATA_CONFIGURE_FEATURES:
+ printk("ASD_STATE_INIT_SATA_CONFIGURE_FEATURES");
+ break;
+
+ case ASD_STATE_INIT_SATA_FINISHED:
+ printk("ASD_STATE_INIT_SATA_FINISHED");
+ break;
+
+ case ASD_STATE_INIT_SATA_FAILED:
+ printk("ASD_STATE_INIT_SATA_FAILED");
+ break;
+
+ case ASD_STATE_SATA_SPINHOLD_START:
+ printk("ASD_STATE_SATA_SPINHOLD_START");
+ break;
+
+ case ASD_STATE_SATA_SPINHOLD_PHY_CONTROL:
+ printk("ASD_STATE_SATA_SPINHOLD_PHY_CONTROL");
+ break;
+
+ case ASD_STATE_SATA_SPINHOLD_DISCOVER:
+ printk("ASD_STATE_SATA_SPINHOLD_DISCOVER");
+ break;
+
+ case ASD_STATE_SATA_SPINHOLD_FINISHED:
+ printk("ASD_STATE_SATA_SPINHOLD_FINISHED");
+ break;
+
+ case ASD_STATE_SATA_SPINHOLD_FAILED:
+ printk("ASD_STATE_SATA_SPINHOLD_FAILED");
+ break;
+
+ case ASD_STATE_INIT_SAS_START:
+ printk("ASD_STATE_INIT_SAS_START");
+ break;
+
+ case ASD_STATE_INIT_SAS_INQUIRY:
+ printk("ASD_STATE_INIT_SAS_INQUIRY");
+ break;
+
+ case ASD_STATE_INIT_SAS_GET_DEVICE_ID:
+ printk("ASD_STATE_INIT_SAS_GET_DEVICE_ID");
+ break;
+
+ case ASD_STATE_INIT_SAS_GET_SERIAL_NUMBER:
+ printk("ASD_STATE_INIT_SAS_GET_SERIAL_NUMBER");
+ break;
+
+ case ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS:
+ printk("ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS");
+ break;
+
+ case ASD_STATE_INIT_SAS_GET_PORT_CONTROL:
+ printk("ASD_STATE_INIT_SAS_GET_PORT_CONTROL");
+ break;
+
+ case ASD_STATE_INIT_SAS_FINISHED:
+ printk("ASD_STATE_INIT_SAS_FINISHED");
+ break;
+
+ case ASD_STATE_INIT_SAS_FAILED:
+ printk("ASD_STATE_INIT_SAS_FAILED");
+ break;
+
+ case ASD_STATE_INIT_SMP_START:
+ printk("ASD_STATE_INIT_SMP_START");
+ break;
+
+ case ASD_STATE_INIT_SMP_REPORT_MANUFACTURER_INFO:
+ printk("ASD_STATE_INIT_SMP_REPORT_MANUFACTURER_INFO");
+ break;
+
+ case ASD_STATE_INIT_SMP_FINISHED:
+ printk("ASD_STATE_INIT_SMP_FINISHED");
+ break;
+
+ case ASD_STATE_INIT_SMP_FAILED:
+ printk("ASD_STATE_INIT_SMP_FAILED");
+ break;
+
+ case ASD_STATE_CONFIGURE_ATA_START:
+ printk("ASD_STATE_CONFIGURE_ATA_START");
+ break;
+
+ case ASD_STATE_CONFIGURE_ATA_FEATURES:
+ printk("ASD_STATE_CONFIGURE_ATA_FEATURES");
+ break;
+
+ case ASD_STATE_CONFIGURE_ATA_FINISHED:
+ printk("ASD_STATE_CONFIGURE_ATA_FINISHED");
+ break;
+
+ case ASD_STATE_CONFIGURE_ATA_FAILED:
+ printk("ASD_STATE_CONFIGURE_ATA_FAILED");
+ break;
+
+ default:
+ printk("[0x%04x]", state);
+ break;
+ }
+
+ printk("%s", s);
+}
+
+void SM_new_state(struct state_machine_context *sm_contextp, unsigned new_state)
+{
+ struct state_information *state_infop;
+
+ SETUP_STATE(sm_contextp);
+
+ //printk("[%d]===== ", sm_contextp->state_stack_top);
+ //asd_print_state(state_infop->current_state, " -> ");
+ //asd_print_state(new_state, "\n");
+
+ if ((new_state & state_infop->state_machine_p->first_state) !=
+ state_infop->state_machine_p->first_state) {
+
+ printk("illegal state 0x%x\n", new_state);
+ printk("[%d]===== ", sm_contextp->state_stack_top);
+ asd_print_state(state_infop->current_state, " -> ");
+ asd_print_state(new_state, "\n");
+ }
+
+ state_infop->current_state = new_state;
+}
+
+DISCOVER_RESULTS
+asd_run_state_machine(struct state_machine_context *sm_contextp)
+{
+ struct state_information *state_infop;
+ DISCOVER_RESULTS results;
+
+ SETUP_STATE(sm_contextp);
+
+ results = state_infop->state_machine_p->state_machine(sm_contextp);
+
+ while (results != DISCOVER_OK) {
+
+ state_infop =
+ &sm_contextp->state_stack[sm_contextp->state_stack_top];
+
+ if (results == DISCOVER_FAILED) {
+ printk("State Machine Failure: ");
+ asd_print_state(state_infop->current_state, "\n");
+ }
+
+ if ((results == DISCOVER_FINISHED) ||
+ (results == DISCOVER_FAILED)) {
+
+ state_infop->state_machine_p->finish(sm_contextp,
+ results);
+
+ if (sm_contextp->state_stack_top == 0) {
+
+ //printk("nothing on stack\n");
+
+ return DISCOVER_OK;
+ }
+
+ POP_STATE(sm_contextp);
+ }
+
+ results =
+ state_infop->state_machine_p->state_machine(sm_contextp);
+ }
+
+ return results;
+}
+
+#define ASD_PUSH_STATE_MACHINE(sm_contextp, state_machine_p, arg) \
+ asd_push_state_machine(sm_contextp, #state_machine_p, \
+ state_machine_p, arg)
+
+DISCOVER_RESULTS
+asd_push_state_machine(struct state_machine_context * sm_contextp,
+ char *s,
+ struct state_machine * state_machine_p, void *arg)
+{
+ struct state_information *state_infop;
+ DISCOVER_RESULTS results;
+
+#if 0
+ printk("\n\n%s:=====================================================\n",
+ __FUNCTION__);
+ printk("%s: %s ============================\n", __FUNCTION__, s);
+ printk("%s:=====================================================\n\n\n",
+ __FUNCTION__);
+#endif
+
+ sm_contextp->state_stack_top++;
+
+ SETUP_STATE(sm_contextp);
+
+ state_infop->current_state = state_machine_p->first_state;
+ state_infop->stack_top = 0;
+ state_infop->state_machine_p = state_machine_p;
+
+ results = state_machine_p->initialize(sm_contextp, arg);
+
+ if (results != DISCOVER_CONTINUE) {
+
+ state_infop->state_machine_p->finish(sm_contextp, results);
+
+ if (sm_contextp->state_stack_top == 0) {
+
+ //printk("nothing on stack\n");
+
+ return results;
+ }
+
+ POP_STATE(sm_contextp);
+ }
+
+ return results;
+}
+
+void asd_abort_state_machine(struct state_machine_context *sm_contextp)
+{
+ struct state_information *state_infop;
+
+ SETUP_STATE(sm_contextp);
+
+ while (sm_contextp->state_stack_top != -1) {
+
+ state_infop =
+ &sm_contextp->state_stack[sm_contextp->state_stack_top];
+
+ state_infop->state_machine_p->abort(sm_contextp);
+
+ POP_STATE(sm_contextp);
+ }
+}
+
+void
+asd_dump_expander(char *function,
+ unsigned line, char *string, struct asd_target *expander)
+{
+ unsigned i;
+ struct Discover *discover;
+
+ printk("||||||||||| - %s - %s:%d:\n", string, function, line);
+ printk("||||||||||| - %0llx - ",
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ if (expander->management_type == ASD_DEVICE_END) {
+ printk("\n");
+ return;
+ }
+
+ printk("num_phys %d\n", expander->num_phys);
+
+ for (i = 0; i < expander->num_phys; i++) {
+
+ discover = &(expander->Phy[i].Result);
+
+ printk("||||||||||| - phy %d attached to %0llx\n", i,
+ *((uint64_t *) discover->AttachedSASAddress));
+ }
+}
+
+void asd_dump_expander_list(char *s, struct list_head *discover_listp)
+{
+ struct asd_target *target;
+ struct asd_target *parent;
+
+ printk("----- %s\n", s);
+
+ list_for_each_entry(target, discover_listp, all_domain_targets) {
+
+ DUMP_EXPANDER("target:", target);
+
+ printk(" %0llx", *((uint64_t *) target->ddb_profile.sas_addr));
+
+ asd_print_conn_rate(target->ddb_profile.conn_rate, "\n");
+
+ for (parent = target->parent; parent != NULL;
+ parent = parent->parent) {
+
+ printk("\t:%0llx\n",
+ *((uint64_t *) parent->ddb_profile.sas_addr));
+ }
+ }
+
+ printk("-----\n");
+}
+
+void
+asd_discover_wakeup_state_machine(struct state_machine_context *sm_contextp)
+{
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ discover_contextp->port->events |= ASD_DISCOVERY_EVENT;
+
+ asd_wakeup_sem(&discover_contextp->asd->platform_data->discovery_sem);
+}
+
+DISCOVER_RESULTS
+asd_ssp_request(struct state_machine_context *sm_contextp,
+ struct asd_target *target,
+ uint8_t * command,
+ unsigned command_len,
+ dma_addr_t buf_busaddr, unsigned buffer_len, unsigned direction)
+{
+ struct asd_ssp_task_hscb *ssp_hscb;
+ unsigned long flags;
+ struct scb *scb;
+ struct sg_element *sg;
+ int error;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_lock(discover_contextp->asd, &flags);
+
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = asd_hwi_get_scb(discover_contextp->asd, 1)) == NULL) {
+ // TODO - fix this
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_FAILED;
+ }
+
+ scb->flags |= SCB_INTERNAL;
+
+ scb->platform_data->targ = target;
+
+ scb->platform_data->dev = NULL;
+
+ list_add_tail(&scb->owner_links,
+ &discover_contextp->asd->platform_data->pending_os_scbs);
+
+ ssp_hscb = &scb->hscb->ssp_task;
+
+ ssp_hscb->header.opcode = SCB_INITIATE_SSP_TASK;
+
+ asd_build_sas_header(target, ssp_hscb);
+
+ ssp_hscb->protocol_conn_rate |= PROTOCOL_TYPE_SSP;
+
+ ssp_hscb->data_dir_flags |= direction;
+
+ ssp_hscb->xfer_len = asd_htole32(buffer_len);
+
+ memcpy(ssp_hscb->cdb, command, command_len);
+
+ memset(&ssp_hscb->cdb[command_len], 0,
+ SCB_EMBEDDED_CDB_SIZE - command_len);
+
+ sg = scb->sg_list;
+
+ scb->platform_data->buf_busaddr = buf_busaddr;
+
+ error = asd_sg_setup(sg, buf_busaddr, buffer_len, /*last */ 1);
+
+ if (error != 0) {
+ return DISCOVER_FAILED;
+ }
+
+ memcpy(ssp_hscb->sg_elements, scb->sg_list, sizeof(*sg));
+
+ scb->sg_count = 1;
+
+ asd_push_post_stack(discover_contextp->asd, scb, (void *)sm_contextp,
+ asd_ssp_request_done);
+
+ scb->flags |= SCB_ACTIVE;
+
+ asd_hwi_post_scb(discover_contextp->asd, scb);
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_OK;
+}
+
+void
+asd_ssp_request_done(struct asd_softc *asd,
+ struct scb *scb, struct asd_done_list *done_listp)
+{
+ struct state_machine_context *sm_contextp;
+ struct discover_context *discover_contextp;
+
+ sm_contextp = (struct state_machine_context *)scb->io_ctx;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_scb_internal_done(asd, scb, done_listp);
+
+ discover_contextp->resid_len = 0;
+
+ /*
+ * TODO: need better return value here
+ */
+ switch (done_listp->opcode) {
+ case TASK_COMP_WO_ERR:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ break;
+
+ case TASK_COMP_W_UNDERRUN:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ discover_contextp->resid_len =
+ asd_le32toh(done_listp->stat_blk.data.res_len);
+ break;
+
+ case TASK_F_W_OPEN_REJECT:
+ printk("%s:%d: reject abandon_open %x reason %x\n",
+ __FUNCTION__, __LINE__,
+ done_listp->stat_blk.open_reject.abandon_open,
+ done_listp->stat_blk.open_reject.reason);
+ break;
+
+ case SSP_TASK_COMP_W_RESP:
+ default:
+#if 0
+ printk("%s:%d opcode=0x%x\n", __FUNCTION__, __LINE__,
+ done_listp->opcode);
+#endif
+ /*
+ * TODO: need better return value here
+ */
+ discover_contextp->openStatus = OPEN_REJECT_BAD_DESTINATION;
+ break;
+ }
+
+ sm_contextp->wakeup_state_machine(sm_contextp);
+}
+
+DISCOVER_RESULTS
+asd_smp_request(struct state_machine_context *sm_contextp,
+ struct asd_target *target,
+ unsigned request_length, unsigned response_length)
+{
+ struct asd_smp_task_hscb *smp_hscb;
+ unsigned long flags;
+ struct scb *scb;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_lock(discover_contextp->asd, &flags);
+
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = asd_hwi_get_scb(discover_contextp->asd, 1)) == NULL) {
+ // TODO - fix this
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_FAILED;
+ }
+
+ scb->flags |= SCB_INTERNAL;
+ scb->platform_data->dev = NULL;
+ scb->platform_data->targ = target;
+
+ list_add_tail(&scb->owner_links,
+ &discover_contextp->asd->platform_data->pending_os_scbs);
+
+ smp_hscb = &scb->hscb->smp_task;
+
+ smp_hscb->header.opcode = SCB_INITIATE_SMP_TASK;
+ smp_hscb->protocol_conn_rate = target->ddb_profile.conn_rate;
+
+ smp_hscb->smp_req_busaddr = discover_contextp->SMPRequestBusAddr;
+ smp_hscb->smp_req_size = request_length;
+
+ smp_hscb->smp_req_ds = 0;
+ smp_hscb->sister_scb = 0xffff;
+ smp_hscb->conn_handle = target->ddb_profile.conn_handle;
+
+ smp_hscb->smp_resp_busaddr = discover_contextp->SMPResponseBusAddr;
+ smp_hscb->smp_resp_size = response_length;
+
+ smp_hscb->smp_resp_ds = 0;
+
+ asd_push_post_stack(discover_contextp->asd, scb, (void *)sm_contextp,
+ asd_smp_request_done);
+
+ scb->flags |= SCB_ACTIVE;
+
+ asd_hwi_post_scb(discover_contextp->asd, scb);
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_OK;
+}
+
+void
+asd_smp_request_done(struct asd_softc *asd,
+ struct scb *scb, struct asd_done_list *done_listp)
+{
+ struct state_machine_context *sm_contextp;
+ struct discover_context *discover_contextp;
+
+ sm_contextp = (struct state_machine_context *)scb->io_ctx;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_scb_internal_done(asd, scb, done_listp);
+
+#if 0
+ if (done_listp->opcode != TASK_COMP_WO_ERR) {
+ printk("%s:%d: opcode = 0x%x\n", __FUNCTION__, __LINE__,
+ done_listp->opcode);
+ }
+#endif
+
+ discover_contextp->resid_len = 0;
+
+ /*
+ * TODO: need better return value here
+ */
+ switch (done_listp->opcode) {
+ case TASK_COMP_WO_ERR:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ break;
+#ifdef SMP_UNDERRUN_WORKAROUND
+ case TASK_COMP_W_UNDERRUN:
+ printk("Ignoring UNDERRUN condition on SMP request\n");
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ discover_contextp->resid_len =
+ asd_le32toh(done_listp->stat_blk.data.res_len);
+ break;
+#endif
+#ifdef SMP_OVERRUN_WORKAROUND
+ case TASK_COMP_W_OVERRUN:
+ /*
+ * This wasn't fixed in B0, so it will be investigated more.
+ */
+ //printk("Ignoring OVERRUN condition on SMP request - ");
+ //printk("should be fixed in B0\n");
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ break;
+#endif
+ case TASK_F_W_SMPRSP_TO:
+ case TASK_F_W_SMP_XMTRCV_ERR:
+ discover_contextp->openStatus = OPEN_REJECT_BAD_DESTINATION;
+ break;
+ case TASK_ABORTED_BY_ITNL_EXP:
+ switch (done_listp->stat_blk.itnl_exp.reason) {
+ case TASK_F_W_PHY_DOWN:
+ case TASK_F_W_BREAK_RCVD:
+ case TASK_F_W_OPEN_TO:
+ discover_contextp->openStatus =
+ OPEN_REJECT_BAD_DESTINATION;
+ break;
+
+ case TASK_F_W_OPEN_REJECT:
+ discover_contextp->openStatus =
+ OPEN_REJECT_BAD_DESTINATION;
+#if 0
+ discover_contextp->openStatus =
+ OPEN_REJECT_RATE_NOT_SUPPORTED;
+ discover_contextp->openStatus =
+ OPEN_REJECT_NO_DESTINATION;
+ discover_contextp->openStatus =
+ OPEN_REJECT_PATHWAY_BLOCKED;
+ discover_contextp->openStatus =
+ OPEN_REJECT_PROTOCOL_NOT_SUPPORTED;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_ABANDON;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_CONTINUE;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_INITIALIZE;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_STOP;
+ discover_contextp->openStatus = OPEN_REJECT_RETRY;
+ discover_contextp->openStatus =
+ OPEN_REJECT_STP_RESOURCES_BUSY;
+ discover_contextp->openStatus =
+ OPEN_REJECT_WRONG_DESTINATION;
+#endif
+ break;
+ }
+ break;
+ case TASK_CLEARED:
+ /* Aborted command. Status needs to be changed .... */
+ break;
+ default:
+ discover_contextp->openStatus = OPEN_REJECT_BAD_DESTINATION;
+ break;
+ }
+
+ sm_contextp->wakeup_state_machine(sm_contextp);
+}
+
+DISCOVER_RESULTS
+asd_sata_identify_request(struct state_machine_context *sm_contextp,
+ struct asd_target *target)
+{
+ unsigned long flags;
+ struct scb *scb;
+ struct discover_context *discover_contextp;
+ struct asd_target *old_target;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_lock(discover_contextp->asd, &flags);
+
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = asd_hwi_get_scb(discover_contextp->asd, 1)) == NULL) {
+ // TODO - fix this
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_FAILED;
+ }
+
+ scb->flags |= SCB_INTERNAL;
+ scb->platform_data->dev = NULL;
+
+ asd_push_post_stack(discover_contextp->asd, scb, (void *)sm_contextp,
+ asd_sata_identify_request_done);
+
+ list_add_tail(&scb->owner_links,
+ &discover_contextp->asd->platform_data->pending_os_scbs);
+
+ if (asd_sata_identify_build(discover_contextp->asd, target, scb) != 0) {
+
+ asd_hwi_free_scb(discover_contextp->asd, scb);
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_FAILED;
+ }
+
+ scb->flags |= SCB_ACTIVE;
+
+ /*
+ * We want to use the flow control of the device queue if possible.
+ * Look through the old/new discover list for this target.
+ * If target exists, see if device exists.
+ */
+ old_target = asd_find_target(discover_contextp->asd->old_discover_listp,
+ target->ddb_profile.sas_addr);
+
+ if (old_target == NULL) {
+ old_target =
+ asd_find_target(discover_contextp->asd->discover_listp,
+ target->ddb_profile.sas_addr);
+ }
+
+ /*
+ * sata devices should have only one lun, check lun 0.
+ */
+ if (old_target != NULL) {
+
+ if (old_target->devices[0] != NULL) {
+
+ scb->platform_data->dev = target->devices[0];
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ if ((asd_discovery_queue_cmd(discover_contextp->asd,
+ scb, old_target,
+ old_target->devices[0]))) {
+
+ asd_hwi_free_scb(discover_contextp->asd, scb);
+
+ return DISCOVER_FAILED;
+ }
+
+ return DISCOVER_OK;
+ }
+ }
+
+ asd_hwi_post_scb(discover_contextp->asd, scb);
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_OK;
+}
+
+static int
+asd_discovery_queue_cmd(struct asd_softc *asd,
+ struct scb *scb, struct asd_target *targ,
+ struct asd_device *dev)
+{
+ struct scsi_cmnd *cmd;
+ u_long flags;
+
+ if ((cmd = asd_alloc_mem(sizeof(struct scsi_cmnd), GFP_ATOMIC)) == NULL) {
+ return -ENOMEM;
+ }
+
+ /* indicate its discovery SM generated scsi_cmnd */
+ cmd->sc_magic = ASD_CSMI_COMMAND;
+
+ /* stick SCB into scsi_cmnd */
+ cmd->host_scribble = (unsigned char *)scb;
+
+ asd_lock(asd, &flags);
+ list_add_tail(&((union asd_cmd *)cmd)->acmd_links, &dev->busyq);
+ if ((dev->flags & ASD_DEV_ON_RUN_LIST) == 0) {
+ list_add_tail(&dev->links, &asd->platform_data->device_runq);
+ dev->flags |= ASD_DEV_ON_RUN_LIST;
+
+ asd_run_device_queues(asd);
+ }
+ asd_unlock(asd, &flags);
+
+ return 0;
+}
+
+void
+asd_sata_identify_request_done(struct asd_softc *asd,
+ struct scb *scb,
+ struct asd_done_list *done_listp)
+{
+ struct state_machine_context *sm_contextp;
+ struct discover_context *discover_contextp;
+
+ sm_contextp = (struct state_machine_context *)scb->io_ctx;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ /*
+ * If this is not NULL, scb was sent via device queue.
+ */
+ if (scb->platform_data->dev) {
+ scb->platform_data->dev->active--;
+ scb->platform_data->dev->openings++;
+ scb->platform_data->dev->commands_issued--;
+ }
+
+ asd_scb_internal_done(asd, scb, done_listp);
+
+ discover_contextp->resid_len = 0;
+
+ /*
+ * TODO: need better return value here
+ */
+ switch (done_listp->opcode) {
+ case TASK_COMP_WO_ERR:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ break;
+
+ case TASK_COMP_W_UNDERRUN:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ discover_contextp->resid_len =
+ asd_le32toh(done_listp->stat_blk.data.res_len);
+ break;
+
+ case TASK_ABORTED_BY_ITNL_EXP:
+ switch (done_listp->stat_blk.itnl_exp.reason) {
+ case TASK_F_W_PHY_DOWN:
+ case TASK_F_W_BREAK_RCVD:
+ case TASK_F_W_OPEN_TO:
+ discover_contextp->openStatus =
+ OPEN_REJECT_BAD_DESTINATION;
+ break;
+
+ case TASK_F_W_OPEN_REJECT:
+ discover_contextp->openStatus =
+ OPEN_REJECT_BAD_DESTINATION;
+#if 0
+ discover_contextp->openStatus =
+ OPEN_REJECT_RATE_NOT_SUPPORTED;
+ discover_contextp->openStatus =
+ OPEN_REJECT_NO_DESTINATION;
+ discover_contextp->openStatus =
+ OPEN_REJECT_PATHWAY_BLOCKED;
+ discover_contextp->openStatus =
+ OPEN_REJECT_PROTOCOL_NOT_SUPPORTED;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_ABANDON;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_CONTINUE;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_INITIALIZE;
+ discover_contextp->openStatus =
+ OPEN_REJECT_RESERVE_STOP;
+ discover_contextp->openStatus = OPEN_REJECT_RETRY;
+ discover_contextp->openStatus =
+ OPEN_REJECT_STP_RESOURCES_BUSY;
+ discover_contextp->openStatus =
+ OPEN_REJECT_WRONG_DESTINATION;
+#endif
+ break;
+ }
+ break;
+ case TASK_CLEARED:
+ /* Aborted command. Status needs to be changed .... */
+ break;
+ default:
+ discover_contextp->openStatus = OPEN_REJECT_BAD_DESTINATION;
+ break;
+ }
+
+ sm_contextp->wakeup_state_machine(sm_contextp);
+}
+
+DISCOVER_RESULTS
+asd_sata_configure_features(struct state_machine_context *sm_contextp,
+ struct asd_target *target,
+ uint8_t feature, uint8_t sector_count)
+{
+ unsigned long flags;
+ struct scb *scb;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_lock(discover_contextp->asd, &flags);
+
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = asd_hwi_get_scb(discover_contextp->asd, 1)) == NULL) {
+ // TODO - fix this
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_FAILED;
+ }
+
+ scb->flags |= SCB_INTERNAL;
+ scb->platform_data->dev = NULL;
+ scb->platform_data->targ = target;
+
+ list_add_tail(&scb->owner_links,
+ &discover_contextp->asd->platform_data->pending_os_scbs);
+
+ if (asd_sata_set_features_build(discover_contextp->asd, target, scb,
+ feature, sector_count) != 0) {
+
+ asd_hwi_free_scb(discover_contextp->asd, scb);
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_FAILED;
+ }
+
+ asd_push_post_stack(discover_contextp->asd, scb, (void *)sm_contextp,
+ asd_sata_configure_features_done);
+
+ scb->flags |= SCB_ACTIVE;
+
+ asd_hwi_post_scb(discover_contextp->asd, scb);
+
+ asd_unlock(discover_contextp->asd, &flags);
+
+ return DISCOVER_OK;
+}
+
+void
+asd_sata_configure_features_done(struct asd_softc *asd,
+ struct scb *scb,
+ struct asd_done_list *done_listp)
+{
+ struct state_machine_context *sm_contextp;
+ struct discover_context *discover_contextp;
+
+ sm_contextp = (struct state_machine_context *)scb->io_ctx;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ asd_scb_internal_done(asd, scb, done_listp);
+
+ discover_contextp->resid_len = 0;
+
+ /*
+ * TODO: need better return value here
+ */
+ switch (done_listp->opcode) {
+ case TASK_COMP_WO_ERR:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ break;
+
+ case TASK_COMP_W_UNDERRUN:
+ discover_contextp->openStatus = OPEN_ACCEPT;
+ discover_contextp->resid_len =
+ asd_le32toh(done_listp->stat_blk.data.res_len);
+ break;
+
+ case TASK_ABORTED_BY_ITNL_EXP:
+ switch (done_listp->stat_blk.itnl_exp.reason) {
+ case TASK_F_W_PHY_DOWN:
+ case TASK_F_W_BREAK_RCVD:
+ case TASK_F_W_OPEN_TO:
+ discover_contextp->openStatus =
+ OPEN_REJECT_BAD_DESTINATION;
+ break;
+
+ case TASK_F_W_OPEN_REJECT:
+ discover_contextp->openStatus =
+ OPEN_REJECT_BAD_DESTINATION;
+ break;
+ }
+ break;
+ default:
+ discover_contextp->openStatus = OPEN_REJECT_BAD_DESTINATION;
+ break;
+ }
+
+ sm_contextp->wakeup_state_machine(sm_contextp);
+}
+
+struct asd_target *asd_discover_get_target(struct state_machine_context
+ *sm_contextp,
+ uint8_t * dest_sas_address,
+ struct list_head *old_discover_listp,
+ struct list_head *found_listp,
+ unsigned conn_rate,
+ TRANSPORT_TYPE transport_type)
+{
+ struct asd_target *target;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ target = asd_find_target(old_discover_listp, dest_sas_address);
+
+ if (target != NULL) {
+ /*
+ * This target was previously found.
+ */
+ target->flags |= ASD_TARG_RESEEN;
+
+ target->ddb_profile.conn_rate = conn_rate;
+
+ asd_hwi_build_ddb_site(discover_contextp->asd, target);
+
+ /*
+ * First, take this target off of the general chain of all
+ * targets in the domain.
+ */
+ list_del_init(&target->all_domain_targets);
+
+ return target;
+ }
+
+ /*
+ * See if we haven't already talked to this device during the current
+ * discovery process.
+ */
+ target = asd_find_target(found_listp, dest_sas_address);
+
+ if (target != NULL) {
+ /*
+ * First, take this target off of the general chain of all
+ * targets in the domain.
+ */
+ list_del_init(&target->all_domain_targets);
+
+ return target;
+ }
+
+ target = asd_alloc_target(discover_contextp->asd,
+ discover_contextp->port);
+
+ /*
+ * make sure we only do this if the allocation is successful
+ */
+ if (target == NULL) {
+ return NULL;
+ }
+
+ target->domain =
+ discover_contextp->asd->platform_data->domains[discover_contextp->
+ port->id];
+
+ target->ddb_profile.conn_rate = conn_rate;
+
+ target->ddb_profile.itnl_const = ITNL_TIMEOUT_CONST;
+
+ target->parent = NULL;
+
+ /*
+ * Set to 1 for SSP, STP, and SMP device ports. 0 for all SATA direct
+ * attached ports. Every device that is initialized by this routine
+ * is not a SATA direct attach.
+ */
+ target->ddb_profile.open_affl = OPEN_AFFILIATION;
+
+ memcpy(target->ddb_profile.sas_addr, dest_sas_address, SAS_ADDR_LEN);
+
+ if (transport_type == ASD_TRANSPORT_STP) {
+ //TODO: get SUPPORTS_AFFILIATION out of SMP request
+ target->ddb_profile.open_affl |=
+ (STP_AFFILIATION | SUPPORTS_AFFILIATION);
+ }
+
+ asd_hwi_hash(target->ddb_profile.sas_addr,
+ target->ddb_profile.hashed_sas_addr);
+
+ // TODO: - we need to allocate this from LRU DDB list
+ // (doesn't exist yet)
+ asd_hwi_setup_ddb_site(discover_contextp->asd, target);
+
+ return target;
+}
+
+DISCOVER_RESULTS
+asd_find_subtractive_phy(struct state_machine_context * sm_contextp,
+ struct asd_target * expander,
+ uint8_t * subtractiveSASAddress,
+ uint8_t * attachedPhyIdentifier,
+ unsigned *conn_rate, uint8_t * phyIdentifier)
+{
+ struct Discover *discover;
+ DISCOVER_RESULTS result;
+ uint8_t phyCount;
+ unsigned foundSubtractive;
+
+ SAS_ZERO(subtractiveSASAddress);
+ *attachedPhyIdentifier = 0;
+
+ foundSubtractive = 0;
+
+ /*
+ * walk through all the phys of this expander
+ */
+ for (phyCount = 0; phyCount < expander->num_phys; phyCount++) {
+
+ /*
+ * this is just a pointer helper
+ */
+ discover = &(expander->Phy[phyCount].Result);
+
+ /*
+ * look for phys with edge or fanout devices attached...
+ */
+ if ((discover->RoutingAttribute != SUBTRACTIVE) ||
+ ((discover->AttachedDeviceType !=
+ EDGE_EXPANDER_DEVICE) &&
+ (discover->AttachedDeviceType !=
+ FANOUT_EXPANDER_DEVICE))) {
+
+ continue;
+ }
+
+ /*
+ * make sure all the subtractive phys point to
+ * the same address when we are connected to an
+ * expander device
+ */
+ if (SAS_ISZERO(subtractiveSASAddress)) {
+
+ SASCPY(subtractiveSASAddress,
+ discover->AttachedSASAddress);
+
+ *attachedPhyIdentifier =
+ discover->AttachedPhyIdentifier;
+
+ result = DISCOVER_OK;
+
+ *conn_rate = discover->NegotiatedPhysicalLinkRate;
+
+ *phyIdentifier = phyCount;
+
+ foundSubtractive = 1;
+ } else if (!SAS_ISEQUAL(subtractiveSASAddress,
+ discover->AttachedSASAddress)) {
+
+ /*
+ * the addresses don't match...
+ * problem...
+ */
+ asd_log(ASD_DBG_ERROR, "\n"
+ "topology error, diverging "
+ "subtractive phys"
+ ", '%0llx' != '%0llx' \n",
+ *((uint64_t *) subtractiveSASAddress),
+ *((uint64_t *) discover->AttachedSASAddress));
+
+ return DISCOVER_FAILED;
+ }
+ }
+
+ if (foundSubtractive == 0) {
+ return DISCOVER_FINISHED;
+ }
+
+ return DISCOVER_OK;
+}
+
+/*
+ * find the table structure associated with a specific SAS address
+ */
+struct asd_target *asd_find_target(struct list_head *target_list,
+ uint8_t * SASAddress)
+{
+ struct asd_target *target;
+
+ /*
+ * walk the list of expanders, when we find the one that matches, stop
+ */
+ list_for_each_entry(target, target_list, all_domain_targets) {
+ /*
+ * do the SASAdresses match
+ */
+ if (SAS_ISEQUAL(target->ddb_profile.sas_addr, SASAddress)) {
+ return target;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * find the table structure associated with a specific target identity.
+ */
+struct asd_target *asd_find_target_ident(struct list_head *target_list,
+ struct asd_target *check_target)
+{
+ struct asd_target *target;
+
+ /*
+ * walk the list of expanders, when we find the one that matches, stop
+ */
+ list_for_each_entry(target, target_list, all_domain_targets) {
+
+ /*
+ * Make sure we don't match ourselves.
+ */
+ if (check_target == target) {
+ continue;
+ }
+
+ /*
+ * It can't possibly be a match if the transport types don't
+ * match.
+ */
+ if (check_target->transport_type != target->transport_type) {
+ continue;
+ }
+
+ switch (target->transport_type) {
+ case ASD_TRANSPORT_SSP:
+ if (target->scsi_cmdset.ident_len !=
+ check_target->scsi_cmdset.ident_len) {
+
+ continue;
+ }
+
+ if (memcmp(target->scsi_cmdset.ident,
+ check_target->scsi_cmdset.ident,
+ target->scsi_cmdset.ident_len) == 0) {
+
+ return target;
+ }
+ break;
+
+ case ASD_TRANSPORT_STP:
+ if (SAS_ISEQUAL(target->ddb_profile.sas_addr,
+ check_target->ddb_profile.sas_addr)) {
+
+ return target;
+ }
+ break;
+
+ default:
+ continue;
+ }
+
+ }
+
+ return NULL;
+}
+
+struct asd_target *asd_find_multipath(struct asd_softc *asd,
+ struct asd_target *target)
+{
+ struct asd_port *port;
+ unsigned port_id;
+ struct asd_target *multipath_target;
+
+ /*
+ * Check to make sure that this same device hasn't been exposed to the
+ * OS on a different port.
+ */
+ for (port_id = 0; port_id < asd->hw_profile.max_ports; port_id++) {
+
+ port = asd->port_list[port_id];
+
+ multipath_target = asd_find_target_ident(&port->targets,
+ target);
+
+ if (multipath_target != NULL) {
+ return multipath_target;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ *
+ * this routine searches the subtractive phys for the upstream expander address
+ *
+ */
+static int
+asd_upstream_expander(struct asd_target *expander,
+ uint8_t * SASAddress, uint8_t * PhyIdentifier)
+{
+ struct Discover *discover;
+ uint8_t phyCount;
+ int found;
+
+ found = 0;
+
+ /*
+ * walk through all the phys of this expander, searching for
+ * subtractive phys return the SASAddress and PhyIdentifier for the
+ * first subtractive phy encountered, they should all be the same if
+ * they have anything attached
+ */
+ for (phyCount = 0; phyCount < expander->num_phys; phyCount++) {
+ /*
+ * this is just a pointer helper
+ */
+ discover = &(expander->Phy[phyCount].Result);
+
+ /*
+ * look for phys with edge or fanout devices attached...
+ */
+ if ((discover->RoutingAttribute == SUBTRACTIVE) &&
+ ((discover->AttachedDeviceType == EDGE_EXPANDER_DEVICE) ||
+ (discover->AttachedDeviceType ==
+ FANOUT_EXPANDER_DEVICE))) {
+
+ SASCPY(SASAddress, discover->AttachedSASAddress);
+
+ *PhyIdentifier = discover->AttachedPhyIdentifier;
+
+ found = 1;
+
+ break;
+ }
+ }
+
+ return found;
+}
+
+/*
+ * this routine determines whether a SAS address is directly attached to
+ * an expander
+ */
+static int
+asd_direct_attached(struct asd_target *expander, uint8_t * SASAddress)
+{
+ int direct;
+ uint8_t phyCount;
+
+ direct = 0;
+
+ for (phyCount = 0; phyCount < expander->num_phys; phyCount++) {
+
+ /*
+ * did we find the address attached locally
+ */
+ if (*((uint64_t *) SASAddress) ==
+ *((uint64_t *) expander->Phy[phyCount].
+ Result.AttachedSASAddress)) {
+
+ direct = 1;
+ break;
+ }
+ }
+
+ return direct;
+}
+
+/*
+ * this routine determines whether the SAS address, can be optimized out
+ * of the route table.
+ *
+ * expander: the expander whose route table we are configuring.
+ *
+ * discover: the response to the discovery request for the device attached
+ * to the phy of the expander that we are trying to configure
+ * into "expander's" route table.
+ */
+static int
+asd_qualified_address(struct asd_target *expander,
+ uint8_t PhyIdentifier,
+ struct Discover *discover, uint8_t * DisableRouteEntry)
+{
+ int qualified;
+ uint16_t routeIndex;
+ uint8_t *sas_address;
+
+ qualified = 1;
+
+ if (DiscoverAlgorithm != SAS_UNIQUE_LEVEL_DESCENT) {
+ return qualified;
+ }
+
+ /*
+ * leave in any entries that are direct routing attribute,
+ * assumes that they are slots that will be filled by end
+ * devices, if it is not direct, then filter out any empty
+ * connections, connections that match the expander we are
+ * configuring and connections that are truly direct attached
+ */
+ if (!SAS_ISZERO(discover->AttachedSASAddress) &&
+ !SAS_ISEQUAL(discover->AttachedSASAddress,
+ expander->ddb_profile.sas_addr) &&
+ (!asd_direct_attached(expander, discover->AttachedSASAddress))) {
+
+ if (discover->RoutingAttribute == DIRECT) {
+ /*
+ * if this is a phy that is has a direct
+ * routing attribute then, have it consume an
+ * entry, it may be filled in at any time
+ */
+ } else {
+ for (routeIndex = 0; routeIndex <
+ expander->num_route_indexes; routeIndex++) {
+
+ sas_address = ROUTE_ENTRY(expander,
+ PhyIdentifier,
+ routeIndex);
+
+ if (SAS_ISEQUAL(sas_address,
+ discover->AttachedSASAddress)) {
+
+ qualified = 0;
+
+ break;
+ }
+ }
+ }
+ } else if (SAS_ISZERO(discover->AttachedSASAddress)) {
+ /*
+ * if a 0 address, then assume it is an
+ * empty slot that can be filled at any time,
+ * this keeps things positionally stable for most
+ * reasonable topologies
+ */
+ *DisableRouteEntry = DISABLED;
+ } else {
+ qualified = 0;
+ }
+
+ return qualified;
+}
+
+void
+asd_add_child(struct asd_port *port,
+ struct asd_target *parent, struct asd_target *child)
+{
+ /*
+ * Check to make sure that this particular target hasn't already been
+ * put in the tree, or that it isn't the top of the tree.
+ */
+ if ((port->tree_root == child) || (child->parent != NULL)) {
+ return;
+ }
+
+ child->parent = parent;
+
+ if (child->parent != NULL) {
+ list_add_tail(&child->siblings, &parent->children);
+ }
+}
+
+#define DUMP_LIST(a) dump_list(__FUNCTION__, __LINE__, #a, a);
+void
+dump_list(char *function, unsigned line, char *s, struct list_head *target_list)
+{
+ struct asd_target *target;
+
+ printk("%s:%d: dumping list %s\n", function, line, s);
+ /*
+ * walk the list of expanders, when we find the one that matches, stop
+ */
+ list_for_each_entry(target, target_list, all_domain_targets) {
+ printk("%s:%d: %llx\n", __FUNCTION__, __LINE__,
+ *((uint64_t *) target->ddb_profile.sas_addr));
+ }
+}
+
+DISCOVER_RESULTS
+asd_configure_device(struct state_machine_context *sm_contextp,
+ struct asd_target *parentExpander,
+ struct Discover *discover,
+ struct list_head *discover_listp,
+ struct list_head *found_listp,
+ struct list_head *old_discover_listp, unsigned conn_rate)
+{
+ struct asd_target *target;
+ COMMAND_SET_TYPE command_set_type;
+ DEVICE_PROTOCOL_TYPE device_protocol_type;
+ MANAGEMENT_TYPE management_type;
+ TRANSPORT_TYPE transport_type;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ /*
+ * TODO: right now we are ignoring all virtual phy devices.
+ */
+ if ((discover->InitiatorBits != 0) || (discover->VirtualPhy == 1)) {
+ return DISCOVER_OK;
+ }
+
+ switch (conn_rate) {
+ case RATE_UNKNOWN:
+ case PHY_DISABLED:
+ case PHY_FAILED:
+ return DISCOVER_FAILED;
+
+ case SPINUP_HOLD_OOB:
+ printk("found SPINUP_HOLD\n");
+ break;
+ case GBPS_1_5:
+ case GBPS_3_0:
+ /*
+ * Nothing special to do.
+ */
+ break;
+ }
+
+ /*
+ * This routine is only called for end devices.
+ */
+ ASSERT((discover->TargetBits & SMP_TGT_PORT) == 0);
+
+ if (discover->TargetBits & SSP_TGT_PORT) {
+
+ command_set_type = ASD_COMMAND_SET_SCSI;
+ device_protocol_type = ASD_DEVICE_PROTOCOL_SCSI;
+ transport_type = ASD_TRANSPORT_SSP;
+ management_type = ASD_DEVICE_END;
+
+ } else if (discover->TargetBits & STP_TGT_PORT) {
+ /*
+ * We don't know the command set yet (could be ATAPI or ATA)
+ * We won't know until IDENTIFY / PIDENTIFY.
+ */
+ command_set_type = ASD_COMMAND_SET_UNKNOWN;
+ device_protocol_type = ASD_DEVICE_PROTOCOL_ATA;
+ transport_type = ASD_TRANSPORT_STP;
+ management_type = ASD_DEVICE_END;
+
+ } else if (discover->TargetBits & SATA_TGT_PORT) {
+ /*
+ * We don't know the command set yet (could be ATAPI or ATA)
+ * We won't know until IDENTIFY / PIDENTIFY.
+ */
+ /*
+ * An "attached SATA host" which is "outside of the
+ * scope of this standard"
+ * T10/1562-D Rev 5 - 10.4.3.5 pg. 340 - 7/9/2003
+ */
+ command_set_type = ASD_COMMAND_SET_UNKNOWN;
+ device_protocol_type = ASD_DEVICE_PROTOCOL_ATA;
+ transport_type = ASD_TRANSPORT_STP;
+ management_type = ASD_DEVICE_END;
+ } else {
+ command_set_type = ASD_COMMAND_SET_UNKNOWN;
+ device_protocol_type = ASD_DEVICE_PROTOCOL_UNKNOWN;
+ transport_type = ASD_TRANSPORT_UNKNOWN;
+ management_type = ASD_DEVICE_UNKNOWN;
+ }
+
+ target = asd_discover_get_target(sm_contextp,
+ discover->AttachedSASAddress,
+ old_discover_listp,
+ found_listp, conn_rate,
+ transport_type);
+
+ if (target == NULL) {
+
+ printk("couldn't allocate target\n");
+
+ return DISCOVER_FAILED;
+ }
+
+ target->command_set_type = command_set_type;
+ target->device_protocol_type = device_protocol_type;
+ target->management_type = management_type;
+ target->transport_type = transport_type;
+
+ /*
+ * Add the device to the tree.
+ */
+ asd_add_child(discover_contextp->port, parentExpander, target);
+
+ list_add_tail(&target->all_domain_targets, discover_listp);
+
+ return DISCOVER_OK;
+}
+
+void
+asd_destroy_discover_list(struct asd_softc *asd,
+ struct list_head *discover_list)
+{
+ struct asd_target *target;
+ struct asd_target *tmp_target;
+
+ list_for_each_entry_safe(target, tmp_target, discover_list,
+ all_domain_targets) {
+
+ list_del_init(&target->all_domain_targets);
+
+ asd_free_ddb(asd, target->ddb_profile.conn_handle);
+
+ asd_free_target(asd, target);
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+DISCOVER_RESULTS
+asd_issue_discover_request(struct state_machine_context *sm_contextp,
+ struct asd_target *expander, unsigned phyIndex)
+{
+ DISCOVER_RESULTS results;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ memset(discover_contextp->SMPRequestFrame, 0,
+ sizeof(struct SMPRequest));
+
+ discover_contextp->SMPRequestFrame->SMPFrameType = SMP_REQUEST_FRAME;
+ discover_contextp->SMPRequestFrame->Function = DISCOVER;
+ discover_contextp->SMPRequestFrame->Request.Discover.PhyIdentifier =
+ phyIndex;
+
+ /*
+ * get the discover information for each phy
+ */
+ results = asd_smp_request(sm_contextp, expander,
+ sizeof(struct SMPRequestPhyInput),
+ sizeof(struct SMPResponseDiscover));
+
+ return results;
+}
+
+DISCOVER_RESULTS
+asd_issue_discover_request_post(struct state_machine_context * sm_contextp,
+ struct asd_target * expander, unsigned phyIndex)
+{
+ struct Discover *discover;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ if ((discover_contextp->openStatus != OPEN_ACCEPT) ||
+ (discover_contextp->SMPResponseFrame->FunctionResult !=
+ SMP_FUNCTION_ACCEPTED)) {
+
+ /*
+ * if we had a problem on this link, then don't
+ * bother to do anything else, production code,
+ * should be more robust...
+ */
+ // asd_log(ASD_DBG_ERROR, "\n"
+ printk("discover error, %02Xh at %0llx\n",
+ discover_contextp->SMPResponseFrame->FunctionResult,
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ return DISCOVER_FAILED;
+ }
+
+ discover = &(expander->Phy[phyIndex].Result);
+
+ /*
+ * copy the result into the topology table
+ */
+ memcpy((void *)&(expander->Phy[phyIndex]),
+ (void *)&discover_contextp->SMPResponseFrame->Response.Discover,
+ sizeof(struct SMPResponseDiscover));
+
+#if 0
+ printk("----------\n");
+ printk("PhyIdentifier 0x%x SASAddress 0x%llx\n",
+ phyIndex, *((uint64_t *) discover->SASAddress));
+ printk("AttachedSASAddress 0x%llx AttachedPhyIdentifier 0x%x\n",
+ *((uint64_t *) discover->AttachedSASAddress),
+ discover->AttachedPhyIdentifier);
+ printk("InitiatorBits 0x%x TargetBits 0x%x\n",
+ discover->InitiatorBits, discover->TargetBits);
+#endif
+
+ return DISCOVER_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * this function gets the report general and discover information for
+ * a specific expander. The discover process should begin at the subtractive
+ * boundary and progress downstream.
+ *
+ * If the dest_sas_address is an expander, the expander structure is returned
+ * in retExpander.
+ */
+DISCOVER_RESULTS
+asd_issue_report_general(struct state_machine_context * sm_contextp,
+ uint8_t * dest_sas_address,
+ uint8_t conn_rate,
+ uint8_t attachedDeviceType,
+ struct list_head * old_discover_listp,
+ struct list_head * found_listp,
+ struct asd_target ** retExpander)
+{
+ struct asd_target *expander;
+ DISCOVER_RESULTS results;
+ COMMAND_SET_TYPE command_set_type;
+ DEVICE_PROTOCOL_TYPE device_protocol_type;
+ TRANSPORT_TYPE transport_type;
+ MANAGEMENT_TYPE management_type;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ expander = NULL;
+
+ memset(discover_contextp->SMPRequestFrame, 0,
+ sizeof(struct SMPRequest));
+
+ discover_contextp->SMPRequestFrame->SMPFrameType = SMP_REQUEST_FRAME;
+ discover_contextp->SMPRequestFrame->Function = REPORT_GENERAL;
+
+ switch (attachedDeviceType) {
+ case EDGE_EXPANDER_DEVICE:
+ command_set_type = ASD_COMMAND_SET_SMP;
+ device_protocol_type = ASD_DEVICE_PROTOCOL_SMP;
+ transport_type = ASD_TRANSPORT_SMP;
+ management_type = ASD_DEVICE_EDGE_EXPANDER;
+ break;
+
+ case FANOUT_EXPANDER_DEVICE:
+ management_type = ASD_DEVICE_FANOUT_EXPANDER;
+ device_protocol_type = ASD_DEVICE_PROTOCOL_SMP;
+ command_set_type = ASD_COMMAND_SET_SMP;
+ transport_type = ASD_TRANSPORT_SMP;
+ break;
+
+ default:
+ /*
+ * This should never happen.
+ */
+ return DISCOVER_FAILED;
+ }
+
+ expander = asd_discover_get_target(sm_contextp, dest_sas_address,
+ old_discover_listp, found_listp,
+ conn_rate, transport_type);
+
+ /*
+ * make sure we only do this if the allocation is successful
+ */
+ if (expander == NULL) {
+ return DISCOVER_FAILED;
+ }
+
+ expander->command_set_type = command_set_type;
+ expander->device_protocol_type = device_protocol_type;
+ expander->transport_type = transport_type;
+ expander->management_type = management_type;
+
+ /*
+ * get the report general information for the expander
+ */
+ results = asd_smp_request(sm_contextp, expander,
+ sizeof(struct SMPRequestGeneralInput),
+ sizeof(struct SMPResponseReportGeneral));
+
+ *retExpander = expander;
+
+ return results;
+}
+
+DISCOVER_RESULTS
+asd_issue_report_general_post(struct state_machine_context * sm_contextp,
+ struct asd_target * expander)
+{
+ uint8_t phyCount;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ phyCount = 0;
+
+ /*
+ * the assumptions we made were exceeded, need to bump limits...
+ */
+ if ((discover_contextp->openStatus != OPEN_ACCEPT) ||
+ (discover_contextp->SMPResponseFrame->FunctionResult !=
+ SMP_FUNCTION_ACCEPTED)) {
+ /*
+ * if we had a problem getting report general for this expander,
+ * something is wrong, can't go any further down this path...
+ * production code, should be more robust...
+ */
+ asd_log(ASD_DBG_ERROR, "\n"
+ "report general error, open %02Xh result %02Xh at "
+ "0x%0llx\n",
+ discover_contextp->openStatus,
+ discover_contextp->SMPResponseFrame->FunctionResult,
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ asd_free_ddb(discover_contextp->asd,
+ expander->ddb_profile.conn_handle);
+
+ asd_free_target(discover_contextp->asd, expander);
+
+ return DISCOVER_FAILED;
+ }
+
+ if (discover_contextp->SMPResponseFrame->Response.
+ ReportGeneral.NumberOfPhys > MAXIMUM_EXPANDER_PHYS) {
+
+ asd_log(ASD_DBG_ERROR, "\n"
+ "report general error"
+ ", NumberOfPhys %d exceeded limit %d on %0llx\n",
+ discover_contextp->SMPResponseFrame->
+ Response.ReportGeneral.NumberOfPhys,
+ MAXIMUM_EXPANDER_PHYS,
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ asd_free_ddb(discover_contextp->asd,
+ expander->ddb_profile.conn_handle);
+
+ asd_free_target(discover_contextp->asd, expander);
+
+ return DISCOVER_FAILED;
+ }
+
+ expander->num_phys = discover_contextp->SMPResponseFrame->
+ Response.ReportGeneral.NumberOfPhys;
+
+ expander->num_route_indexes =
+ asd_be16toh(discover_contextp->SMPResponseFrame->Response.
+ ReportGeneral.ExpanderRouteIndexes);
+
+ if (expander->Phy != NULL) {
+ asd_free_mem(expander->Phy);
+ }
+
+ expander->Phy =
+ (struct SMPResponseDiscover *)
+ asd_alloc_mem(sizeof(struct SMPResponseDiscover) *
+ expander->num_phys, GFP_KERNEL);
+
+ if (expander->Phy == NULL) {
+
+ printk("unable to allocate memory\n");
+
+ asd_free_ddb(discover_contextp->asd,
+ expander->ddb_profile.conn_handle);
+
+ asd_free_target(discover_contextp->asd, expander);
+
+ return DISCOVER_FAILED;
+ }
+
+ if (expander->num_route_indexes != 0) {
+
+ if (expander->RouteTable != NULL) {
+ asd_free_mem(expander->RouteTable);
+ }
+
+ expander->RouteTable =
+ (uint8_t *) asd_alloc_mem(SAS_ADDR_LEN *
+ expander->num_phys *
+ expander->num_route_indexes,
+ GFP_KERNEL);
+
+ if (expander->route_indexes != NULL) {
+ asd_free_mem(expander->route_indexes);
+ }
+
+ expander->route_indexes =
+ asd_alloc_mem(expander->num_route_indexes
+ * sizeof(uint16_t), GFP_KERNEL);
+
+ memset(expander->route_indexes, 0,
+ expander->num_route_indexes * sizeof(uint16_t));
+ }
+
+ return DISCOVER_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+DISCOVER_RESULTS
+asd_issue_report_manufacturer_info(struct state_machine_context * sm_contextp,
+ struct asd_target * expander)
+{
+ DISCOVER_RESULTS results;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ memset(discover_contextp->SMPRequestFrame, 0,
+ sizeof(struct SMPRequest));
+
+ discover_contextp->SMPRequestFrame->SMPFrameType = SMP_REQUEST_FRAME;
+ discover_contextp->SMPRequestFrame->Function =
+ REPORT_MANUFACTURER_INFORMATION;
+
+ results = asd_smp_request(sm_contextp, expander,
+ sizeof(struct SMPRequestGeneralInput),
+ sizeof(struct
+ SMPResponseReportManufacturerInfo));
+
+ return results;
+}
+
+DISCOVER_RESULTS
+asd_issue_report_manufacturer_info_post(struct state_machine_context *
+ sm_contextp,
+ struct asd_target * expander)
+{
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ /*
+ * the assumptions we made were exceeded, need to bump limits...
+ */
+ if ((discover_contextp->openStatus != OPEN_ACCEPT) ||
+ (discover_contextp->SMPResponseFrame->FunctionResult !=
+ SMP_FUNCTION_ACCEPTED)) {
+ /*
+ * if we had a problem getting report general for this expander,
+ * something is wrong, can't go any further down this path...
+ * production code, should be more robust...
+ */
+ asd_log(ASD_DBG_ERROR, "\n"
+ "get manufacturer information error, "
+ "open %02Xh result %02Xh at "
+ "0x%0llx\n",
+ discover_contextp->openStatus,
+ discover_contextp->SMPResponseFrame->FunctionResult,
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ asd_free_ddb(discover_contextp->asd,
+ expander->ddb_profile.conn_handle);
+
+ asd_free_target(discover_contextp->asd, expander);
+
+ return DISCOVER_FAILED;
+ }
+
+ memcpy(&expander->smp_cmdset.manufacturer_info,
+ &discover_contextp->SMPResponseFrame->
+ Response.ReportManufacturerInfo,
+ sizeof(struct SMPResponseReportManufacturerInfo));
+
+#if 0
+ printk("%8.8s|%16.16s|%4.4s\n",
+ expander->smp_cmdset.manufacturer_info.VendorIdentification,
+ expander->smp_cmdset.manufacturer_info.ProductIdentification,
+ expander->smp_cmdset.manufacturer_info.ProductRevisionLevel);
+#endif
+
+ return DISCOVER_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+DISCOVER_RESULTS
+asd_issue_route_config(struct state_machine_context * sm_contextp,
+ struct asd_target * expander,
+ unsigned phyIndex,
+ uint8_t disableRouteEntry, uint8_t * attachedSASAddress)
+{
+ uint16_t index;
+ DISCOVER_RESULTS results;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ index = expander->route_indexes[phyIndex];
+
+ memset(discover_contextp->SMPRequestFrame, 0,
+ sizeof(struct SMPRequest));
+
+ discover_contextp->SMPRequestFrame->SMPFrameType = SMP_REQUEST_FRAME;
+ discover_contextp->SMPRequestFrame->Function =
+ CONFIGURE_ROUTE_INFORMATION;
+
+ discover_contextp->SMPRequestFrame->Request.
+ ConfigureRouteInformation.ExpanderRouteIndex = asd_htobe16(index);
+
+ discover_contextp->SMPRequestFrame->Request.
+ ConfigureRouteInformation.PhyIdentifier = phyIndex;
+
+ discover_contextp->SMPRequestFrame->Request.
+ ConfigureRouteInformation.Configure.DisableRouteEntry =
+ disableRouteEntry;
+
+ SASCPY(discover_contextp->SMPRequestFrame->Request.
+ ConfigureRouteInformation.Configure.RoutedSASAddress,
+ attachedSASAddress);
+
+ /*
+ * configure the route indexes for the
+ * expander with the attached address
+ * information
+ */
+ results = asd_smp_request(sm_contextp, expander,
+ sizeof(struct
+ SMPRequestConfigureRouteInformation),
+ sizeof(struct
+ SMPResponseConfigureRouteInformation));
+
+ return results;
+}
+
+DISCOVER_RESULTS
+asd_issue_route_config_post(struct state_machine_context * sm_contextp,
+ struct asd_target * expander, unsigned phyIndex)
+{
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ /*
+ * the assumptions we made were exceeded, need to bump limits...
+ */
+ if ((discover_contextp->openStatus != OPEN_ACCEPT) ||
+ (discover_contextp->SMPResponseFrame->FunctionResult !=
+ SMP_FUNCTION_ACCEPTED)) {
+ /*
+ * if we had a problem getting report general for this expander,
+ * something is wrong, can't go any further down this path...
+ * production code, should be more robust...
+ */
+ asd_log(ASD_DBG_ERROR, "\n"
+ "route config error, open %02Xh result %02Xh at "
+ "0x%0llx\n",
+ discover_contextp->openStatus,
+ discover_contextp->SMPResponseFrame->FunctionResult,
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ return DISCOVER_FAILED;
+ }
+
+ return DISCOVER_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+DISCOVER_RESULTS
+asd_issue_report_phy_sata(struct state_machine_context * sm_contextp,
+ struct asd_target * expander, unsigned phyIndex)
+{
+ DISCOVER_RESULTS results;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ memset(discover_contextp->SMPRequestFrame, 0,
+ sizeof(struct SMPRequest));
+
+ discover_contextp->SMPRequestFrame->SMPFrameType = SMP_REQUEST_FRAME;
+ discover_contextp->SMPRequestFrame->Function = REPORT_PHY_SATA;
+
+ discover_contextp->SMPRequestFrame->Request.
+ ReportPhySATA.PhyIdentifier = phyIndex;
+
+ results = asd_smp_request(sm_contextp, expander,
+ sizeof(struct SMPRequestPhyInput),
+ sizeof(struct SMPResponseReportPhySATA));
+
+ return results;
+}
+
+DISCOVER_RESULTS
+asd_issue_report_phy_sata_post(struct state_machine_context * sm_contextp,
+ struct asd_target * expander)
+{
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ /*
+ * the assumptions we made were exceeded, need to bump limits...
+ */
+ if ((discover_contextp->openStatus != OPEN_ACCEPT) ||
+ (discover_contextp->SMPResponseFrame->FunctionResult !=
+ SMP_FUNCTION_ACCEPTED)) {
+ /*
+ * if we had a problem getting report general for this expander,
+ * something is wrong, can't go any further down this path...
+ * production code, should be more robust...
+ */
+ asd_log(ASD_DBG_ERROR, "\n"
+ "report phy SATA error, open %02Xh result %02Xh at "
+ "0x%0llx\n",
+ discover_contextp->openStatus,
+ discover_contextp->SMPResponseFrame->FunctionResult,
+ *((uint64_t *) expander->ddb_profile.sas_addr));
+
+ return DISCOVER_FAILED;
+ }
+
+ return DISCOVER_OK;
+}
+
+COMMAND_SET_TYPE asd_sata_get_type(struct adp_dev_to_host_fis * fis)
+{
+ if ((fis->sector_count == 1) && (fis->lba0 == 1) &&
+ (fis->lba1 == 0x14) && (fis->lba2 == 0xeb)) {
+
+ return ASD_COMMAND_SET_ATAPI;
+ }
+
+ return ASD_COMMAND_SET_ATA;
+}
+
+void
+asd_init_sata_direct_attached(struct state_machine_context *sm_contextp,
+ struct asd_target *target)
+{
+ struct asd_phy *phy;
+ struct adp_dev_to_host_fis *fis;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ phy = list_entry(target->src_port->phys_attached.next,
+ struct asd_phy, links);
+
+ fis = (struct adp_dev_to_host_fis *)
+ &phy->bytes_dmaed_rcvd.initial_fis_rcvd.fis[0];
+
+ target->command_set_type = asd_sata_get_type(fis);
+
+ memcpy((void *)&target->device_protocol.
+ ata_device_protocol.initial_fis[0],
+ (void *)fis, sizeof(struct adp_dev_to_host_fis));
+
+ target->ddb_profile.sata_status = fis->status;
+
+ asd_hwi_update_sata(discover_contextp->asd, target);
+}
+
+/* -------------------------------------------------------------------------- */
+
+DISCOVER_RESULTS
+asd_issue_phy_control(struct state_machine_context *sm_contextp,
+ struct asd_target *expander,
+ unsigned phyIndex, unsigned operation)
+{
+ DISCOVER_RESULTS results;
+ struct discover_context *discover_contextp;
+
+ discover_contextp = (struct discover_context *)
+ sm_contextp->state_handle;
+
+ memset(discover_contextp->SMPRequestFrame, 0,
+ sizeof(struct SMPRequest));
+
+ discover_contextp->SMPRequestFrame->SMPFrameType = SMP_REQUEST_FRAME;
+
+ discover_contextp->SMPRequestFrame->Function = PHY_CONTROL;
+
+ discover_contextp->SMPRequestFrame->Request.PhyControl.
+ PhyIdentifier = phyIndex;
+
+ discover_contextp->SMPRequestFrame->Request.PhyControl.
+ PhyOperation = operation;
+
+ /*
+ * get the discover information for each phy
+ */
+ results = asd_smp_request(sm_contextp, expander,
+ sizeof(struct SMPRequestPhyControl),
+ sizeof(struct SMPResponsePhyControl));
+
+ return results;
+}
next reply other threads:[~2005-02-17 17:35 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-02-17 17:35 Luben Tuikov [this message]
2005-02-17 19:03 ` [ANNOUNCE] Adaptec SAS/SATA device driver [03/27] Jeff Garzik
2005-02-17 19:09 ` Luben Tuikov
2005-02-17 19:17 ` Jeff Garzik
2005-02-17 19:22 ` Luben Tuikov
2005-02-17 21:39 ` Michael Tokarev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4214D5E8.9000907@adaptec.com \
--to=luben_tuikov@adaptec.com \
--cc=linux-scsi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.