linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [ANNOUNCE] Adaptec SAS/SATA device driver [05/27]
@ 2005-02-17 17:35 Luben Tuikov
  0 siblings, 0 replies; only message 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 3/3.

+
+void
+asd_ConfigureExpanderSM_Finish(struct state_machine_context *sm_contextp,
+			       DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(results);
+}
+
+void asd_ConfigureExpanderSM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_Context *ctx;
+	ASD_DISCOVERY_STATES current_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_CONFIG_EXPANDER_ROUTE:
+		break;
+
+	case ASD_STATE_CONFIG_EXPANDER_ROUTE_LOOP:
+		break;
+
+	default:
+		break;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_DiscoverConfigSetSM_Context {
+	struct discover_context *discover_contextp;
+	struct list_head *discover_listp;
+	struct asd_target *currentExpander;
+	struct asd_target *newExpander;
+	unsigned phyIndex;
+	struct list_head *found_listp;
+	struct list_head *old_discover_listp;
+};
+
+DISCOVER_RESULTS
+asd_state_config_set_issue_discover(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct Discover *currentDiscover;
+	unsigned conn_rate;
+	unsigned found_expander;
+	struct asd_DiscoverExpanderSM_Arguments args;
+	struct asd_target *target;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	currentDiscover = NULL;
+
+	while (1) {
+		for (; ctx->phyIndex !=
+		     ctx->currentExpander->num_phys; ctx->phyIndex++) {
+
+			/*
+			 * this is just a pointer helper
+			 */
+			currentDiscover = &(ctx->currentExpander->
+					    Phy[ctx->phyIndex].Result);
+
+			if (SAS_ISZERO(currentDiscover->AttachedSASAddress)) {
+				continue;
+			}
+
+			/*
+			 * Vitesse:
+			 */
+			if (((currentDiscover->TargetBits &
+			      SMP_TGT_PORT) != 0) &&
+			    (currentDiscover->AttachedDeviceType ==
+			     END_DEVICE)) {
+
+				printk("%s:%d: expander %llx reports %llx as "
+				       "an END_DEVICE, but SMP_TGT_PORT is "
+				       "set in TargetBits\n",
+				       __FUNCTION__, __LINE__,
+				       *((uint64_t *) ctx->currentExpander->
+					 ddb_profile.sas_addr),
+				       *((uint64_t *) currentDiscover->
+					 AttachedSASAddress));
+
+				printk("%s:%d: Assuming expander\n",
+				       __FUNCTION__, __LINE__);
+
+				currentDiscover->AttachedDeviceType =
+				    EDGE_EXPANDER_DEVICE;
+			}
+
+			/*
+			 * look for phys with edge or fanout devices attached...
+			 */
+			switch (currentDiscover->AttachedDeviceType) {
+			case EDGE_EXPANDER_DEVICE:
+				break;
+
+			case FANOUT_EXPANDER_DEVICE:
+				break;
+
+			case END_DEVICE:
+				conn_rate = ctx->currentExpander->
+				    ddb_profile.conn_rate;
+
+				if (conn_rate > currentDiscover->
+				    NegotiatedPhysicalLinkRate) {
+
+					conn_rate = currentDiscover->
+					    NegotiatedPhysicalLinkRate;
+				}
+
+				results = asd_configure_device(sm_contextp,
+							       ctx->
+							       currentExpander,
+							       currentDiscover,
+							       ctx->
+							       discover_listp,
+							       ctx->found_listp,
+							       ctx->
+							       old_discover_listp,
+							       conn_rate);
+
+				continue;
+
+			case NO_DEVICE:
+			default:
+				continue;
+			}
+
+			if (currentDiscover->RoutingAttribute != TABLE) {
+				continue;
+			}
+
+			/*
+			 * check to see if we already have the address
+			 * information in our expander list
+			 */
+			target = asd_find_target(ctx->discover_listp,
+						 currentDiscover->
+						 AttachedSASAddress);
+
+			if (target != NULL) {
+				continue;
+			}
+
+			args.sas_addr = currentDiscover->AttachedSASAddress;
+			args.upstreamExpander = ctx->currentExpander;
+			args.attachedDeviceType =
+			    currentDiscover->AttachedDeviceType;
+			args.old_discover_listp = ctx->old_discover_listp;
+			args.found_listp = ctx->found_listp;
+			args.conn_rate =
+			    currentDiscover->NegotiatedPhysicalLinkRate;
+
+			/*
+			 * if we did not have the expander in our list, then get
+			 * the information
+			 */
+			results = ASD_PUSH_STATE_MACHINE(sm_contextp,
+							 &asd_DiscoverExpanderSM,
+							 (void *)&args);
+
+			return results;
+		}
+
+		if (ctx->phyIndex == ctx->currentExpander->num_phys) {
+
+			found_expander = 0;
+
+			while (ctx->currentExpander->all_domain_targets.next
+			       != ctx->discover_listp) {
+
+				ctx->currentExpander =
+				    list_entry(ctx->currentExpander->
+					       all_domain_targets.next,
+					       struct asd_target,
+					       all_domain_targets);
+
+				if ((ctx->currentExpander->management_type ==
+				     ASD_DEVICE_EDGE_EXPANDER) ||
+				    (ctx->currentExpander->management_type ==
+				     ASD_DEVICE_FANOUT_EXPANDER)) {
+
+					found_expander = 1;
+
+					break;
+				}
+			}
+
+			if (found_expander == 0) {
+				return DISCOVER_FINISHED;
+			}
+
+			ctx->phyIndex = 0;
+		}
+	}
+}
+
+ASD_DISCOVERY_STATES
+asd_state_config_set_issue_discover_post(struct state_machine_context *
+					 sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+	struct asd_target *expander;
+	DISCOVER_RESULTS results;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	/*
+	 * The new expander is returned on the stack
+	 */
+	POP_STACK(results);
+	POP_STACK(expander);
+
+	ctx->phyIndex++;
+
+	if (results != DISCOVER_FINISHED) {
+		/*
+		 * For some reason we couldn't talk to this expander
+		 */
+		printk("Discover of expander 0x%0llx failed\n",
+		       *((uint64_t *) expander->ddb_profile.sas_addr));
+
+		return ASD_STATE_CONFIG_SET_ISSUE_DISCOVER;
+	}
+
+	ctx->newExpander = expander;
+
+	/*
+	 * Add the new expander to the tree.
+	 */
+	asd_add_child(discover_contextp->port, ctx->currentExpander,
+		      ctx->newExpander);
+
+	list_add_tail(&ctx->newExpander->all_domain_targets,
+		      ctx->discover_listp);
+
+	return ASD_STATE_CONFIG_SET_CONFIGURE_EXPANDER;
+}
+
+DISCOVER_RESULTS
+asd_state_config_set_configure_expander(struct state_machine_context *
+					sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_ConfigureExpanderSM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = ctx->discover_listp;
+	args.newExpander = ctx->newExpander;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp,
+					 &asd_ConfigureExpanderSM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_config_set_configure_expander_post(struct state_machine_context *
+					     sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_CONFIG_SET_FAILED;
+	}
+
+	return ASD_STATE_CONFIG_SET_ISSUE_DISCOVER;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverConfigSetSM_Initialize(struct state_machine_context * sm_contextp,
+				   void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+	struct asd_DiscoverConfigSetSM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_DiscoverConfigSetSM_Arguments *)init_args;
+
+	ctx->currentExpander = args->currentExpander;
+	ctx->discover_listp = args->discover_listp;
+	ctx->found_listp = args->found_listp;
+	ctx->old_discover_listp = args->old_discover_listp;
+	ctx->phyIndex = 0;
+	ctx->newExpander = NULL;
+
+	list_del_init(&ctx->currentExpander->all_domain_targets);
+
+	list_add_tail(&ctx->currentExpander->all_domain_targets,
+		      ctx->discover_listp);
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverConfigSetSM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	ASD_DISCOVERY_STATES current_state;
+	ASD_DISCOVERY_STATES new_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_CONFIG_SET_START:
+		new_state = ASD_STATE_CONFIG_SET_ISSUE_DISCOVER;
+		break;
+
+	case ASD_STATE_CONFIG_SET_ISSUE_DISCOVER:
+		new_state =
+		    asd_state_config_set_issue_discover_post(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_SET_CONFIGURE_EXPANDER:
+		new_state =
+		    asd_state_config_set_configure_expander_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_CONFIG_SET_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_CONFIG_SET_ISSUE_DISCOVER:
+		results = asd_state_config_set_issue_discover(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_SET_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	case ASD_STATE_CONFIG_SET_CONFIGURE_EXPANDER:
+		results = asd_state_config_set_configure_expander(sm_contextp);
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_DiscoverConfigSetSM_Finish(struct state_machine_context *sm_contextp,
+			       DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(results);
+
+}
+
+void asd_DiscoverConfigSetSM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverConfigSetSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_DiscoverFindBoundarySM_Context {
+	struct discover_context *discover_contextp;
+	struct asd_target *expander;
+	uint8_t upstreamSASAddress[SAS_ADDR_LEN];
+	uint8_t attachedPhyIdentifier;
+	uint8_t phyIdentifier;
+	struct asd_target *attachedRootExpander;
+	struct list_head *found_listp;
+	struct list_head *old_discover_listp;
+	unsigned conn_rate;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_find_boundary_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_find_subtractive_phy(sm_contextp, ctx->expander,
+					   ctx->upstreamSASAddress,
+					   &ctx->attachedPhyIdentifier,
+					   &ctx->conn_rate,
+					   &ctx->phyIdentifier);
+
+	if ((results == DISCOVER_FINISHED) ||
+	    SAS_ISZERO(ctx->upstreamSASAddress)) {
+
+		ctx->expander->management_flags |= DEVICE_SET_ROOT;
+
+		return ASD_STATE_FIND_BOUNDARY_FINISHED;
+	}
+
+	return ASD_STATE_FIND_BOUNDARY_LOOP;
+}
+
+DISCOVER_RESULTS
+asd_state_find_boundary_loop(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+	struct asd_DiscoverExpanderSM_Arguments args;
+	DISCOVER_RESULTS results;
+	struct Discover *discover;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	/*
+	 * if we have a subtractive address then go upstream
+	 * to see if it is part of the edge expander device set
+	 */
+
+	discover = &(ctx->expander->Phy[ctx->phyIdentifier].Result);
+
+	args.sas_addr = ctx->upstreamSASAddress;
+	args.upstreamExpander = ctx->expander;
+	args.attachedDeviceType = discover->AttachedDeviceType;
+	args.old_discover_listp = ctx->old_discover_listp;
+	args.found_listp = ctx->found_listp;
+	args.conn_rate = ctx->conn_rate;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_DiscoverExpanderSM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_find_boundary_loop_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *upstreamExpander;
+	struct Discover *discover;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	/*
+	 * The new expander is returned on the stack
+	 */
+	POP_STACK(results);
+	POP_STACK(upstreamExpander);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FIND_BOUNDARY_FAILED;
+	}
+
+	/*
+	 * Add the new expander to the tree.
+	 */
+	asd_add_child(discover_contextp->port, ctx->expander, upstreamExpander);
+
+	list_add_tail(&upstreamExpander->all_domain_targets, ctx->found_listp);
+
+	results = asd_find_subtractive_phy(sm_contextp, upstreamExpander,
+					   ctx->upstreamSASAddress,
+					   &ctx->attachedPhyIdentifier,
+					   &ctx->conn_rate,
+					   &ctx->phyIdentifier);
+
+	if (results == DISCOVER_FINISHED) {
+
+		ctx->expander = upstreamExpander;
+
+		ctx->expander->management_flags |= DEVICE_SET_ROOT;
+
+		return ASD_STATE_FIND_BOUNDARY_FINISHED;
+	}
+
+	/*
+	 * initialize the subtractive address, a zero value is not valid
+	 */
+	if (results == DISCOVER_FAILED) {
+		/*
+		 * to get here, we had to see more than one subtractive
+		 * phy that connect to different SAS addresses, this is
+		 * a topology error do cleanup on any memory allocated
+		 * if necessary
+		 */
+
+		// TODO: fix this
+		// TODO: what do we want to return here???
+
+		return ASD_STATE_FIND_BOUNDARY_FAILED;
+	}
+
+	/*
+	 * if no error, then decide if we need to go upstream or stop
+	 */
+	if (SAS_ISZERO(ctx->upstreamSASAddress)) {
+
+		ctx->expander = upstreamExpander;
+
+		return ASD_STATE_FIND_BOUNDARY_FINISHED;
+	}
+
+	/*
+	 * this is just a pointer helper
+	 */
+	discover = &(upstreamExpander->Phy[ctx->phyIdentifier].Result);
+
+	/*
+	 * check to see if the upstream expander is connected to the 
+	 * subtractive port of the previous expander, if we are then we 
+	 * have two expander device sets connected together, stop here 
+	 * and save the target pointer of next expander in device set.
+	 */
+	if (SAS_ISEQUAL(discover->AttachedSASAddress,
+			upstreamExpander->parent->ddb_profile.sas_addr)) {
+
+		ctx->attachedRootExpander = upstreamExpander;
+
+		ctx->expander->management_flags |= DEVICE_SET_ROOT;
+		ctx->attachedRootExpander->management_flags |= DEVICE_SET_ROOT;
+
+		return ASD_STATE_FIND_BOUNDARY_FINISHED;
+	}
+
+	/*
+	 * Movin' on up.
+	 */
+	ctx->expander = upstreamExpander;
+
+	return ASD_STATE_FIND_BOUNDARY_LOOP;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverFindBoundarySM_Initialize(struct state_machine_context *
+				      sm_contextp, void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+	struct asd_DiscoverFindBoundarySM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_DiscoverFindBoundarySM_Arguments *)init_args;
+
+	ctx->expander = args->expander;
+	ctx->found_listp = args->found_listp;
+	ctx->old_discover_listp = args->old_discover_listp;
+
+	SAS_ZERO(ctx->upstreamSASAddress);
+	ctx->attachedPhyIdentifier = 0;
+	ctx->attachedRootExpander = NULL;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverFindBoundarySM_StateMachine(struct state_machine_context *
+					sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	ASD_DISCOVERY_STATES current_state;
+	ASD_DISCOVERY_STATES new_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_FIND_BOUNDARY_START:
+		new_state = asd_state_find_boundary_start(sm_contextp);
+		break;
+
+	case ASD_STATE_FIND_BOUNDARY_LOOP:
+		new_state = asd_state_find_boundary_loop_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_FIND_BOUNDARY_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_FIND_BOUNDARY_LOOP:
+		results = asd_state_find_boundary_loop(sm_contextp);
+		break;
+
+	case ASD_STATE_FIND_BOUNDARY_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_DiscoverFindBoundarySM_Finish(struct state_machine_context *sm_contextp,
+				  DISCOVER_RESULTS results)
+{
+
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(ctx->attachedRootExpander);
+	RETURN_STACK(ctx->expander);
+	RETURN_STACK(results);
+}
+
+void asd_DiscoverFindBoundarySM_Abort(struct state_machine_context *sm_contextp)
+{
+
+	struct state_information *state_infop;
+	struct asd_DiscoverFindBoundarySM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_DiscoverExpanderSM_Context {
+	struct discover_context *discover_contextp;
+	struct asd_target *expander;
+	unsigned phyIndex;
+	uint8_t *sas_addr;
+	unsigned attachedDeviceType;
+	struct list_head *old_discover_listp;
+	struct list_head *found_listp;
+	struct asd_target *upstreamExpander;
+	unsigned conn_rate;
+};
+
+DISCOVER_RESULTS
+asd_state_issue_report_general(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	unsigned conn_rate;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	/*
+	 * Get the connection rate from the upstream expander.  The upstream
+	 * expander is different than the "parent" expander with is based on
+	 * the topology of the device set.
+	 */
+	if (ctx->upstreamExpander == NULL) {
+		conn_rate = discover_contextp->port->conn_rate;
+	} else {
+		conn_rate = ctx->upstreamExpander->ddb_profile.conn_rate;
+	}
+
+	if (conn_rate > ctx->conn_rate) {
+		conn_rate = ctx->conn_rate;
+	}
+
+	results = asd_issue_report_general(sm_contextp,
+					   ctx->sas_addr,
+					   conn_rate, ctx->attachedDeviceType,
+					   ctx->old_discover_listp,
+					   ctx->found_listp, &ctx->expander);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_issue_report_general_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	results = asd_issue_report_general_post(sm_contextp, ctx->expander);
+
+	if (results != DISCOVER_OK) {
+
+		printk("%s:%d: Report General Failed\n", __FUNCTION__,
+		       __LINE__);
+
+		return ASD_STATE_REPORT_AND_DISCOVER_FAILED;
+	}
+
+	return ASD_STATE_ISSUE_DISCOVER_LOOP;
+}
+
+DISCOVER_RESULTS
+asd_state_issue_discover_loop(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_discover_request(sm_contextp,
+					     ctx->expander, ctx->phyIndex);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_issue_discover_loop_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_discover_request_post(sm_contextp,
+						  ctx->expander, ctx->phyIndex);
+
+	if (results != DISCOVER_OK) {
+		printk("%s:%d: Discover Request Failed\n", __FUNCTION__,
+		       __LINE__);
+
+		return ASD_STATE_REPORT_AND_DISCOVER_FAILED;
+	}
+
+	ctx->phyIndex++;
+
+	if (ctx->phyIndex == ctx->expander->num_phys) {
+		return ASD_STATE_REPORT_AND_DISCOVER_FINISHED;
+	}
+
+	return ASD_STATE_ISSUE_DISCOVER_LOOP;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverExpanderSM_Initialize(struct state_machine_context * sm_contextp,
+				  void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+	struct asd_DiscoverExpanderSM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_DiscoverExpanderSM_Arguments *)init_args;
+
+	ctx->sas_addr = args->sas_addr;
+	ctx->upstreamExpander = args->upstreamExpander;
+	ctx->attachedDeviceType = args->attachedDeviceType;
+	ctx->old_discover_listp = args->old_discover_listp;
+	ctx->found_listp = args->found_listp;
+	ctx->conn_rate = args->conn_rate;
+	ctx->phyIndex = 0;
+	ctx->expander = NULL;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverExpanderSM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	ASD_DISCOVERY_STATES current_state;
+	ASD_DISCOVERY_STATES new_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_REPORT_AND_DISCOVER_START:
+		new_state = ASD_STATE_ISSUE_REPORT_GENERAL;
+		break;
+
+	case ASD_STATE_ISSUE_REPORT_GENERAL:
+		new_state = asd_state_issue_report_general_post(sm_contextp);
+		break;
+
+	case ASD_STATE_ISSUE_DISCOVER_LOOP:
+		new_state = asd_state_issue_discover_loop_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_REPORT_AND_DISCOVER_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_ISSUE_REPORT_GENERAL:
+		results = asd_state_issue_report_general(sm_contextp);
+		break;
+
+	case ASD_STATE_ISSUE_DISCOVER_LOOP:
+		results = asd_state_issue_discover_loop(sm_contextp);
+		break;
+
+	case ASD_STATE_REPORT_AND_DISCOVER_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_DiscoverExpanderSM_Finish(struct state_machine_context *sm_contextp,
+			      DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(ctx->expander);
+	RETURN_STACK(results);
+}
+
+void asd_DiscoverExpanderSM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverExpanderSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_DiscoverySM_Context {
+	struct discover_context *discover_contextp;
+
+	/*
+	 * The expander that is directly attached to this initiator/port
+	 */
+	struct asd_target *connectedExpander;
+
+	/*
+	 * The expander that is at the root of this device set
+	 */
+	struct asd_target *thisDeviceSet;
+
+	/*
+	 * The expander that is at the root of the attached device set (if any).
+	 * The attached device set is the the device set on the the other side
+	 * of a subtractive-subtractive boundary.
+	 */
+	struct asd_target *attachedDeviceSet;
+
+	/*
+	 * The list that we discovered from thisDeviceSet.
+	 */
+	struct list_head discover_list;
+
+	/*
+	 * The list that we discovered from attachedDeviceSet.
+	 */
+	struct list_head attached_device_list;
+
+	/*
+	 * During the process of discovering expanders, we may find a
+	 * device or expander that we don't know where to put at the moment.
+	 */
+	struct list_head found_list;
+
+	/*
+	 * The list from the previous discovery.  If there is anything left
+	 * on this list at the end of discovery, the device is no longer
+	 * present.
+	 */
+	struct list_head old_discover_list;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_discover_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	struct asd_target *target;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	/*
+	 * Invalidate all the targets, if any, found so far.  
+	 */
+	asd_invalidate_targets(discover_contextp->asd, discover_contextp->port);
+
+	/*
+	 * Move our list of formerly discovered targets to the "old discover
+	 * list".  We will use this list later to figure out which devices have
+	 * been removed.
+	 */
+	list_move_all(&ctx->old_discover_list,
+		      &discover_contextp->port->targets);
+
+	/*
+	 * MDT. Saving pointers for easy access.
+	 */
+	discover_contextp->asd->old_discover_listp = &ctx->old_discover_list;
+	discover_contextp->asd->discover_listp = &ctx->discover_list;
+
+	if (discover_contextp->port->tree_root == NULL) {
+		return ASD_STATE_FINISHED;
+	}
+
+	/*
+	 * check to see if an expander is attached
+	 */
+	if ((discover_contextp->port->management_type !=
+	     ASD_DEVICE_EDGE_EXPANDER) &&
+	    (discover_contextp->port->management_type !=
+	     ASD_DEVICE_FANOUT_EXPANDER)) {
+
+		target = asd_find_target(&ctx->old_discover_list,
+					 discover_contextp->port->tree_root->
+					 ddb_profile.sas_addr);
+
+		if (target != NULL) {
+			/*
+			 * This target was previously found.
+			 */
+			target->flags |= ASD_TARG_RESEEN;
+
+			/*
+			 * Take this target off of the old discover list.
+			 */
+			list_del_init(&target->all_domain_targets);
+		}
+
+		list_add_tail(&discover_contextp->port->tree_root->
+			      all_domain_targets, &ctx->discover_list);
+
+		/*
+		 * There isn't an expander to discover, so move on to the
+		 * initialization stage of the state machine.
+		 */
+		return ASD_STATE_SATA_SPINHOLD;
+	}
+
+	/*
+	 * walk the list of targets, and re-initialize the tree links.
+	 */
+	list_for_each_entry(target, &ctx->old_discover_list, all_domain_targets) {
+
+		target->parent = NULL;
+		INIT_LIST_HEAD(&target->children);
+		INIT_LIST_HEAD(&target->siblings);
+		INIT_LIST_HEAD(&target->multipath);
+
+		if ((target->management_type != ASD_DEVICE_EDGE_EXPANDER) &&
+		    (target->management_type != ASD_DEVICE_FANOUT_EXPANDER)) {
+
+			continue;
+		}
+
+		if (target->num_route_indexes == 0) {
+			continue;
+		}
+
+		if (target->RouteTable != NULL) {
+			memset(target->RouteTable, 0,
+			       SAS_ADDR_LEN * target->num_phys *
+			       target->num_route_indexes);
+		}
+
+		if (target->route_indexes != NULL) {
+			memset(target->route_indexes, 0,
+			       target->num_route_indexes * sizeof(uint16_t));
+		}
+	}
+
+	/*
+	 * Put our newly discovered, directly attached target in the found
+	 * list.
+	 */
+	list_del_init(&discover_contextp->port->tree_root->all_domain_targets);
+
+	list_add_tail(&discover_contextp->port->tree_root->all_domain_targets,
+		      &ctx->found_list);
+
+	return ASD_STATE_DISCOVER_ATTACHED;
+}
+
+DISCOVER_RESULTS
+asd_state_discover_attached(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_DiscoverExpanderSM_Arguments args;
+	uint8_t device_type;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	switch (discover_contextp->port->management_type) {
+	case ASD_DEVICE_EDGE_EXPANDER:
+		device_type = EDGE_EXPANDER_DEVICE;
+		break;
+
+	case ASD_DEVICE_FANOUT_EXPANDER:
+		device_type = FANOUT_EXPANDER_DEVICE;
+		break;
+
+	default:
+		/*
+		 * If this isn't some kind of expander, then we shouldn't
+		 * be here.
+		 */
+		results = DISCOVER_FAILED;
+		return results;
+	}
+
+	/*
+	 * There is no upstream expander from this expander, because it is
+	 * the closest device to the initiator.
+	 */
+	args.sas_addr = discover_contextp->port->attached_sas_addr;
+	args.upstreamExpander = NULL;
+	args.attachedDeviceType = device_type;
+	args.old_discover_listp = &ctx->old_discover_list;
+	args.found_listp = &ctx->found_list;
+	args.conn_rate = discover_contextp->port->conn_rate;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_DiscoverExpanderSM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_discover_attached_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *new_expander;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	/*
+	 * The new expander is returned on the stack
+	 */
+	POP_STACK(results);
+	POP_STACK(new_expander);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	ctx->connectedExpander = new_expander;
+
+	list_add_tail(&new_expander->all_domain_targets, &ctx->found_list);
+
+	return ASD_STATE_FIND_BOUNDARY;
+}
+
+DISCOVER_RESULTS
+asd_state_find_boundary(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_DiscoverFindBoundarySM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.expander = ctx->connectedExpander;
+	args.found_listp = &ctx->found_list;
+	args.old_discover_listp = &ctx->old_discover_list;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp,
+					 &asd_DiscoverFindBoundarySM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_find_boundary_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *expander;
+	struct asd_target *attachedRootExpander;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+	POP_STACK(expander);
+	POP_STACK(attachedRootExpander);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	ctx->thisDeviceSet = expander;
+	ctx->attachedDeviceSet = attachedRootExpander;
+
+	return ASD_STATE_CONFIG_BOUNDARY_SET;
+}
+
+DISCOVER_RESULTS
+asd_state_config_boundary_set(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_DiscoverConfigSetSM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = &ctx->discover_list;
+	args.found_listp = &ctx->found_list;
+	args.old_discover_listp = &ctx->old_discover_list;
+	args.currentExpander = ctx->thisDeviceSet;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_DiscoverConfigSetSM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_config_boundary_set_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	if (ctx->attachedDeviceSet == NULL) {
+		return ASD_STATE_SATA_SPINHOLD;
+	}
+
+	return ASD_STATE_CONFIG_ATTACHED_SET;
+}
+
+DISCOVER_RESULTS
+asd_state_config_attached_set(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_DiscoverConfigSetSM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = &ctx->attached_device_list;
+	args.found_listp = &ctx->found_list;
+	args.old_discover_listp = &ctx->old_discover_list;
+	args.currentExpander = ctx->attachedDeviceSet;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_DiscoverConfigSetSM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_config_attached_set_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	/*
+	 * put the domains together
+	 */
+	list_splice(&ctx->attached_device_list, ctx->discover_list.prev);
+
+	return ASD_STATE_SATA_SPINHOLD;
+}
+
+DISCOVER_RESULTS
+asd_state_sata_spinhold(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_SATA_SpinHoldSM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = &ctx->discover_list;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_SATA_SpinHoldSM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_sata_spinhold_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	return ASD_STATE_INIT_SATA;
+}
+
+DISCOVER_RESULTS asd_state_init_sata(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_InitSATA_SM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = &ctx->discover_list;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_InitSATA_SM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sata_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		//TODO: we can't give up, but we need to notify the user
+	}
+
+	return ASD_STATE_INIT_SAS;
+}
+
+DISCOVER_RESULTS asd_state_init_sas(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_InitSAS_SM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = &ctx->discover_list;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_InitSAS_SM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sas_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	return ASD_STATE_INIT_SMP;
+}
+
+DISCOVER_RESULTS asd_state_init_smp(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_InitSMP_SM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.discover_listp = &ctx->discover_list;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp, &asd_InitSMP_SM,
+					 (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_smp_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	POP_STACK(results);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_FAILED;
+	}
+
+	return ASD_STATE_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverySM_Initialize(struct state_machine_context * sm_contextp,
+			   void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	struct discover_context *discover_contextp;
+
+	NEW_CONTEXT(ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	ctx->connectedExpander = NULL;
+	ctx->thisDeviceSet = NULL;
+	ctx->attachedDeviceSet = NULL;
+
+	INIT_LIST_HEAD(&ctx->discover_list);
+	INIT_LIST_HEAD(&ctx->attached_device_list);
+	INIT_LIST_HEAD(&ctx->found_list);
+	INIT_LIST_HEAD(&ctx->old_discover_list);
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_DiscoverySM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	DISCOVER_RESULTS results;
+	ASD_DISCOVERY_STATES current_state;
+	ASD_DISCOVERY_STATES new_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_DISCOVER_START:
+		new_state = asd_state_discover_start(sm_contextp);
+		break;
+
+	case ASD_STATE_DISCOVER_ATTACHED:
+		new_state = asd_state_discover_attached_post(sm_contextp);
+		break;
+
+	case ASD_STATE_FIND_BOUNDARY:
+		new_state = asd_state_find_boundary_post(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_BOUNDARY_SET:
+		new_state = asd_state_config_boundary_set_post(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_ATTACHED_SET:
+		new_state = asd_state_config_attached_set_post(sm_contextp);
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD:
+		new_state = asd_state_sata_spinhold_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA:
+		new_state = asd_state_init_sata_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS:
+		new_state = asd_state_init_sas_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SMP:
+		new_state = asd_state_init_smp_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_DISCOVER_ATTACHED:
+		results = asd_state_discover_attached(sm_contextp);
+		break;
+
+	case ASD_STATE_FIND_BOUNDARY:
+		results = asd_state_find_boundary(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_BOUNDARY_SET:
+		results = asd_state_config_boundary_set(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_ATTACHED_SET:
+		results = asd_state_config_attached_set(sm_contextp);
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD:
+		results = asd_state_sata_spinhold(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA:
+		results = asd_state_init_sata(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS:
+		results = asd_state_init_sas(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SMP:
+		results = asd_state_init_smp(sm_contextp);
+		break;
+
+	case ASD_STATE_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_DiscoverySM_Finish(struct state_machine_context *sm_contextp,
+		       DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+	struct discover_context *discover_contextp;
+	unsigned long flags;
+	struct asd_phy *phy;
+	uint8_t conn_mask;
+	unsigned num_discovery;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	/*
+	 * We don't necessarily update the mask when we start discovery.  Set
+	 * it to the right value.
+	 */
+	conn_mask = 0;
+
+	list_for_each_entry(phy, &discover_contextp->port->phys_attached, links) {
+
+		conn_mask |= (1 << phy->id);
+	};
+
+	discover_contextp->port->conn_mask = conn_mask;
+
+	asd_apply_conn_mask(discover_contextp->asd, &ctx->discover_list);
+
+	if (discover_contextp->asd->platform_data->flags & ASD_DISCOVERY_INIT) {
+
+		// TODO: this is not MP safe, even with the lock.
+
+		asd_lock(discover_contextp->asd, &flags);
+
+		discover_contextp->asd->num_discovery--;
+
+		num_discovery = discover_contextp->asd->num_discovery;
+
+		asd_unlock(discover_contextp->asd, &flags);
+
+		INIT_LIST_HEAD(&discover_contextp->port->targets_to_validate);
+
+		list_move_all(&discover_contextp->port->targets_to_validate,
+			      &ctx->discover_list);
+
+		if (num_discovery == 0) {
+			asd_validate_targets_init(discover_contextp->asd);
+		}
+	} else {
+		/*
+		 * Hot Plug.
+		 */
+		/*
+		 * Validate any new targets that have been hot-plugged or 
+		 * hot-removed.
+		 */
+		asd_validate_targets_hotplug(discover_contextp->asd,
+					     discover_contextp->port, ctx);
+
+		list_move_all(&discover_contextp->port->targets,
+			      &ctx->discover_list);
+	}
+
+	discover_contextp->port->events &= ~ASD_DISCOVERY_PROCESS;
+
+	asd_wakeup_sem(&discover_contextp->asd->platform_data->discovery_sem);
+
+#if DISCOVER_DEBUG
+	//asd_dump_tree(asd, port);
+#endif
+}
+
+void asd_DiscoverySM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_DiscoverySM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+}
+
+/* -------------------------------------------------------------------------- */
+
+DISCOVER_RESULTS asd_do_discovery(struct asd_softc *asd, struct asd_port *port)
+{
+	DISCOVER_RESULTS results;
+
+	port->dc.sm_context.state_stack_top = -1;
+
+	port->dc.asd = asd;
+	port->dc.port = port;
+
+	port->dc.sm_context.state_handle = (void *)&port->dc;
+	port->dc.sm_context.wakeup_state_machine =
+	    asd_discover_wakeup_state_machine;
+
+	results =
+	    ASD_PUSH_STATE_MACHINE(&port->dc.sm_context, &asd_DiscoverySM,
+				   NULL);
+
+	if (results == DISCOVER_CONTINUE) {
+		results = asd_run_state_machine(&port->dc.sm_context);
+	}
+
+	return results;
+}
+
+void asd_abort_discovery(struct asd_softc *asd, struct asd_port *port)
+{
+	asd_abort_state_machine(&port->dc.sm_context);
+}
+
+static void asd_invalidate_targets(struct asd_softc *asd, struct asd_port *port)
+{
+	struct asd_target *target;
+
+	list_for_each_entry(target, &port->targets, all_domain_targets) {
+		target->flags &= ~ASD_TARG_ONLINE;
+	}
+}
+
+static void
+asd_apply_conn_mask(struct asd_softc *asd, struct list_head *discover_list)
+{
+	struct asd_target *target;
+
+	if (list_empty(discover_list)) {
+		return;
+	}
+
+	list_for_each_entry(target, discover_list, all_domain_targets) {
+		asd_hwi_build_ddb_site(asd, target);
+	}
+}
+
+int asd_map_multipath(struct asd_softc *asd, struct asd_target *target)
+{
+	int ret;
+	struct asd_target *multipath_target;
+	struct asd_device *dev;
+	unsigned i;
+
+	/*
+	 * Check to make sure that this same device hasn't been exposed to the
+	 * OS on a different port.
+	 */
+	multipath_target = asd_find_multipath(asd, target);
+
+	if (multipath_target != NULL) {
+		list_add_tail(&target->multipath, &multipath_target->multipath);
+
+		return 1;
+	}
+
+	ret = asd_map_target(asd, target);
+
+	if (ret != 0) {
+		return ret;
+	}
+
+	target->flags |= ASD_TARG_MAPPED;
+
+	/*
+	 * At this point in time, we know how many luns this device has,
+	 * create the device structures for each LUN.
+	 */
+	switch (target->command_set_type) {
+	case ASD_COMMAND_SET_SCSI:
+		for (i = 0; i < target->scsi_cmdset.num_luns; i++) {
+			dev = asd_alloc_device(asd, target,
+					       target->src_port->id,
+					       target->target, i);
+
+			memcpy(dev->saslun, &target->scsi_cmdset.luns[i],
+			       SAS_LUN_LEN);
+		}
+		break;
+
+	case ASD_COMMAND_SET_ATA:
+	case ASD_COMMAND_SET_ATAPI:
+		dev = asd_alloc_device(asd, target,
+				       target->src_port->id, target->target, 0);
+
+		memset(dev->saslun, 0, SAS_LUN_LEN);
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+
+}
+
+void asd_mark_duplicates(struct asd_softc *asd, struct list_head *discover_list)
+{
+	struct asd_target *target;
+	struct asd_target *tmp_target;
+	struct asd_target *check_target;
+	struct list_head map_list;
+	unsigned found_mapped;
+
+	INIT_LIST_HEAD(&map_list);
+
+	/*
+	 * Walk down the discovery list looking for duplicate devices.  If we
+	 * find a duplicate device, chain it to the mutipath list of the primary
+	 * target.  Put all of the unique devices in a list to mapped.
+	 */
+	while (!list_empty(discover_list)) {
+
+		target = list_entry(discover_list->next,
+				    struct asd_target, all_domain_targets);
+
+		list_del_init(&target->all_domain_targets);
+
+		/*
+		 * We only care about presenting end devices to the OS.
+		 */
+		if (target->management_type != ASD_DEVICE_END) {
+
+			list_add_tail(&target->all_domain_targets, &map_list);
+
+			continue;
+		}
+
+		switch (target->transport_type) {
+		case ASD_TRANSPORT_ATA:
+			list_add_tail(&target->all_domain_targets, &map_list);
+
+			continue;
+
+		case ASD_TRANSPORT_STP:
+		case ASD_TRANSPORT_SSP:
+			break;
+
+		default:
+			list_add_tail(&target->all_domain_targets, &map_list);
+			continue;
+		}
+
+		found_mapped = 0;
+
+		/*
+		 * Walk through the list marking each target that matches this
+		 * target as a duplicate.
+		 */
+		list_for_each_entry_safe(check_target, tmp_target,
+					 discover_list, all_domain_targets) {
+
+			/*
+			 * If they are not the same type of device, then
+			 * they can not be duplicates.
+			 */
+			if (target->transport_type !=
+			    check_target->transport_type) {
+
+				continue;
+			}
+
+			switch (target->transport_type) {
+			case ASD_TRANSPORT_SSP:
+				/*
+				 * If the lengths of the identification values
+				 * don't match, then the values themselves
+				 * can't match.
+				 */
+				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) {
+
+					continue;
+				}
+				break;
+
+			case ASD_TRANSPORT_STP:
+				if (!SAS_ISEQUAL(target->ddb_profile.sas_addr,
+						 check_target->ddb_profile.
+						 sas_addr)) {
+
+					continue;
+				}
+
+				break;
+
+			default:
+				continue;
+			}
+
+			/*
+			 * We have found a duplicate device.
+			 */
+			list_del_init(&check_target->all_domain_targets);
+
+			if (check_target->flags & ASD_TARG_MAPPED) {
+				/*
+				 * This is a device that has not been
+				 * discovered before that matches a device
+				 * that has already been discovered and is
+				 * exposed to the OS.
+				 */
+				list_add_tail(&target->multipath,
+					      &check_target->multipath);
+
+				list_add_tail(&check_target->all_domain_targets,
+					      &map_list);
+
+				found_mapped = 1;
+
+				continue;
+			}
+
+			/*
+			 * This device is a multipath, so we will not map it.
+			 */
+			list_add_tail(&check_target->multipath,
+				      &target->multipath);
+		}
+
+		if (found_mapped == 0) {
+			/*
+			 * We didn't find another matching target, so we
+			 * will add this target to our list of devices to be
+			 * mapped.
+			 */
+			list_add_tail(&target->all_domain_targets, &map_list);
+		}
+	}
+
+	/*
+	 * Walk down the list of targets to be mapped, and map them.
+	 */
+	list_for_each_entry_safe(target, tmp_target, &map_list,
+				 all_domain_targets) {
+
+		/*
+		 * We only care about not presenting end devices to the OS.
+		 */
+		if (target->management_type != ASD_DEVICE_END) {
+			continue;
+		}
+
+		if (target->flags & ASD_TARG_MAPPED) {
+			continue;
+		}
+
+		target->flags |= ASD_TARG_NEEDS_MAP;
+
+	}
+
+	list_move_all(discover_list, &map_list);
+}
+
+void
+asd_remap_device(struct asd_softc *asd,
+		 struct asd_target *target, struct asd_target *multipath_target)
+{
+	unsigned i;
+	struct asd_domain *dm;
+
+	if ((target->flags & ASD_TARG_MAPPED) == 0) {
+		return;
+	}
+
+	multipath_target->flags |= ASD_TARG_MAPPED;
+
+	target->flags &= ~ASD_TARG_MAPPED;
+
+	dm = target->domain;
+
+	for (i = 0; i < ASD_MAX_TARGETS; i++) {
+
+		if (dm->targets[i] != target) {
+			continue;
+		}
+
+		dm->targets[i] = multipath_target;
+
+		multipath_target->target = i;
+
+		break;
+	}
+
+	multipath_target->domain = dm;
+	multipath_target->refcount = target->refcount;
+
+	target->domain = NULL;
+
+	for (i = 0; i < ASD_MAX_LUNS; i++) {
+
+		if (target->devices[i] == NULL) {
+			continue;
+		}
+
+		multipath_target->devices[i] = target->devices[i];
+		multipath_target->devices[i]->target = multipath_target;
+#ifdef MULTIPATH_IO
+		multipath_target->devices[i]->current_target = multipath_target;
+#endif
+	}
+}
+
+/*
+ * At this point, all targets are either on their port->target list or on
+ * the context discover_list.  There can be more than one discovery going on
+ * simulataneously, but each one will call asd_validate_targets when it 
+ * completes.
+ */
+static void asd_validate_targets_init(struct asd_softc *asd)
+{
+	struct asd_target *target;
+	struct asd_target *tmp_target;
+	struct asd_port *port;
+	unsigned port_id;
+	struct list_head map_list;
+#ifdef OCM_REMAP
+	unsigned i;
+	struct asd_phy *phy;
+	struct asd_unit_element_format *punit_elm;
+	struct asd_uid_lu_naa_wwn *punit_id_lun;
+	struct asd_uid_lu_sata_model_num *punit_id_dir_sata;
+#endif
+
+	INIT_LIST_HEAD(&map_list);
+
+	/*
+	 * This code has been turned off in favor of using labels or UUIDS to
+	 * determine drive to filesystem mapping.
+	 */
+#ifdef OCM_REMAP
+	/*
+	 * Do one pass through the OCM structure that was passed by the BIOS.
+	 * The devices will go at the head of the "map_list".  Since these
+	 * devices are going on the list first, they will also be the exported
+	 * targets if we find duplicates later.
+	 */
+	for (i = 0; i < asd->num_unit_elements; i++) {
+
+		punit_elm = &asd->unit_elements[i];
+
+		switch (punit_elm->type) {
+		case UNELEM_LU_SATA_MOD_SERIAL_NUM:
+			punit_id_dir_sata = (struct asd_uid_lu_sata_model_num *)
+			    punit_elm->id;
+
+#if 0
+			printk("%s:%d: UNIT_ELEMENT_DIRECT_ATTACHED_SATA "
+			       "phy ID %d\n", __FUNCTION__, __LINE__,
+			       punit_id_dir_sata->phy_id);
+#endif
+
+			port = NULL;
+			phy = NULL;
+
+			for (port_id = 0; port_id < asd->hw_profile.max_ports;
+			     port_id++) {
+
+				port = asd->port_list[port_id];
+
+				list_for_each_entry(phy,
+						    &port->phys_attached,
+						    links) {
+
+					if (phy->id ==
+					    punit_id_dir_sata->phy_id) {
+						break;
+					}
+				}
+			}
+
+			if (port_id != asd->hw_profile.max_ports) {
+				if (list_empty(&port->targets_to_validate)) {
+					target =
+					    list_entry(port->
+						       targets_to_validate.next,
+						       struct asd_target,
+						       all_domain_targets);
+
+					target->flags |= ASD_TARG_ONLINE;
+
+					list_del_init(&target->
+						      all_domain_targets);
+
+					list_add_tail(&target->
+						      all_domain_targets,
+						      &map_list);
+
+					target->flags |= ASD_TARG_MAP_BOOT;
+				} else {
+					printk("Warning: no devices on target "
+					       "list for phy %d\n", phy->id);
+				}
+			} else {
+				printk("Warning: could not find direct phy ID "
+				       "matching %d\n",
+				       punit_id_dir_sata->phy_id);
+			}
+			break;
+		case UNELEM_LU_NAA_WWN:
+			punit_id_lun = (struct asd_uid_lu_naa_wwn *)
+			    punit_elm->id;
+
+#if 0
+			printk("%s:%d: UNIT_ELEMENT_LOGICAL_UNIT %llx\n",
+			       __FUNCTION__, __LINE__,
+			       asd_be64toh(*((uint64_t *) punit_id_lun->
+					     sas_address)));
+#endif
+
+			target = NULL;
+
+			for (port_id = 0; port_id < asd->hw_profile.max_ports;
+			     port_id++) {
+
+				port = asd->port_list[port_id];
+
+				target =
+				    asd_find_target(&port->targets_to_validate,
+						    punit_id_lun->sas_address);
+
+				if (target != NULL) {
+					break;
+				}
+			}
+
+			if (target != NULL) {
+				target->flags |= ASD_TARG_ONLINE;
+
+				list_del_init(&target->all_domain_targets);
+
+				list_add_tail(&target->all_domain_targets,
+					      &map_list);
+
+				target->flags |= ASD_TARG_MAP_BOOT;
+			} else {
+				printk("Warning: Couldn't find "
+				       "UNIT_ELEMENT_LOGICAL_UNIT %llx\n",
+				       asd_be64toh(*((uint64_t *) punit_id_lun->
+						     sas_address)));
+			}
+
+			break;
+		case UNELEM_VOLUME_SET_DDF_GUID:
+			printk("%s:%d: UNIT_ELEMENT_VOLUME_SET\n",
+			       __FUNCTION__, __LINE__);
+			break;
+
+		case UNELEM_LU_SGPIO:
+			printk("%s:%d: UNELEM_LU_SGPIO\n",
+			       __FUNCTION__, __LINE__);
+			break;
+
+		case UNELEM_LU_SAS_TOPOLOGY:
+			printk("%s:%d: UNELEM_LU_SAS_TOPOLOGY\n",
+			       __FUNCTION__, __LINE__);
+			break;
+		}
+
+	}
+#endif
+
+	/*
+	 * Now go through the devices that are left and put them on our list
+	 * of potential devices to be exported to the OS.
+	 */
+	for (port_id = 0; port_id < asd->hw_profile.max_ports; port_id++) {
+
+		port = asd->port_list[port_id];
+
+		list_for_each_entry_safe(target, tmp_target,
+					 &port->targets_to_validate,
+					 all_domain_targets) {
+
+			target->flags |= ASD_TARG_ONLINE;
+
+			if (target->management_type == ASD_DEVICE_END) {
+				target->flags |= ASD_TARG_NEEDS_MAP;
+			}
+
+			list_del_init(&target->all_domain_targets);
+
+			list_add_tail(&target->all_domain_targets, &map_list);
+		}
+	}
+
+	asd_mark_duplicates(asd, &map_list);
+
+	list_for_each_entry_safe(target, tmp_target,
+				 &map_list, all_domain_targets) {
+
+		list_del_init(&target->all_domain_targets);
+
+		if ((target->flags & ASD_TARG_NEEDS_MAP) != 0) {
+
+			target->flags &= ~ASD_TARG_NEEDS_MAP;
+
+			if (asd_map_multipath(asd, target) != 0) {
+				// TODO: fix potential leak
+
+				continue;
+			}
+		}
+
+		list_add_tail(&target->all_domain_targets,
+			      &target->src_port->targets);
+	}
+
+	return;
+}
+
+static void
+asd_validate_targets_hotplug(struct asd_softc *asd,
+			     struct asd_port *port,
+			     struct asd_DiscoverySM_Context *ctx)
+{
+	struct asd_target *target;
+	struct asd_target *tmp_target;
+	struct asd_target *multipath_target;
+
+	INIT_LIST_HEAD(&port->targets_to_validate);
+
+	/*
+	 * Go through the old discover list and see if any devices that 
+	 * have been removed.  If this is our first time through the discovery
+	 * process, then there will be nothing on the old_discover_list.  The
+	 * old_discover_list has all of the devices that were seen in a
+	 * previous discover, but are no longer present.
+	 */
+	list_for_each_entry_safe(target, tmp_target,
+				 &ctx->old_discover_list, all_domain_targets) {
+
+		list_del_init(&target->all_domain_targets);
+
+		list_add_tail(&target->validate_links,
+			      &port->targets_to_validate);
+
+		/* 
+		 * The target was previously seen and now has gone missing.
+		 */
+		target->flags |= ASD_TARG_HOT_REMOVED;
+
+		multipath_target = asd_find_target_ident(&ctx->discover_list,
+							 target);
+
+		if (multipath_target != NULL) {
+
+			/*
+			 * If there were multiple paths to the same device,
+			 * then don't report this device as removed;
+			 */
+			asd_remap_device(asd, target, multipath_target);
+
+			continue;
+		}
+
+		multipath_target = asd_find_multipath(asd, target);
+
+		if (multipath_target != NULL) {
+
+			/*
+			 * If there were multiple paths to the same device,
+			 * then don't report this device as removed;
+			 */
+			asd_remap_device(asd, target, multipath_target);
+		}
+
+		if (!list_empty(&target->multipath)) {
+			multipath_target = list_entry(target->multipath.next,
+						      struct asd_target,
+						      multipath);
+
+			asd_remap_device(asd, target, multipath_target);
+
+			/*
+			 * This device wasn't in the target list because it
+			 * was a multipath.  Add it to the list.
+			 */
+			list_add_tail(&multipath_target->all_domain_targets,
+				      &multipath_target->src_port->targets);
+
+			target->flags &= ~ASD_TARG_MAPPED;
+		}
+	}
+
+	asd_mark_duplicates(asd, &ctx->discover_list);
+
+	list_for_each_entry_safe(target, tmp_target, &ctx->discover_list,
+				 all_domain_targets) {
+
+		if (((target->flags & ASD_TARG_ONLINE) == 0) &&
+		    (target->flags & ASD_TARG_RESEEN)) {
+
+			/*
+			 * Previously seen target.
+			 * Set target state to ONLINE.
+			 */
+			target->flags &= ~ASD_TARG_RESEEN;
+			target->flags |= ASD_TARG_ONLINE;
+
+			continue;
+		}
+
+		if ((target->flags & ASD_TARG_NEEDS_MAP) == 0) {
+			/*
+			 * If the device is not an end device, then
+			 * ASD_TARG_NEEDS_MAP will never be set.
+			 */
+			continue;
+		}
+
+		target->flags &= ~ASD_TARG_NEEDS_MAP;
+
+		if (asd_map_multipath(asd, target) == 0) {
+			/*
+			 * New target found.
+			 */
+			target->flags |= ASD_TARG_HOT_ADDED;
+
+			list_add_tail(&target->validate_links,
+				      &port->targets_to_validate);
+		} else {
+			/*
+			 * The target was not mapped.  It might have been
+			 * already mapped, so don't include it in our list
+			 * of new targets.
+			 */
+			list_del_init(&target->all_domain_targets);
+		}
+	}
+
+	INIT_LIST_HEAD(&ctx->old_discover_list);
+
+	/*
+	 * Set the flag that targets validation is required.
+	 */
+	port->events |= ASD_VALIDATION_REQ;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#if DISCOVER_DEBUG
+void asd_debug_indent(unsigned indent)
+{
+	unsigned i;
+
+	for (i = 0; i < indent; i++) {
+		kdb_printf("|  ");
+	}
+}
+
+static void
+asd_dump_tree_internal(struct asd_softc *asd,
+		       struct asd_port *port,
+		       struct asd_target *target,
+		       unsigned indent, unsigned phy_num)
+{
+	struct Discover *discover;
+	struct asd_target *child_target;
+	unsigned i;
+
+	asd_debug_indent(indent);
+
+	kdb_printf("+ Phy %d:", phy_num);
+
+	kdb_printf(" Addr: %0llx - ",
+		   asd_be64toh(*((uint64_t *) target->ddb_profile.sas_addr)));
+
+	asd_print_conn_rate(target->ddb_profile.conn_rate, "|");
+
+	switch (target->command_set_type) {
+	case ASD_COMMAND_SET_SCSI:
+		kdb_printf("SCSI|");
+		break;
+	case ASD_COMMAND_SET_ATA:
+		kdb_printf("ATA|");
+		break;
+	case ASD_COMMAND_SET_ATAPI:
+		kdb_printf("ATAPI|");
+		break;
+	case ASD_COMMAND_SET_SMP:
+		kdb_printf("SMP|");
+		break;
+	default:
+		kdb_printf("Unknown (%u)|", (unsigned)target->command_set_type);
+		break;
+	}
+
+	switch (target->device_protocol_type) {
+	case ASD_DEVICE_PROTOCOL_SCSI:
+		kdb_printf("SCSI|");
+		break;
+	case ASD_DEVICE_PROTOCOL_ATA:
+		kdb_printf("ATA|");
+		break;
+	case ASD_DEVICE_PROTOCOL_SMP:
+		kdb_printf("SMP|");
+		break;
+	default:
+		kdb_printf("Unknown (%u)|",
+			   (unsigned)target->device_protocol_type);
+		break;
+	}
+
+	switch (target->transport_type) {
+	case ASD_TRANSPORT_SSP:
+		kdb_printf("SSP|");
+		break;
+	case ASD_TRANSPORT_SMP:
+		kdb_printf("SMP|");
+		break;
+	case ASD_TRANSPORT_STP:
+		kdb_printf("STP|");
+		break;
+	case ASD_TRANSPORT_ATA:
+		kdb_printf("ATA|");
+		break;
+	default:
+		kdb_printf("Unknown (%u)|", (unsigned)target->transport_type);
+		break;
+	}
+
+	switch (target->management_type) {
+	case ASD_DEVICE_END:
+		kdb_printf("END_DEVICE");
+		break;
+	case ASD_DEVICE_FANOUT_EXPANDER:
+		kdb_printf("FANOUT_DEVICE");
+		break;
+	case ASD_DEVICE_EDGE_EXPANDER:
+		kdb_printf("EXPANDER_DEVICE");
+		break;
+	default:
+		kdb_printf("Unknown (%u)", (unsigned)target->management_type);
+		break;
+	}
+
+	kdb_printf("\n");
+
+	if (target->transport_type != ASD_TRANSPORT_SMP) {
+		return;
+	}
+
+	asd_debug_indent(indent);
+
+	kdb_printf("| Number of Phys: %d\n", target->num_phys);
+
+	discover = NULL;
+
+	indent++;
+
+	list_for_each_entry(child_target, &target->children, siblings) {
+
+		for (i = 0; i < target->num_phys; i++) {
+
+			discover = &(target->Phy[i].Result);
+
+			if (SAS_ISEQUAL(child_target->ddb_profile.sas_addr,
+					discover->AttachedSASAddress)) {
+				break;
+			}
+		}
+
+		if (i == target->num_phys) {
+			continue;
+		}
+
+		asd_dump_tree_internal(asd, port, child_target, indent, i);
+	}
+}
+
+static void asd_dump_tree(struct asd_softc *asd, struct asd_port *port)
+{
+	struct asd_target *target;
+	struct state_information *state_infop;
+
+	SETUP_STATE(&port->dc.sm_context);
+
+	kdb_printf("port %p: current_state 0x%x\n", port,
+		   state_infop->current_state);
+
+	if (!list_empty(&port->targets)) {
+
+		target = port->tree_root;
+
+		asd_dump_tree_internal(asd, port, target, 1, 0);
+	}
+}
+#endif
+
+#if KDB_ENABLE
+#if DISCOVER_DEBUG
+extern struct list_head asd_hbas;
+
+void asd_dump_sas_state(void
+    )
+{
+	unsigned i;
+	struct asd_softc *asd;
+
+	printk("%s:%d --\n", __FUNCTION__, __LINE__);
+
+	list_for_each_entry(asd, &asd_hbas, link) {
+		for (i = 0; i < asd->hw_profile.max_ports; i++) {
+			if (asd->port_list[i] != NULL) {
+				asd_dump_tree(asd, asd->port_list[i]);
+			}
+		}
+	}
+}
+
+void asd_kdb_init(void
+    )
+{
+	kdb_register("sas", (void *)asd_dump_sas_state, "",
+		     "Dumps the SAS state", 0);
+}
+
+void asd_kdb_exit(void
+    )
+{
+	kdb_unregister("sas");
+}
+#endif
+#endif




^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2005-02-17 17:35 UTC | newest]

Thread overview: (only message) (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 [05/27] Luben Tuikov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).