All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luben Tuikov <luben_tuikov@adaptec.com>
To: SCSI Mailing List <linux-scsi@vger.kernel.org>
Subject: [ANNOUNCE] Adaptec SAS/SATA device driver [04/27]
Date: Thu, 17 Feb 2005 12:35:42 -0500	[thread overview]
Message-ID: <4214D5EE.9050801@adaptec.com> (raw)

This is the SAS domain discovery code. Part 2/3.

+
+DISCOVER_RESULTS
+asd_issue_phy_control_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;
+
+	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"
+			"phy control error, %02Xh at %0llx\n",
+			discover_contextp->SMPResponseFrame->FunctionResult,
+			*((uint64_t *) expander->ddb_profile.sas_addr));
+
+		return DISCOVER_FAILED;
+	}
+
+	return DISCOVER_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Standard inquiry command
+ */
+static uint8_t inquiry_cmd[] = {
+	INQUIRY, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * Device ID
+ */
+static uint8_t inquiry_dev_id_cmd[] = {
+	INQUIRY, 0x01, 0x83, 0x00, 0x00, 0x00
+};
+
+/*
+ * Unit Serial Number
+ */
+static uint8_t inquiry_USN_cmd[] = {
+	INQUIRY, 0x01, 0x80, 0x00, 0x00, 0x00
+};
+
+#define MAX_INQUIRY_LEN(a)	(((a) > 255) ? 255 : (a))
+
+DISCOVER_RESULTS
+asd_issue_inquiry(struct state_machine_context *sm_contextp,
+		  struct asd_target *target,
+		  uint8_t * command, unsigned command_len)
+{
+	struct discover_context *discover_contextp;
+	unsigned xfer_len;
+	DISCOVER_RESULTS results;
+	uint8_t icmd[SCB_EMBEDDED_CDB_SIZE];
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	xfer_len = MAX_INQUIRY_LEN(discover_contextp->sas_info_len);
+
+	memcpy(icmd, command, command_len);
+
+	*((uint16_t *) & icmd[3]) = asd_htobe16(xfer_len);
+
+	results = asd_ssp_request(sm_contextp, target, icmd, command_len,
+				  discover_contextp->SASInfoBusAddr, xfer_len,
+				  DATA_DIR_INBOUND);
+
+	return results;
+}
+
+/* -------------------------------------------------------------------------- */
+
+uint8_t report_luns_cmd[] = {
+	REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+DISCOVER_RESULTS
+asd_issue_report_luns(struct state_machine_context * sm_contextp,
+		      struct asd_target * target)
+{
+	struct discover_context *discover_contextp;
+	DISCOVER_RESULTS results;
+	uint8_t rl_cmd[SCB_EMBEDDED_CDB_SIZE];
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	memcpy(rl_cmd, report_luns_cmd, sizeof(report_luns_cmd));
+
+	*((uint32_t *) & rl_cmd[6]) =
+	    asd_htobe32(discover_contextp->sas_info_len);
+
+	results = asd_ssp_request(sm_contextp, target,
+				  rl_cmd, sizeof(report_luns_cmd),
+				  discover_contextp->SASInfoBusAddr,
+				  discover_contextp->sas_info_len,
+				  DATA_DIR_INBOUND);
+
+	return results;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define CONTROL_MODE_PAGE_SIZE		0x08
+
+uint8_t mode_sense_port_control_cmd[] = {
+	MODE_SENSE, 0x00, 0x19, 0x00, 0x00, 0x00
+};
+
+DISCOVER_RESULTS
+asd_issue_get_port_control(struct state_machine_context * sm_contextp,
+			   struct asd_target * target)
+{
+	struct discover_context *discover_contextp;
+	DISCOVER_RESULTS results;
+	uint8_t pc_cmd[SCB_EMBEDDED_CDB_SIZE];
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	memcpy(pc_cmd, mode_sense_port_control_cmd,
+	       sizeof(mode_sense_port_control_cmd));
+
+	pc_cmd[4] = CONTROL_MODE_PAGE_SIZE;
+
+	results = asd_ssp_request(sm_contextp, target,
+				  pc_cmd, sizeof(mode_sense_port_control_cmd),
+				  discover_contextp->SASInfoBusAddr,
+				  CONTROL_MODE_PAGE_SIZE, DATA_DIR_INBOUND);
+
+	return results;
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_InitSMP_SM_Context {
+	struct list_head *discover_listp;
+	struct asd_target *currentTarget;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_init_smp_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		asd_log(ASD_DBG_INFO, "%s:%d - %llx %d\n",
+			__FUNCTION__, __LINE__,
+			*((uint64_t *) ctx->currentTarget->
+			  ddb_profile.sas_addr),
+			ctx->currentTarget->ddb_profile.conn_rate);
+
+		if (ctx->currentTarget->command_set_type == ASD_COMMAND_SET_SMP) {
+
+			return ASD_STATE_INIT_SMP_REPORT_MANUFACTURER_INFO;
+		}
+	}
+
+	return ASD_STATE_INIT_SMP_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_init_smp_report_manufacturer_info(struct state_machine_context *
+					    sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_report_manufacturer_info(sm_contextp,
+						     ctx->currentTarget);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_smp_report_manufacturer_info_post(struct state_machine_context *
+						 sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_Context *ctx;
+	struct discover_context *discover_contextp;
+	DISCOVER_RESULTS results;
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_report_manufacturer_info_post(sm_contextp,
+							  ctx->currentTarget);
+
+	if (results != DISCOVER_OK) {
+		printk("could not get manufactur_info from device\n");
+	}
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->command_set_type == ASD_COMMAND_SET_SMP) {
+
+			return ASD_STATE_INIT_SMP_REPORT_MANUFACTURER_INFO;
+		}
+	}
+
+	return ASD_STATE_INIT_SMP_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_InitSMP_SM_Initialize(struct state_machine_context * sm_contextp,
+			  void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_Context *ctx;
+	struct asd_InitSMP_SM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_InitSMP_SM_Arguments *)init_args;
+
+	ctx->discover_listp = args->discover_listp;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_InitSMP_SM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_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_INIT_SMP_START:
+		new_state = asd_state_init_smp_start(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SMP_REPORT_MANUFACTURER_INFO:
+		new_state =
+		    asd_state_init_smp_report_manufacturer_info_post
+		    (sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_INIT_SMP_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+
+	case ASD_STATE_INIT_SMP_REPORT_MANUFACTURER_INFO:
+		results =
+		    asd_state_init_smp_report_manufacturer_info(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SMP_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_InitSMP_SM_Finish(struct state_machine_context *sm_contextp,
+		      DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_Arguments *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(results);
+}
+
+void asd_InitSMP_SM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSMP_SM_Arguments *ctx;
+	ASD_DISCOVERY_STATES current_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+#if 0
+	switch (current_state) {
+	}
+#endif
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_InitSAS_SM_Context {
+	struct list_head *discover_listp;
+	struct asd_target *currentTarget;
+	unsigned phyIndex;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_init_sas_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		asd_log(ASD_DBG_INFO, "%s:%d - %llx %d\n",
+			__FUNCTION__, __LINE__,
+			*((uint64_t *) ctx->currentTarget->
+			  ddb_profile.sas_addr),
+			ctx->currentTarget->ddb_profile.conn_rate);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_INQUIRY;
+		}
+	}
+
+	return ASD_STATE_INIT_SAS_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_init_sas_inquiry(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_inquiry(sm_contextp, ctx->currentTarget,
+				    inquiry_cmd, sizeof(inquiry_cmd));
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sas_inquiry_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	unsigned inquiry_response_len;
+	struct discover_context *discover_contextp;
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		printk("Inquiry failed\n");
+
+	} else {
+
+		inquiry_response_len =
+		    MAX_INQUIRY_LEN(discover_contextp->sas_info_len) -
+		    discover_contextp->resid_len;
+
+		ctx->currentTarget->scsi_cmdset.inquiry = (uint8_t *)
+		    asd_alloc_mem(inquiry_response_len, GFP_KERNEL);
+
+		if (ctx->currentTarget->scsi_cmdset.inquiry != NULL) {
+
+			memcpy(ctx->currentTarget->scsi_cmdset.inquiry,
+			       &discover_contextp->SASInfoFrame[0],
+			       inquiry_response_len);
+
+#if 0
+			printk("%8.8s | ",
+			       &ctx->currentTarget->scsi_cmdset.inquiry[8]);
+			printk("%16.16s | ",
+			       &ctx->currentTarget->scsi_cmdset.inquiry[16]);
+			printk("%4.4s\n",
+			       &ctx->currentTarget->scsi_cmdset.inquiry[32]);
+#endif
+		}
+	}
+
+	return ASD_STATE_INIT_SAS_GET_DEVICE_ID;
+}
+
+DISCOVER_RESULTS
+asd_state_init_get_device_id(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_inquiry(sm_contextp, ctx->currentTarget,
+				    inquiry_dev_id_cmd,
+				    sizeof(inquiry_dev_id_cmd));
+
+	return results;
+}
+
+#define ASSOCIATION_SCSI_LOGICAL_UNIT		0
+#define ASSOCIATION_SCSI_TARGET_PORT		1
+#define ASSOCIATION_SCSI_TARGET_DEVICE		2
+
+#define PIV_VALID				0x80
+#define VPD_SAS_PROTOCOL			6
+
+ASD_DISCOVERY_STATES
+asd_state_init_get_device_id_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	uint8_t *vpd_page_header;
+	uint8_t *vpd_pagep;
+	unsigned page_length;
+	unsigned identifier_type;
+	unsigned offset;
+	unsigned association;
+	unsigned code_set;
+	unsigned protocol;
+	unsigned length;
+	struct discover_context *discover_contextp;
+	unsigned inquiry_response_len;
+	unsigned piv_valid;
+#if 0
+	unsigned i;
+#endif
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		return ASD_STATE_INIT_SAS_GET_SERIAL_NUMBER;
+	}
+
+	vpd_page_header = &discover_contextp->SASInfoFrame[0];
+
+	inquiry_response_len =
+	    MAX_INQUIRY_LEN(discover_contextp->sas_info_len) -
+	    discover_contextp->resid_len;
+
+	page_length = MIN(asd_be16toh(*((uint16_t *) & vpd_page_header[2])) + 3,
+			  inquiry_response_len);
+
+#if 0
+	for (i = 0; i < page_length; i++) {
+		printk("%02x ", vpd_page_header[i]);
+
+		if (((i + 1) % 16) == 0) {
+			printk("\n");
+		}
+	}
+#endif
+
+	vpd_pagep = &vpd_page_header[4];
+
+	for (offset = vpd_pagep - vpd_page_header; offset < page_length;
+	     vpd_pagep = vpd_pagep + length,
+	     offset = vpd_pagep - vpd_page_header) {
+
+		length = vpd_pagep[3] + 3;
+
+		if ((offset + length) > page_length) {
+			break;
+		}
+
+		identifier_type = vpd_pagep[1] & 0xf;
+
+#if 0
+		switch (identifier_type & 0xf) {
+		case IDENTIFIER_TYPE_VENDOR_SPECIFIC:
+			printk("IDENTIFIER_TYPE_VENDOR_SPECIFIC\n");
+			break;
+		case IDENTIFIER_TYPE_T10:
+			printk("IDENTIFIER_TYPE_T10\n");
+			break;
+		case IDENTIFIER_TYPE_EIU_64:
+			printk("IDENTIFIER_TYPE_EIU_64\n");
+			break;
+		case IDENTIFIER_TYPE_NAA:
+			printk("IDENTIFIER_TYPE_NAA\n");
+			break;
+		case IDENTIFIER_TYPE_RELATIVE_TARGET_PORT:
+			printk("IDENTIFIER_TYPE_RELATIVE_TARGET_PORT\n");
+			break;
+		case IDENTIFIER_TYPE_TARGET_PORT_GROUP:
+			printk("IDENTIFIER_TYPE_TARGET_PORT_GROUP\n");
+			break;
+		case IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP:
+			printk("IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP\n");
+			break;
+		case IDENTIFIER_TYPE_MD5_LOGICAL_UNIT:
+			printk("IDENTIFIER_TYPE_MD5_LOGICAL_UNIT\n");
+			break;
+		case IDENTIFIER_TYPE_SCSI_NAME_STRING:
+			printk("IDENTIFIER_TYPE_SCSI_NAME_STRING\n");
+			break;
+		default:
+			printk("UNKNOWN\n");
+			break;
+		}
+#endif
+		if ((identifier_type & 0xf) != IDENTIFIER_TYPE_NAA) {
+
+			vpd_pagep = vpd_pagep + length;
+
+			continue;
+		}
+
+		protocol = (vpd_pagep[0] & 0xf0) >> 4;
+		piv_valid = vpd_pagep[1] & PIV_VALID;
+		code_set = vpd_pagep[0] & 0xf;
+		association = (vpd_pagep[1] & 0x30) >> 4;
+
+#if 0
+		printk("Association %d | ", association);
+		printk("Code Set %d | ", code_set);
+		printk("PIV Valid %d | ", piv_valid);
+		printk("Protocol %d\n", protocol);
+#endif
+
+		ctx->currentTarget->scsi_cmdset.ident_len = length - 3;
+
+		switch (association) {
+		case ASSOCIATION_SCSI_LOGICAL_UNIT:
+			// seagate
+			if (vpd_pagep[1] & PIV_VALID) {
+				continue;
+			}
+			break;
+
+		case ASSOCIATION_SCSI_TARGET_PORT:
+			// fujitsu
+			if (((piv_valid & PIV_VALID) != PIV_VALID) ||
+			    (protocol != VPD_SAS_PROTOCOL)) {
+				continue;
+			}
+			break;
+
+		case ASSOCIATION_SCSI_TARGET_DEVICE:
+			if (piv_valid & PIV_VALID) {
+				continue;
+			}
+		}
+
+		ctx->currentTarget->scsi_cmdset.ident = (uint8_t *)
+		    asd_alloc_mem(ctx->currentTarget->scsi_cmdset.ident_len,
+				  GFP_KERNEL);
+
+		if (ctx->currentTarget->scsi_cmdset.ident != NULL) {
+
+			memcpy(ctx->currentTarget->scsi_cmdset.ident,
+			       &vpd_pagep[4],
+			       ctx->currentTarget->scsi_cmdset.ident_len);
+		}
+	}
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_INQUIRY;
+		}
+	}
+
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		asd_log(ASD_DBG_INFO, "%s:%d - %llx %d\n",
+			__FUNCTION__, __LINE__,
+			*((uint64_t *) ctx->currentTarget->
+			  ddb_profile.sas_addr),
+			ctx->currentTarget->ddb_profile.conn_rate);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS;
+		}
+	}
+
+	return ASD_STATE_INIT_SAS_FINISHED;
+}
+
+static const char *asd_vpd_warning = "Warning: This device does not support "
+"INQUIRY VPD Page 83h commmand which is required by the SAS spec. In addition, "
+"this device does not support the optional INQUIRY VPD Page 80h. Devices that "
+"do not support either of these commands may experience file corruption and "
+"data loss. Please upgrade your firmware.";
+
+DISCOVER_RESULTS
+asd_state_init_get_serial_number(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_inquiry(sm_contextp, ctx->currentTarget,
+				    inquiry_USN_cmd, sizeof(inquiry_USN_cmd));
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_get_serial_number_post(struct state_machine_context *
+				      sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	uint8_t *vpd_pagep;
+	unsigned page_length;
+	unsigned i;
+	unsigned serial_number_len;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		printk("%s\n", asd_vpd_warning);
+
+#ifdef NO_VPD_WORKAROUND
+		//TODO: free the ident field when the target is destroyed
+
+		ctx->currentTarget->scsi_cmdset.ident_len = sizeof(uint64_t);
+
+		ctx->currentTarget->scsi_cmdset.ident = (uint8_t *)
+		    asd_alloc_mem(sizeof(uint64_t), GFP_KERNEL);
+
+		if (ctx->currentTarget->scsi_cmdset.ident != NULL) {
+
+			*(uint64_t *) ctx->currentTarget->scsi_cmdset.ident =
+			    asd_be64toh(*((uint64_t *) ctx->currentTarget->
+					  ddb_profile.sas_addr)) & ~0xfL;
+
+			printk("Setting ident to %llx\n",
+			       *((uint64_t *) ctx->currentTarget->
+				 scsi_cmdset.ident));
+		}
+#endif
+	} else {
+		vpd_pagep = &discover_contextp->SASInfoFrame[0];
+
+		page_length = vpd_pagep[3];
+
+		for (i = 0; i < (page_length + 3); i++) {
+			printk("%02x ", vpd_pagep[i]);
+
+			if (((i + 1) % 16) == 0) {
+				printk("\n");
+			}
+		}
+
+		serial_number_len = discover_contextp->SASInfoFrame[3];
+
+		if (serial_number_len > discover_contextp->sas_info_len) {
+			serial_number_len = discover_contextp->sas_info_len;
+		}
+
+		ctx->currentTarget->scsi_cmdset.ident_len = serial_number_len;
+
+		ctx->currentTarget->scsi_cmdset.ident = (uint8_t *)
+		    asd_alloc_mem(ctx->currentTarget->scsi_cmdset.ident_len,
+				  GFP_KERNEL);
+
+		if (ctx->currentTarget->scsi_cmdset.ident != NULL) {
+			memcpy(ctx->currentTarget->scsi_cmdset.ident,
+			       &discover_contextp->SASInfoFrame[4],
+			       ctx->currentTarget->scsi_cmdset.ident_len);
+		}
+
+		printk("serial number is %s\n",
+		       ctx->currentTarget->scsi_cmdset.ident);
+	}
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_INQUIRY;
+		}
+	}
+
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		asd_log(ASD_DBG_INFO, "%s:%d - %llx %d\n",
+			__FUNCTION__, __LINE__,
+			*((uint64_t *) ctx->currentTarget->
+			  ddb_profile.sas_addr),
+			ctx->currentTarget->ddb_profile.conn_rate);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS;
+		}
+	}
+
+	return ASD_STATE_INIT_SAS_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_init_sas_issue_report_luns(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_report_luns(sm_contextp, ctx->currentTarget);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sas_issue_report_lun_post(struct state_machine_context *
+					 sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	unsigned i;
+	unsigned num_luns;
+	uint64_t *ReportLunsFrame;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		ctx->currentTarget->scsi_cmdset.num_luns = 1;
+
+		ctx->currentTarget->scsi_cmdset.luns = (uint64_t *)
+		    asd_alloc_mem(sizeof(uint64_t), GFP_KERNEL);
+
+		memset(&ctx->currentTarget->scsi_cmdset.luns[0], 0,
+		       sizeof(uint64_t));
+
+		printk("Report LUNS failed\n");
+		/*
+		 * Report LUNS failed.
+		 */
+	} else {
+		ReportLunsFrame = (uint64_t *) discover_contextp->SASInfoFrame;
+
+		num_luns = asd_be32toh(ReportLunsFrame[0]) / sizeof(uint64_t);
+
+		if (num_luns > ASD_MAX_LUNS) {
+			num_luns = ASD_MAX_LUNS;
+		}
+		// printk("%s: found %d luns\n", __FUNCTION__, num_luns);
+
+		ctx->currentTarget->scsi_cmdset.num_luns = num_luns;
+
+		ctx->currentTarget->scsi_cmdset.luns = (uint64_t *)
+		    asd_alloc_mem(sizeof(uint64_t) * num_luns, GFP_KERNEL);
+
+		if (ctx->currentTarget->scsi_cmdset.luns != NULL) {
+			for (i = 0; i < num_luns; i++) {
+				/*
+				 * The SASInfoFrame includes the length
+				 * of the list as the first element.
+				 */
+				ctx->currentTarget->scsi_cmdset.luns[i] =
+				    asd_be64toh(ReportLunsFrame[i + 1]);
+			}
+		} else {
+			ctx->currentTarget->scsi_cmdset.num_luns = 0;
+		}
+	}
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS;
+		}
+	}
+
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		asd_log(ASD_DBG_INFO, "%s:%d - %llx %d\n",
+			__FUNCTION__, __LINE__,
+			*((uint64_t *) ctx->currentTarget->
+			  ddb_profile.sas_addr),
+			ctx->currentTarget->ddb_profile.conn_rate);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_GET_PORT_CONTROL;
+		}
+	}
+
+	return ASD_STATE_INIT_SAS_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_init_sas_get_port_control(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_get_port_control(sm_contextp, ctx->currentTarget);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sas_get_port_control_post(struct state_machine_context *
+					 sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	uint8_t *port_control_page;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		//printk("Port Control failed\n");
+		/*
+		 * Report LUNS failed.
+		 */
+	} else {
+		port_control_page = (uint8_t *) discover_contextp->SASInfoFrame;
+
+#if 0
+		printk("page = 0x%x length = 0x%x protocol = 0x%x\n",
+		       port_control_page[0],
+		       port_control_page[1], port_control_page[2]);
+
+		printk("I_T Nexus Loss Time = %d\n",
+		       asd_be16toh(*((uint16_t *) & port_control_page[4])));
+#endif
+	}
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->command_set_type ==
+		    ASD_COMMAND_SET_SCSI) {
+
+			return ASD_STATE_INIT_SAS_GET_PORT_CONTROL;
+		}
+	}
+
+	return ASD_STATE_INIT_SAS_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_InitSAS_SM_Initialize(struct state_machine_context * sm_contextp,
+			  void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Context *ctx;
+	struct asd_InitSAS_SM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_InitSAS_SM_Arguments *)init_args;
+
+	/*
+	 * go through the configure cycle progressively
+	 * ascending to each expander starting at "newExpander"
+	 */
+	ctx->discover_listp = args->discover_listp;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_InitSAS_SM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_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_INIT_SAS_START:
+		new_state = asd_state_init_sas_start(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_INQUIRY:
+		new_state = asd_state_init_sas_inquiry_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_GET_DEVICE_ID:
+		new_state = asd_state_init_get_device_id_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_GET_SERIAL_NUMBER:
+		new_state = asd_state_init_get_serial_number_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS:
+		new_state =
+		    asd_state_init_sas_issue_report_lun_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_GET_PORT_CONTROL:
+		new_state =
+		    asd_state_init_sas_get_port_control_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_INIT_SAS_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+
+	case ASD_STATE_INIT_SAS_INQUIRY:
+		results = asd_state_init_sas_inquiry(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_GET_DEVICE_ID:
+		results = asd_state_init_get_device_id(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_GET_SERIAL_NUMBER:
+		results = asd_state_init_get_serial_number(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_ISSUE_REPORT_LUNS:
+		results = asd_state_init_sas_issue_report_luns(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_GET_PORT_CONTROL:
+		results = asd_state_init_sas_get_port_control(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SAS_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_InitSAS_SM_Finish(struct state_machine_context *sm_contextp,
+		      DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Arguments *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(results);
+}
+
+void asd_InitSAS_SM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSAS_SM_Arguments *ctx;
+	ASD_DISCOVERY_STATES current_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+#if 0
+	switch (current_state) {
+	}
+#endif
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_SATA_SpinHoldSM_Context {
+	struct discover_context *discover_contextp;
+	struct list_head *discover_listp;
+	struct asd_target *currentTarget;
+	unsigned phyIndex;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_sata_spinhold_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		asd_log(ASD_DBG_INFO, "%s:%d - %llx %d\n",
+			__FUNCTION__, __LINE__,
+			*((uint64_t *) ctx->currentTarget->
+			  ddb_profile.sas_addr),
+			ctx->currentTarget->ddb_profile.conn_rate);
+
+		if ((ctx->currentTarget->transport_type ==
+		     ASD_TRANSPORT_STP) &&
+		    (ctx->currentTarget->ddb_profile.conn_rate ==
+		     SPINUP_HOLD_OOB)) {
+
+			return ASD_STATE_SATA_SPINHOLD_PHY_CONTROL;
+		}
+	}
+
+	return ASD_STATE_SATA_SPINHOLD_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_sata_spinhold_get_next_target(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if ((ctx->currentTarget->transport_type ==
+		     ASD_TRANSPORT_STP) &&
+		    (ctx->currentTarget->ddb_profile.conn_rate ==
+		     SPINUP_HOLD_OOB)) {
+
+			return DISCOVER_OK;
+		}
+	}
+
+	return DISCOVER_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_sata_spinhold_phy_control(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *parent;
+	unsigned i;
+	struct Discover *discover;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	parent = ctx->currentTarget->parent;
+
+	/*
+	 * Find the phy number of this device in the parent.
+	 */
+	for (i = 0; i < parent->num_phys; i++) {
+
+		discover = &(parent->Phy[i].Result);
+
+		if (SAS_ISEQUAL(ctx->currentTarget->ddb_profile.sas_addr,
+				discover->AttachedSASAddress)) {
+
+			break;
+		}
+	}
+
+	if (i == parent->num_phys) {
+
+		results = asd_sata_spinhold_get_next_target(sm_contextp);
+
+		return results;
+	}
+
+	results = asd_issue_phy_control(sm_contextp, parent, i, LINK_RESET);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_sata_spinhold_phy_control_post(struct state_machine_context *
+					 sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *parent;
+	struct Discover *discover;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_phy_control_post(sm_contextp,
+					     ctx->currentTarget->parent);
+
+	if (results != DISCOVER_OK) {
+
+		results = asd_sata_spinhold_get_next_target(sm_contextp);
+
+		if (results != DISCOVER_FINISHED) {
+			return ASD_STATE_SATA_SPINHOLD_PHY_CONTROL;
+		}
+
+		return ASD_STATE_SATA_SPINHOLD_FINISHED;
+	}
+
+	parent = ctx->currentTarget->parent;
+
+	/*
+	 * Find the phy number of the first matching device in the parent.
+	 */
+	for (ctx->phyIndex = 0; ctx->phyIndex < parent->num_phys;
+	     ctx->phyIndex++) {
+
+		discover = &(parent->Phy[ctx->phyIndex].Result);
+
+		if (SAS_ISEQUAL(ctx->currentTarget->ddb_profile.sas_addr,
+				discover->AttachedSASAddress)) {
+
+			return ASD_STATE_SATA_SPINHOLD_DISCOVER;
+		}
+	}
+
+	printk("Didn't find target!\n");
+
+	results = asd_sata_spinhold_get_next_target(sm_contextp);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_SATA_SPINHOLD_PHY_CONTROL;
+	}
+
+	return ASD_STATE_SATA_SPINHOLD_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_sata_spinhold_discover(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *parent;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	parent = ctx->currentTarget->parent;
+
+	results = asd_issue_discover_request(sm_contextp,
+					     parent, ctx->phyIndex);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_sata_spinhold_discover_post(struct state_machine_context *
+				      sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *parent;
+	struct Discover *discover;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	parent = ctx->currentTarget->parent;
+
+	results = asd_issue_discover_request_post(sm_contextp,
+						  parent, ctx->phyIndex);
+
+	if (results != DISCOVER_OK) {
+		printk("discover request failed\n");
+	}
+
+	parent = ctx->currentTarget->parent;
+
+	discover = &(parent->Phy[ctx->phyIndex].Result);
+
+	ctx->currentTarget->ddb_profile.conn_rate =
+	    discover->NegotiatedPhysicalLinkRate;
+
+	asd_hwi_setup_ddb_site(discover_contextp->asd, ctx->currentTarget);
+
+	ctx->phyIndex++;
+
+	/*
+	 * Find the phy number of this device in the parent.
+	 */
+	for (; ctx->phyIndex < parent->num_phys; ctx->phyIndex++) {
+
+		discover = &(parent->Phy[ctx->phyIndex].Result);
+
+		if (SAS_ISEQUAL(ctx->currentTarget->ddb_profile.sas_addr,
+				discover->AttachedSASAddress)) {
+
+			return ASD_STATE_SATA_SPINHOLD_DISCOVER;
+		}
+	}
+
+	results = asd_sata_spinhold_get_next_target(sm_contextp);
+
+	if (results != DISCOVER_FINISHED) {
+		return ASD_STATE_SATA_SPINHOLD_PHY_CONTROL;
+	}
+
+	return ASD_STATE_SATA_SPINHOLD_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_SATA_SpinHoldSM_Initialize(struct state_machine_context * sm_contextp,
+			       void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Context *ctx;
+	struct asd_SATA_SpinHoldSM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_SATA_SpinHoldSM_Arguments *)init_args;
+
+	/*
+	 * go through the configure cycle progressively
+	 * ascending to each expander starting at "newExpander"
+	 */
+	ctx->discover_listp = args->discover_listp;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_SATA_SpinHoldSM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_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_SATA_SPINHOLD_START:
+		new_state = asd_state_sata_spinhold_start(sm_contextp);
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD_PHY_CONTROL:
+		new_state =
+		    asd_state_sata_spinhold_phy_control_post(sm_contextp);
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD_DISCOVER:
+		new_state = asd_state_sata_spinhold_discover_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_SATA_SPINHOLD_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_SATA_SPINHOLD_PHY_CONTROL:
+		results = asd_state_sata_spinhold_phy_control(sm_contextp);
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD_DISCOVER:
+		results = asd_state_sata_spinhold_discover(sm_contextp);
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_SATA_SpinHoldSM_Finish(struct state_machine_context *sm_contextp,
+			   DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Arguments *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(results);
+}
+
+void asd_SATA_SpinHoldSM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_SATA_SpinHoldSM_Arguments *ctx;
+	ASD_DISCOVERY_STATES current_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_SATA_SPINHOLD_PHY_CONTROL:
+		/*
+		 * TODO: we need to abort the outstanding Phy Control request.
+		 */
+		break;
+
+	case ASD_STATE_SATA_SPINHOLD_DISCOVER:
+		/*
+		 * TODO: we need to abort the outstanding Discover request.
+		 */
+		break;
+	default:
+		break;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_ConfigureATA_SM_Context {
+	struct asd_target *target;
+	uint8_t next_feature;
+	uint8_t sector_count;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_configure_ata_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	ctx->next_feature = asd_get_feature_to_enable(ctx->target,
+						      &ctx->sector_count);
+
+	if (ctx->next_feature == 0) {
+		return ASD_STATE_CONFIGURE_ATA_FINISHED;
+	}
+
+	if (ctx->target->transport_type != ASD_TRANSPORT_ATA) {
+		return ASD_STATE_CONFIGURE_ATA_FINISHED;
+	}
+
+	return ASD_STATE_CONFIGURE_ATA_FEATURES;
+}
+
+DISCOVER_RESULTS
+asd_state_configure_ata_features(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_sata_configure_features(sm_contextp, ctx->target,
+					      ctx->next_feature,
+					      ctx->sector_count);
+
+	return results;
+}
+
+uint8_t
+asd_get_feature_to_enable(struct asd_target * target, uint8_t * sector_count)
+{
+	unsigned features_enabled;
+	unsigned features_state;
+	unsigned *dma_mode_level;
+
+	switch (target->command_set_type) {
+	case ASD_COMMAND_SET_ATA:
+		features_enabled = target->ata_cmdset.features_enabled;
+		features_state = target->ata_cmdset.features_state;
+		dma_mode_level = &target->ata_cmdset.dma_mode_level;
+		break;
+
+	case ASD_COMMAND_SET_ATAPI:
+		features_enabled = target->atapi_cmdset.features_enabled;
+		features_state = target->ata_cmdset.features_state;
+		dma_mode_level = &target->atapi_cmdset.dma_mode_level;
+		break;
+
+	default:
+		return 0;
+	}
+
+	*sector_count = 0;
+
+	if (features_enabled & WRITE_CACHE_FEATURE_ENABLED) {
+		if ((features_state & SATA_USES_WRITE_CACHE) == 0) {
+			return SETFEATURES_EN_WCACHE;
+		}
+	} else {
+		if (features_state & SATA_USES_WRITE_CACHE) {
+			return SETFEATURES_DIS_WCACHE;
+		}
+	}
+
+	if (features_enabled & READ_AHEAD_FEATURE_ENABLED) {
+		if ((features_state & SATA_USES_READ_AHEAD) == 0) {
+			return SETFEATURES_EN_RLA;
+		}
+	} else {
+		if (features_state & SATA_USES_READ_AHEAD) {
+			return SETFEATURES_DIS_RLA;
+		}
+	}
+
+	if (features_enabled & SATA_USES_UDMA) {
+		if (features_state & NEEDS_XFER_SETFEATURES) {
+			*sector_count = *dma_mode_level;
+
+			return SETFEATURES_XFER;
+		}
+	}
+
+	return 0;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_configure_ata_features_post(struct state_machine_context *
+				      sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_Context *ctx;
+	unsigned *features_state;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		printk("configure failed\n");
+
+		//TODO: we can't give up, but we need to notify the user
+
+	} else {
+		switch (ctx->target->command_set_type) {
+		case ASD_COMMAND_SET_ATA:
+			features_state =
+			    &ctx->target->ata_cmdset.features_state;
+			break;
+
+		case ASD_COMMAND_SET_ATAPI:
+			features_state =
+			    &ctx->target->ata_cmdset.features_state;
+			break;
+
+		default:
+			return ASD_STATE_CONFIGURE_ATA_FINISHED;
+		}
+
+		switch (ctx->next_feature) {
+		case SETFEATURES_EN_WCACHE:
+			*features_state |= SATA_USES_WRITE_CACHE;
+			break;
+
+		case SETFEATURES_EN_RLA:
+			*features_state |= SATA_USES_READ_AHEAD;
+			break;
+
+		case SETFEATURES_DIS_WCACHE:
+			*features_state &= ~SATA_USES_WRITE_CACHE;
+			break;
+
+		case SETFEATURES_DIS_RLA:
+			*features_state &= ~SATA_USES_READ_AHEAD;
+			break;
+
+		case SETFEATURES_XFER:
+			*features_state &= ~NEEDS_XFER_SETFEATURES;
+			break;
+		}
+	}
+
+	ctx->next_feature = asd_get_feature_to_enable(ctx->target,
+						      &ctx->sector_count);
+
+	if (ctx->next_feature != 0) {
+		return ASD_STATE_CONFIGURE_ATA_FEATURES;
+	}
+
+	return ASD_STATE_CONFIGURE_ATA_FINISHED;
+}
+
+/*
+ * The ConfigureATA state machine operates on a signle target so that it can
+ * be performed on an individual device to reconfigure that device after reset.
+ *
+ * This state machine does not need a discovery context (discover_contextp).
+ */
+DISCOVER_RESULTS
+asd_ConfigureATA_SM_Initialize(struct state_machine_context * sm_contextp,
+			       void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_Context *ctx;
+	struct asd_ConfigureATA_SM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_ConfigureATA_SM_Arguments *)init_args;
+
+	ctx->target = args->target;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_ConfigureATA_SM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_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_CONFIGURE_ATA_START:
+		new_state = asd_state_configure_ata_start(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIGURE_ATA_FEATURES:
+		new_state = asd_state_configure_ata_features_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_CONFIGURE_ATA_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_CONFIGURE_ATA_FEATURES:
+		results = asd_state_configure_ata_features(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIGURE_ATA_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_ConfigureATA_SM_Finish(struct state_machine_context *sm_contextp,
+			   DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	RETURN_STACK(results);
+}
+
+void asd_ConfigureATA_SM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureATA_SM_Context *ctx;
+	ASD_DISCOVERY_STATES current_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_CONFIGURE_ATA_FEATURES:
+		break;
+
+	default:
+		break;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_InitSATA_SM_Context {
+	struct discover_context *discover_contextp;
+	struct list_head *discover_listp;
+	struct asd_target *currentTarget;
+};
+
+ASD_DISCOVERY_STATES
+asd_state_init_sata_start(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	/*
+	 * Are there any devices that need a report phy?
+	 */
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		if (ctx->currentTarget->transport_type == ASD_TRANSPORT_STP) {
+
+			return ASD_STATE_INIT_SATA_REPORT_PHY;
+
+		} else if (ctx->currentTarget->transport_type ==
+			   ASD_TRANSPORT_ATA) {
+
+			asd_init_sata_direct_attached(sm_contextp,
+						      ctx->currentTarget);
+
+			continue;
+		}
+	}
+
+	/*
+	 * If not, are there any devices that need a SATA identify or configure
+	 * features?
+	 */
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		if (ctx->currentTarget->device_protocol_type ==
+		    ASD_DEVICE_PROTOCOL_ATA) {
+
+			return ASD_STATE_INIT_SATA_IDENTIFY;
+		}
+	}
+
+	return ASD_STATE_INIT_SATA_FINISHED;
+}
+
+DISCOVER_RESULTS
+asd_state_init_sata_report_phy(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_target *parent;
+	unsigned i;
+	struct Discover *discover;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	parent = ctx->currentTarget->parent;
+
+	for (i = 0; i < parent->num_phys; i++) {
+
+		discover = &(parent->Phy[i].Result);
+
+		if (SAS_ISEQUAL(ctx->currentTarget->ddb_profile.sas_addr,
+				discover->AttachedSASAddress)) {
+
+			break;
+		}
+	}
+
+	if (i == parent->num_phys) {
+
+		results = DISCOVER_FAILED;
+
+		return results;
+	}
+
+	results = asd_issue_report_phy_sata(sm_contextp, parent, i);
+
+	return results;
+}
+
+#define NUM_FIS_DWORDS \
+		(sizeof(struct adp_dev_to_host_fis) / sizeof(unsigned))
+
+ASD_DISCOVERY_STATES
+asd_init_sata_report_phy_next_target(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->transport_type == ASD_TRANSPORT_STP) {
+
+			return ASD_STATE_INIT_SATA_REPORT_PHY;
+
+		} else if (ctx->currentTarget->transport_type ==
+			   ASD_TRANSPORT_ATA) {
+
+			asd_init_sata_direct_attached(sm_contextp,
+						      ctx->currentTarget);
+		}
+	}
+
+	/*
+	 * If there are no more STP devices to phy control, check to see if
+	 * there are any devices that need IDENTIFY / PIDENTIFY.
+	 */
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		if (ctx->currentTarget->device_protocol_type ==
+		    ASD_DEVICE_PROTOCOL_ATA) {
+
+			return ASD_STATE_INIT_SATA_IDENTIFY;
+		}
+	}
+
+	return ASD_STATE_INIT_SATA_FINISHED;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sata_report_phy_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+	unsigned i;
+	struct adp_dev_to_host_fis *fis;
+	ASD_DISCOVERY_STATES new_state;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	results = asd_issue_report_phy_sata_post(sm_contextp,
+						 ctx->currentTarget->parent);
+
+	if (results != DISCOVER_OK) {
+
+		new_state = asd_init_sata_report_phy_next_target(sm_contextp);
+
+		return new_state;
+	}
+
+	fis = (struct adp_dev_to_host_fis *)&discover_contextp->
+	    SMPResponseFrame->Response.ReportPhySATA.FIS;
+
+	if (fis->error == FIS_DEVICE_TO_HOST) {
+
+		for (i = 0; i < NUM_FIS_DWORDS; i++) {
+
+			*((unsigned *)fis + i) =
+			    asd_htobe32(*((unsigned *)fis + i));
+		}
+	}
+
+	ctx->currentTarget->command_set_type = asd_sata_get_type(fis);
+
+	memcpy((void *)&ctx->currentTarget->
+	       device_protocol.ata_device_protocol.initial_fis[0],
+	       (void *)fis, sizeof(struct adp_dev_to_host_fis));
+
+	ctx->currentTarget->ddb_profile.sata_status = fis->status;
+
+	asd_hwi_update_sata(discover_contextp->asd, ctx->currentTarget);
+
+	new_state = asd_init_sata_report_phy_next_target(sm_contextp);
+
+	return new_state;
+}
+
+DISCOVER_RESULTS
+asd_state_init_sata_identify(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_sata_identify_request(sm_contextp, ctx->currentTarget);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_init_sata_identify_next_target(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->device_protocol_type ==
+		    ASD_DEVICE_PROTOCOL_ATA) {
+
+			return ASD_STATE_INIT_SATA_IDENTIFY;
+		}
+	}
+
+	/*
+	 * If there are no more ATA devices to identify, then check to see
+	 * if any device need to be configured.
+	 */
+	list_for_each_entry(ctx->currentTarget, ctx->discover_listp,
+			    all_domain_targets) {
+
+		if (ctx->currentTarget->device_protocol_type ==
+		    ASD_DEVICE_PROTOCOL_ATA) {
+
+			return ASD_STATE_INIT_SATA_CONFIGURE_FEATURES;
+		}
+	}
+
+	return ASD_STATE_INIT_SATA_FINISHED;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sata_identify_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	ASD_DISCOVERY_STATES new_state;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		printk("identify failed\n");
+
+		//TODO: we can't give up, but we need to notify the user
+	} else {
+		/*
+		 * Pre-compute the features that this drive supports so
+		 * that we don't have to hunt them down in the hd_driveid
+		 * structure.
+		 */
+		asd_sata_compute_support(discover_contextp->asd,
+					 ctx->currentTarget);
+	}
+
+	new_state = asd_init_sata_identify_next_target(sm_contextp);
+
+	return new_state;
+}
+
+DISCOVER_RESULTS
+asd_state_init_sata_configure_features(struct state_machine_context *
+				       sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	DISCOVER_RESULTS results;
+	struct asd_ConfigureATA_SM_Arguments args;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	args.target = ctx->currentTarget;
+
+	results = ASD_PUSH_STATE_MACHINE(sm_contextp,
+					 &asd_ConfigureATA_SM, (void *)&args);
+
+	return results;
+}
+
+ASD_DISCOVERY_STATES
+asd_init_sata_configure_features_next_target(struct state_machine_context *
+					     sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	while (ctx->currentTarget->all_domain_targets.next
+	       != ctx->discover_listp) {
+
+		ctx->currentTarget =
+		    list_entry(ctx->currentTarget->all_domain_targets.next,
+			       struct asd_target, all_domain_targets);
+
+		if (ctx->currentTarget->device_protocol_type ==
+		    ASD_DEVICE_PROTOCOL_ATA) {
+
+			return ASD_STATE_INIT_SATA_CONFIGURE_FEATURES;
+		}
+	}
+
+	return ASD_STATE_INIT_SATA_FINISHED;
+}
+
+ASD_DISCOVERY_STATES
+asd_state_init_sata_configure_features_post(struct state_machine_context *
+					    sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	ASD_DISCOVERY_STATES new_state;
+	struct discover_context *discover_contextp;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (discover_contextp->openStatus != OPEN_ACCEPT) {
+
+		printk("configure failed\n");
+
+		//TODO: we can't give up, but we need to notify the user
+	}
+
+	new_state = asd_init_sata_configure_features_next_target(sm_contextp);
+
+	return new_state;
+}
+
+DISCOVER_RESULTS
+asd_InitSATA_SM_Initialize(struct state_machine_context * sm_contextp,
+			   void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	struct asd_InitSATA_SM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_InitSATA_SM_Arguments *)init_args;
+
+	/*
+	 * go through the configure cycle progressively
+	 * ascending to each expander starting at "newExpander"
+	 */
+	ctx->discover_listp = args->discover_listp;
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_InitSATA_SM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_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_INIT_SATA_START:
+		new_state = asd_state_init_sata_start(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA_REPORT_PHY:
+		new_state = asd_state_init_sata_report_phy_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA_IDENTIFY:
+		new_state = asd_state_init_sata_identify_post(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA_CONFIGURE_FEATURES:
+		new_state =
+		    asd_state_init_sata_configure_features_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_INIT_SATA_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_INIT_SATA_REPORT_PHY:
+		results = asd_state_init_sata_report_phy(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA_IDENTIFY:
+		results = asd_state_init_sata_identify(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA_CONFIGURE_FEATURES:
+		results = asd_state_init_sata_configure_features(sm_contextp);
+		break;
+
+	case ASD_STATE_INIT_SATA_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}
+
+void
+asd_InitSATA_SM_Finish(struct state_machine_context *sm_contextp,
+		       DISCOVER_RESULTS results)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	struct discover_context *discover_contextp;
+	struct asd_phy *phy;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	discover_contextp = (struct discover_context *)
+	    sm_contextp->state_handle;
+
+	if (results == DISCOVER_FAILED) {
+		phy = list_entry(discover_contextp->port->phys_attached.next,
+				 struct asd_phy, links);
+
+		printk("Phy%d: %s - discovery error\n", phy->id, __FUNCTION__);
+	}
+
+	RETURN_STACK(results);
+}
+
+void asd_InitSATA_SM_Abort(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_InitSATA_SM_Context *ctx;
+	ASD_DISCOVERY_STATES current_state;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	current_state = GET_CURRENT_STATE();
+
+	switch (current_state) {
+	case ASD_STATE_INIT_SATA_REPORT_PHY:
+		/*
+		 * TODO: Abort Report Phy request.
+		 */
+		break;
+
+	case ASD_STATE_INIT_SATA_IDENTIFY:
+		/*
+		 * TODO: Abort Identify request.
+		 */
+		break;
+
+	default:
+		break;
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct asd_ConfigureExpanderSM_Context {
+	struct discover_context *discover_contextp;
+	struct list_head *discover_listp;
+	struct asd_target *newExpander;
+	struct asd_target *currentExpander;
+	struct asd_target *configureExpander;
+	uint8_t upstreamSASAddress[SAS_ADDR_LEN];
+	uint8_t upstreamPhyIdentifier;
+	unsigned phyIndex;
+	unsigned routeIndex;
+	uint32_t slowest_link;
+};
+
+DISCOVER_RESULTS
+asd_state_config_expander_route(struct state_machine_context *sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_Context *ctx;
+	uint8_t disableRouteEntry;
+	struct Discover *discover;
+	DISCOVER_RESULTS results;
+	uint8_t *attached_sas_addr;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	for (; 1; ctx->currentExpander = ctx->configureExpander) {
+
+		if (ctx->currentExpander->management_flags & DEVICE_SET_ROOT) {
+			/*
+			 * This is the edge of the device set, we are done.
+			 */
+			return DISCOVER_FINISHED;
+		}
+		/*
+		 * move upstream from here to find the expander table to 
+		 * configure with information from "attachedExpander"
+		 */
+		if (!asd_upstream_expander(ctx->currentExpander,
+					   ctx->upstreamSASAddress,
+					   &ctx->upstreamPhyIdentifier)) {
+
+			return DISCOVER_FINISHED;
+		}
+
+		if (SAS_ISZERO(ctx->upstreamSASAddress)) {
+			return DISCOVER_FINISHED;
+		}
+
+		/*
+		 * get the expander associated with the upstream address
+		 */
+		ctx->configureExpander = asd_find_target(ctx->discover_listp,
+							 ctx->
+							 upstreamSASAddress);
+
+		if (ctx->configureExpander == NULL) {
+			/*
+			 * If we don't have the upstream expander, something
+			 * is wrong.  This should never happen.
+			 */
+			return DISCOVER_FAILED;
+		}
+
+		/*
+		 * if we found an upstream expander, then program its route
+		 * table.
+		 */
+		for (ctx->phyIndex = 0; ctx->phyIndex <
+		     ctx->configureExpander->num_phys; ctx->phyIndex++) {
+
+			attached_sas_addr =
+			    ctx->configureExpander->
+			    Phy[ctx->phyIndex].Result.AttachedSASAddress;
+
+			if (SAS_ISEQUAL(attached_sas_addr,
+					ctx->currentExpander->ddb_profile.
+					sas_addr)) {
+
+				break;
+			}
+		}
+
+		if (ctx->phyIndex == ctx->configureExpander->num_phys) {
+
+			continue;
+		}
+
+		/*
+		 * assume the route entry is enabled
+		 */
+		disableRouteEntry = ENABLED;
+
+		discover = NULL;
+
+		for (ctx->routeIndex = 0;
+		     ctx->routeIndex < ctx->newExpander->num_phys;
+		     ctx->routeIndex++) {
+
+			discover = &(ctx->newExpander->
+				     Phy[ctx->routeIndex].Result);
+
+			/*
+			 * check to see if the address needs to be configured
+			 * in the route table, this decision is based on the
+			 * optimization flag
+			 */
+			if (asd_qualified_address(ctx->configureExpander,
+						  ctx->phyIndex, discover,
+						  &disableRouteEntry)) {
+
+				break;
+			}
+		}
+
+		if (ctx->routeIndex == ctx->newExpander->num_phys) {
+
+			continue;
+		}
+
+		results = asd_issue_route_config(sm_contextp,
+						 ctx->configureExpander,
+						 ctx->phyIndex,
+						 disableRouteEntry,
+						 discover->AttachedSASAddress);
+
+		return results;
+	}
+}
+
+ASD_DISCOVERY_STATES
+asd_state_config_expander_route_post(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_Context *ctx;
+	struct Discover *discover;
+	DISCOVER_RESULTS results;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	results = asd_issue_route_config_post(sm_contextp,
+					      ctx->configureExpander,
+					      ctx->phyIndex);
+
+	if (results == DISCOVER_OK) {
+
+		discover = &(ctx->newExpander->Phy[ctx->routeIndex].Result);
+
+		ctx->routeIndex++;
+
+		/*
+		 * add the address to the internal copy of the
+		 * route table, if successfully configured
+		 */
+		SASCPY(ROUTE_ENTRY(ctx->configureExpander, ctx->phyIndex,
+				   ctx->configureExpander->
+				   route_indexes[ctx->phyIndex]),
+		       discover->AttachedSASAddress);
+
+		ctx->configureExpander->route_indexes[ctx->phyIndex]++;
+
+		return ASD_STATE_CONFIG_EXPANDER_ROUTE_LOOP;
+	}
+
+	return ASD_STATE_CONFIG_EXPANDER_FAILED;
+}
+
+DISCOVER_RESULTS
+asd_state_config_expander_route_loop(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_Context *ctx;
+	DISCOVER_RESULTS results;
+	uint8_t disableRouteEntry;
+	struct Discover *discover;
+	uint8_t *attached_sas_addr;
+
+	GET_STATE_CONTEXT(sm_contextp, ctx);
+
+	while (1) {
+		/*
+		 * assume the route entry is enabled
+		 */
+		disableRouteEntry = ENABLED;
+
+		for (; ctx->routeIndex < ctx->newExpander->num_phys;
+		     ctx->routeIndex++) {
+
+			discover = &(ctx->newExpander->
+				     Phy[ctx->routeIndex].Result);
+
+			/*
+			 * check to see if the address needs to be configured
+			 * in the route table, this decision is based on the
+			 * optimization flag
+			 */
+			if (asd_qualified_address(ctx->configureExpander,
+						  ctx->phyIndex, discover,
+						  &disableRouteEntry)) {
+
+				results = asd_issue_route_config(sm_contextp,
+								 ctx->
+								 configureExpander,
+								 ctx->phyIndex,
+								 disableRouteEntry,
+								 discover->
+								 AttachedSASAddress);
+
+				return results;
+			}
+		}
+
+		ctx->phyIndex++;
+
+		/*
+		 * if we found an upstream expander, then program its route
+		 * table.
+		 */
+		for (; ctx->phyIndex <
+		     ctx->configureExpander->num_phys; ctx->phyIndex++) {
+
+			attached_sas_addr = ctx->configureExpander->
+			    Phy[ctx->phyIndex].Result.AttachedSASAddress;
+
+			if (SAS_ISEQUAL(attached_sas_addr,
+					ctx->currentExpander->ddb_profile.
+					sas_addr)) {
+
+				break;
+			}
+		}
+
+		if (ctx->phyIndex == ctx->configureExpander->num_phys) {
+
+			break;
+		}
+
+		ctx->routeIndex = 0;
+	}
+
+	ctx->phyIndex = 0;
+
+	ctx->currentExpander = ctx->configureExpander;
+
+	results = asd_state_config_expander_route(sm_contextp);
+
+	return results;
+}
+
+DISCOVER_RESULTS
+asd_ConfigureExpanderSM_Initialize(struct state_machine_context * sm_contextp,
+				   void *init_args)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_Context *ctx;
+	struct asd_ConfigureExpanderSM_Arguments *args;
+
+	NEW_CONTEXT(ctx);
+
+	args = (struct asd_ConfigureExpanderSM_Arguments *)init_args;
+
+	/*
+	 * go through the configure cycle progressively
+	 * ascending to each expander starting at "newExpander"
+	 */
+	ctx->discover_listp = args->discover_listp;
+	ctx->newExpander = args->newExpander;
+	ctx->currentExpander = ctx->newExpander;
+	ctx->slowest_link = 0;
+	SAS_ZERO(ctx->upstreamSASAddress);
+
+	return DISCOVER_CONTINUE;
+}
+
+DISCOVER_RESULTS
+asd_ConfigureExpanderSM_StateMachine(struct state_machine_context * sm_contextp)
+{
+	struct state_information *state_infop;
+	struct asd_ConfigureExpanderSM_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_EXPANDER_START:
+		new_state = ASD_STATE_CONFIG_EXPANDER_ROUTE;
+		break;
+
+	case ASD_STATE_CONFIG_EXPANDER_ROUTE:
+		new_state = asd_state_config_expander_route_post(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_EXPANDER_ROUTE_LOOP:
+		new_state = asd_state_config_expander_route_post(sm_contextp);
+		break;
+
+	default:
+		new_state = ASD_STATE_CONFIG_EXPANDER_FAILED;
+		break;
+	}
+
+	SM_new_state(sm_contextp, new_state);
+
+	switch (new_state) {
+	case ASD_STATE_CONFIG_EXPANDER_ROUTE:
+		results = asd_state_config_expander_route(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_EXPANDER_ROUTE_LOOP:
+		results = asd_state_config_expander_route_loop(sm_contextp);
+		break;
+
+	case ASD_STATE_CONFIG_EXPANDER_FINISHED:
+		results = DISCOVER_FINISHED;
+		break;
+
+	default:
+		results = DISCOVER_FAILED;
+		break;
+	}
+
+	return results;
+}





                 reply	other threads:[~2005-02-17 17:35 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4214D5EE.9050801@adaptec.com \
    --to=luben_tuikov@adaptec.com \
    --cc=linux-scsi@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.