public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
@ 2005-02-17 17:35 Luben Tuikov
  2005-02-17 19:03 ` Jeff Garzik
  0 siblings, 1 reply; 6+ messages in thread
From: Luben Tuikov @ 2005-02-17 17:35 UTC (permalink / raw)
  To: SCSI Mailing List

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;
+}





^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
  2005-02-17 17:35 [ANNOUNCE] Adaptec SAS/SATA device driver [03/27] Luben Tuikov
@ 2005-02-17 19:03 ` Jeff Garzik
  2005-02-17 19:09   ` Luben Tuikov
  2005-02-17 21:39   ` Michael Tokarev
  0 siblings, 2 replies; 6+ messages in thread
From: Jeff Garzik @ 2005-02-17 19:03 UTC (permalink / raw)
  To: Luben Tuikov; +Cc: SCSI Mailing List, James Bottomley

Luben,

Your emails do not comply with the Linux kernel patch submission format. 
   Please read
	http://linux.yyz.us/patch-format.html

Most critical is rule number #5 (signed-off-by line), but also important 
is rule number #1 (providing a useful subject line).

NOTE:  I am _not_ requesting that you resubmit your 27 patches; that's 
up to James.  Just making a note for the future.

Everyone who submits patches to Linux needs to include a signed-off-by 
line, and format their emails such that automated scripts will process 
the emails correctly.

	Jeff




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
  2005-02-17 19:03 ` Jeff Garzik
@ 2005-02-17 19:09   ` Luben Tuikov
  2005-02-17 19:17     ` Jeff Garzik
  2005-02-17 21:39   ` Michael Tokarev
  1 sibling, 1 reply; 6+ messages in thread
From: Luben Tuikov @ 2005-02-17 19:09 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: SCSI Mailing List, James Bottomley

On 02/17/05 14:03, Jeff Garzik wrote:
> Luben,
> 
> Your emails do not comply with the Linux kernel patch submission format. 
>   Please read
>     http://linux.yyz.us/patch-format.html
> 
> Most critical is rule number #5 (signed-off-by line), but also important 
> is rule number #1 (providing a useful subject line).

Hi Jeff,

Thanks for you reply.  I'm well aware of the patch format.
I wasn't sure about a different subject line since it it all part
of a driver posting, "announce" rathern than "patch".

Yes, I forgot to add a signed off line.  Sorry, but then again
I didn't write the code, just presenting it. (#5. Sign your work...)

Also this is more an announcement rather than a patch, but yes,
recent sd patch I submitted I think I followed the rules.

Thanks for reminding me of the rules,
	Luben


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
  2005-02-17 19:09   ` Luben Tuikov
@ 2005-02-17 19:17     ` Jeff Garzik
  2005-02-17 19:22       ` Luben Tuikov
  0 siblings, 1 reply; 6+ messages in thread
From: Jeff Garzik @ 2005-02-17 19:17 UTC (permalink / raw)
  To: Luben Tuikov; +Cc: SCSI Mailing List, James Bottomley

Luben Tuikov wrote:
> On 02/17/05 14:03, Jeff Garzik wrote:
> 
>> Luben,
>>
>> Your emails do not comply with the Linux kernel patch submission 
>> format.   Please read
>>     http://linux.yyz.us/patch-format.html
>>
>> Most critical is rule number #5 (signed-off-by line), but also 
>> important is rule number #1 (providing a useful subject line).
> 
> 
> Hi Jeff,
> 
> Thanks for you reply.  I'm well aware of the patch format.
> I wasn't sure about a different subject line since it it all part
> of a driver posting, "announce" rathern than "patch".

Yes, I understand that.

A key point is that it is impossible to differentiate your 27 patches, 
from looking at them in a email summary.  Putting a short summary of 
each patch in the email subject line greatly assists those reviewing 
your code.


> Yes, I forgot to add a signed off line.  Sorry, but then again
> I didn't write the code, just presenting it. (#5. Sign your work...)

"sign your work" is more of a legal sign-off than a notation of 
authorship.  In some cases, a company lawyer may be the person included 
in the signed-off-by line.

	Jeff




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
  2005-02-17 19:17     ` Jeff Garzik
@ 2005-02-17 19:22       ` Luben Tuikov
  0 siblings, 0 replies; 6+ messages in thread
From: Luben Tuikov @ 2005-02-17 19:22 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: SCSI Mailing List, James Bottomley

On 02/17/05 14:17, Jeff Garzik wrote:
> A key point is that it is impossible to differentiate your 27 patches, 
> from looking at them in a email summary.  Putting a short summary of 
> each patch in the email subject line greatly assists those reviewing 
> your code.

Ok, that makes sense.  I wish I had added that to the subject lines.
 
> "sign your work" is more of a legal sign-off than a notation of 
> authorship.  In some cases, a company lawyer may be the person included 
> in the signed-off-by line.

Ok, I see.  I guess I was a bit shy on that.
Feel free to add "Signed-off-by: Luben Tuikov <luben_tuikov@adaptec.com>",
to each patch.

Thanks Jeff!
	Luben

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [ANNOUNCE] Adaptec SAS/SATA device driver [03/27]
  2005-02-17 19:03 ` Jeff Garzik
  2005-02-17 19:09   ` Luben Tuikov
@ 2005-02-17 21:39   ` Michael Tokarev
  1 sibling, 0 replies; 6+ messages in thread
From: Michael Tokarev @ 2005-02-17 21:39 UTC (permalink / raw)
  To: SCSI Mailing List

Jeff Garzik wrote:
> Luben,
[]
> Everyone who submits patches to Linux needs to include a signed-off-by 
> line, and format their emails such that automated scripts will process 
> the emails correctly.

BTW, as this is a completely new driver (for the kernel source anyway),
and there's no "patch" per se here in usual sense (but alot of closely
related new files), I think it'll be better to submit it as a tarball,
not a series of "patches", most of which are with useless comments
(given after the filename the path introduces)... Tarball and a patch
for usual places like Kconfig and Makefiles in the upper-level in the
source tree...  Just IMHO ofcourse.

/mjt

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2005-02-17 21:39 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-17 17:35 [ANNOUNCE] Adaptec SAS/SATA device driver [03/27] Luben Tuikov
2005-02-17 19:03 ` 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox