All of lore.kernel.org
 help / color / mirror / Atom feed
* [ANNOUNCE] Adaptec SAS/SATA device driver [04/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 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;
+}





^ 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 [04/27] Luben Tuikov

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.