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

Anything SATA related.  Part 2/2.

+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_control_mode_select(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+uint8_t			*bufptr
+)
+{
+	if (bufptr[1] != (CONTROL_MODE_PAGE_LEN - 2)) {
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	/*
+	 * T10/04-136r0 doesn't support this command, and Linux libata does
+	 * not support select, so we will do nothing.
+	 */
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_informational_exception_control_select(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+uint8_t			*bufptr
+)
+{
+	unsigned			*features_enabled;
+	unsigned			*features_state;
+	struct asd_target		*target;
+
+	if (bufptr[1] != (INFORMATIONAL_EXCEPTION_CONTROL_PAGE_LEN - 2)) {
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	target = dev->target;
+
+	switch (target->command_set_type) {
+	case ASD_COMMAND_SET_ATA:
+		features_enabled = &target->ata_cmdset.features_enabled;
+		features_state = &target->ata_cmdset.features_state;
+		break;
+
+	case ASD_COMMAND_SET_ATAPI:
+		features_enabled = &target->atapi_cmdset.features_enabled;
+		features_state = &target->ata_cmdset.features_state;
+		break;
+
+	default:
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	/*
+	 * Sense we only support polling and we are translating, there is
+	 * nothing more to do.
+	 */
+	if (bufptr[2] & SCSI_DEXCPT) {
+		*features_enabled &= ~SMART_FEATURE_ENABLED;
+		*features_state &= ~SMART_FEATURE_ENABLED;
+	} else {
+		*features_enabled |= SMART_FEATURE_ENABLED;
+		*features_state |= SMART_FEATURE_ENABLED;
+	}
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * MODE_SENSE: (emulated)
+ * MODE_SENSE_10: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_mode_sense_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	unsigned			page_code;
+	unsigned			page_control;
+	unsigned			subpage_code;
+	unsigned			allocation_length;
+	unsigned			long_lba_accepted;
+	uint8_t				*buffer;
+	uint8_t				*bufptr;
+	unsigned			transfer_length;
+	unsigned			len;
+	struct hd_driveid		*hd_driveidp;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	if (cmd->cmnd[1] & DBD_BIT) {
+		/*
+		 * We don't support disabling block discriptors.
+		 */
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	page_control = cmd->cmnd[2] & PAGE_CONTROL_MASK;
+
+	if (page_control != 0) {
+		/*
+		 * We only support the current values.
+		 */
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	subpage_code = cmd->cmnd[3];
+
+	long_lba_accepted = 0;
+	transfer_length = 0;
+	allocation_length = 0;
+
+	/*
+	 * the first thing we need is a mode_parameter_header
+	 * this depends on whether we are using 6 byte or 10 byte CDBs
+	 */
+	switch (cmd->cmnd[0]) {
+	case MODE_SENSE:
+		transfer_length = MODE_PARAMETER_HEADER_LENGTH_6;
+		allocation_length = cmd->cmnd[4];
+		break;
+	case MODE_SENSE_10:
+		transfer_length = MODE_PARAMETER_HEADER_LENGTH_10;
+		long_lba_accepted = cmd->cmnd[1] & LLBA_MASK;
+		allocation_length = (cmd->cmnd[7] << 8) | cmd->cmnd[8];
+		break;
+	}
+
+	if (long_lba_accepted) {
+		transfer_length += BLOCK_DESCRIPTOR_LENGTH_8;
+	}
+	else {
+		transfer_length += BLOCK_DESCRIPTOR_LENGTH_16;
+	}
+
+	page_code = cmd->cmnd[2] & PAGE_CODE_MASK;
+
+	switch (page_code) {
+	case READ_WRITE_ERROR_RECOVERY_MODE_PAGE:
+		transfer_length += READ_WRITE_ERROR_RECOVERY_MODE_PAGE_LEN;
+		break;
+
+	case CACHING_MODE_PAGE:
+		transfer_length += CACHING_MODE_PAGE_LEN;
+		break;
+
+	case CONTROL_MODE_PAGE:
+		transfer_length += CONTROL_MODE_PAGE_LEN;
+		break;
+
+	case INFORMATIONAL_EXCEPTION_CONTROL_PAGE:
+		transfer_length += INFORMATIONAL_EXCEPTION_CONTROL_PAGE_LEN;
+		break;
+
+	case RETURN_ALL_PAGES:
+		transfer_length += 
+			READ_WRITE_ERROR_RECOVERY_MODE_PAGE_LEN + 
+			CACHING_MODE_PAGE_LEN + 
+			CONTROL_MODE_PAGE_LEN + 
+			INFORMATIONAL_EXCEPTION_CONTROL_PAGE_LEN;
+		break;
+	default:
+		/*
+		 * We don't support disabling block discriptors.
+		 */
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	len = MIN(transfer_length, cmd->request_bufflen);
+	len = MIN(len, allocation_length);
+
+	/*
+	 * We can't transfer more than the size of the buffer.
+	 */
+	cmd->request_bufflen = len;
+
+	/*
+	 * MODE_SENSE (6 byte) only supports 256 bytes of data length.  This
+	 * length doesn't include
+	 */
+	if (cmd->cmnd[0] == MODE_SENSE) {
+		/*
+		 * The length doesn't include the length field itself, so we
+		 * can have 256 bytes even though the length field only holds
+		 * 0-255.
+		 */
+		if (len > 256) {
+			len = 256;
+		}
+	}
+
+	/*
+	 * Allocate the whole length that we are going to fill in, fill the
+	 * buffer up, and then transfer back what was requested.
+	 */
+	buffer = (uint8_t *)asd_alloc_mem(transfer_length, GFP_KERNEL);
+
+	if (buffer == NULL) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			RESOURCE_FAILURE, MEMORY_OUT_OF_SPACE);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	memset(buffer, 0, transfer_length);
+
+	bufptr = buffer;
+
+	/*
+	 * SPC-3 says that the medium type should be 0.
+	 *
+	 * MODE_SENSE:    bufptr[1] == 0
+	 * MODE_SENSE_10: bufptr[2] == 0
+	 */
+	/*
+	 * 3.10.2 and 3.11.2 of T10/04-136r0 says that DPO is ignored and that
+	 * FUA is only supported for NCQ (Native Command Queuing) drives.  We
+	 * are not supporting NCQ in this release, so the answer to DPOFUA will
+	 * be 0 for now.
+	 *
+	 * MODE_SENSE:    bufptr[2] == 0
+	 * MODE_SENSE_10: bufptr[3] == 0
+	 */
+
+	/*
+	 * Now we can fill in the response.  Start with the mode parameter 
+	 * header.
+	 */
+	switch (cmd->cmnd[0]) {
+	case MODE_SENSE:
+		bufptr[0] = len - 1;
+		bufptr[3] = 8; // only one block descriptor
+		bufptr += MODE_PARAMETER_HEADER_LENGTH_6;
+		break;
+
+	case MODE_SENSE_10:
+		bufptr[0] = ((len - 2) >> 8) & 0xff;
+		bufptr[1] = (len - 2) & 0xff;
+		bufptr[6] = 0;
+		if (long_lba_accepted) {
+			bufptr[7] = 16; // only one block descriptor
+		} else {
+			bufptr[7] = 8; // only one block descriptor
+		}
+		bufptr += MODE_PARAMETER_HEADER_LENGTH_10;
+		break;
+	}
+
+	hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+	if (long_lba_accepted) {
+		*((uint64_t *)&bufptr[0]) =
+			ATA2SCSI_8(*((uint64_t *)&hd_driveidp->lba_capacity_2));
+
+		bufptr[8] = 0;	// density code: T10/04-136r0 - 3.21.2.1.3
+
+		*((uint32_t *)&bufptr[12]) = ATA2SCSI_4(ATA_BLOCK_SIZE);
+
+		bufptr += BLOCK_DESCRIPTOR_LENGTH_16;
+	} else {
+		*((uint32_t *)&bufptr[0]) =
+			ATA2SCSI_4(*((uint64_t *)&hd_driveidp->lba_capacity_2));
+
+		bufptr[4] = 0;	// density code: T10/04-136r0 - 3.21.2.1.3
+
+		bufptr[5] = (ATA_BLOCK_SIZE >> 16) & 0xff;
+		bufptr[6] = (ATA_BLOCK_SIZE >> 8) & 0xff;
+		bufptr[7] = ATA_BLOCK_SIZE & 0xff;
+
+		bufptr += BLOCK_DESCRIPTOR_LENGTH_8;
+	}
+
+	switch (page_code) {
+	case READ_WRITE_ERROR_RECOVERY_MODE_PAGE:
+		bufptr = asd_sata_read_write_error_recovery_sense(asd,
+			dev, bufptr);
+		break;
+
+	case CACHING_MODE_PAGE:
+		bufptr = asd_sata_caching_sense(asd, dev, bufptr);
+		break;
+
+	case CONTROL_MODE_PAGE:
+		bufptr = asd_sata_control_sense(asd, dev, bufptr);
+		break;
+
+	case INFORMATIONAL_EXCEPTION_CONTROL_PAGE:
+		bufptr = asd_sata_informational_exception_control_sense(asd,
+			dev, bufptr);
+		break;
+
+	case RETURN_ALL_PAGES:
+		bufptr = asd_sata_read_write_error_recovery_sense(asd,
+			dev, bufptr);
+
+		bufptr = asd_sata_caching_sense(asd, dev, bufptr);
+
+		bufptr = asd_sata_control_sense(asd, dev, bufptr);
+
+		bufptr = asd_sata_informational_exception_control_sense(asd,
+			dev, bufptr);
+
+		break;
+	}
+
+	memcpy(cmd->request_buffer, buffer, len);
+
+	asd_free_mem(buffer);
+
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+uint8_t *
+asd_sata_read_write_error_recovery_sense(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+uint8_t			*bufptr
+)
+{
+	bufptr[0] = READ_WRITE_ERROR_RECOVERY_MODE_PAGE;	// PS == 0
+
+	bufptr[1] = READ_WRITE_ERROR_RECOVERY_MODE_PAGE_LEN - 2;
+	bufptr[2] = 0xc0;	// DCR == 0,  DTE == 0, PER == 0, ERR == 0
+				// RC == 0, TB == 0, ARRE == 1, AWRE == 1
+
+	bufptr[3] = 0x00;	// READ_RETRY_COUNT == 0
+	bufptr[8] = 0x00;	// WRITE_RETRY_COUNT == 0
+
+	bufptr[10] = 0x00;	// RECOVERY_TIME_LIMIT == 0
+	bufptr[11] = 0x00;
+
+	return bufptr + bufptr[1] + 2;
+}
+
+uint8_t *
+asd_sata_caching_sense(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+uint8_t			*bufptr
+)
+{
+	struct hd_driveid		*hd_driveidp;
+	unsigned			features_state;
+	struct asd_target		*target;
+
+	target = dev->target;
+
+	switch (target->command_set_type) {
+	case ASD_COMMAND_SET_ATA:
+		hd_driveidp = &target->ata_cmdset.adp_hd_driveid;
+		features_state = target->ata_cmdset.features_state;
+		break;
+
+	case ASD_COMMAND_SET_ATAPI:
+		hd_driveidp = &target->atapi_cmdset.adp_hd_driveid;
+		features_state = target->atapi_cmdset.features_state;
+		break;
+
+	default:
+		return bufptr + CACHING_MODE_PAGE_LEN;
+	}
+
+	bufptr[0] = CACHING_MODE_PAGE;	// PS == 0
+	bufptr[1] = CACHING_MODE_PAGE_LEN - 2;
+
+	bufptr[2] = 0;
+
+	if (features_state & WRITE_CACHE_FEATURE_ENABLED) {
+		/* 
+		 * RCD == 0, MF == 0, SIZE == 0, DISC == 0,
+		 * CAP == 0, ABPF == 0, IC == 0
+		 */
+		/*
+		 * After drive reset, we should re-issue IDENTIFY.
+		 */
+		bufptr[2] |= SCSI_WCE;
+	} else {
+		bufptr[2] &= ~SCSI_WCE;
+	}
+
+	bufptr[3] = 0;  // DEMAND_READ_RETENTION_PROPERTY == 0
+			// WRITE_RETENTION_PROPERTY == 0
+
+	bufptr[4] = 0;	// DISABLE_PRE_FETCH_TRANSFER_LENGTH == 0
+	bufptr[5] = 0;
+
+	bufptr[6] = 0;	// MINIMUM_PRE_FETCH == 0
+	bufptr[7] = 0;
+
+	bufptr[8] = 0;	// MAXIMUM_PRE_FETCH == 0
+	bufptr[9] = 0;
+
+	bufptr[10] = 0;	// MAXIMUM_PRE_FETCH_CEILING == 0
+	bufptr[11] = 0;
+
+	bufptr[12] = 0;
+
+	if ((features_state & READ_AHEAD_FEATURE_ENABLED) == 0) {
+		/*
+		 * NV_DIS == 0, FSW == 0, LBCSS == 0, FSW == 0
+		 */
+		/*
+		 * After drive reset, we should re-issue IDENTIFY.
+		 */
+		bufptr[12] |= SCSI_DRA;
+	} else {
+		bufptr[12] &= ~SCSI_DRA;
+	}
+
+	bufptr[13] = 0;	// NUMBER_OF_CACHE_SEGMENTS == 0
+
+	bufptr[14] = 0;	// CACHE_SEGMENT_SIZE == 0
+	bufptr[15] = 0;
+
+	bufptr[17] = 0;	// NON_CACHE_SEGMENT_SIZE == 0
+	bufptr[18] = 0;
+	bufptr[19] = 0;
+
+	return bufptr + bufptr[1] + 2;
+}
+
+uint8_t *
+asd_sata_control_sense(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+uint8_t			*bufptr
+)
+{
+	bufptr[0] = CONTROL_MODE_PAGE;
+	bufptr[1] = CONTROL_MODE_PAGE_LEN - 2;
+	bufptr[2] = 2;		// RLEC == 0, GLTSD == 0, D_SENSE == 0, TST == 0
+	bufptr[3] = 0;		// QERR == 0, QUEUE_ALGORITHM_MODIFIER == 0
+	bufptr[4] = 0;		// SWP == 0, UA_INTLCK_CTRL == 0, 
+				// RAC == 0, TAS== 0
+	bufptr[5] = 0;		// AUTOLOAD_MODE == 0, APTG_OWN == 0
+
+	bufptr[8] = 0xff;
+	bufptr[9] = 0xff;
+	bufptr[10] = 0;
+	bufptr[11] = 30;
+
+	return bufptr + bufptr[1] + 2;
+}
+
+uint8_t *
+asd_sata_informational_exception_control_sense(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+uint8_t			*bufptr
+)
+{
+	struct hd_driveid		*hd_driveidp;
+	unsigned			features_state;
+	struct asd_target		*target;
+
+	target = dev->target;
+
+	switch (target->command_set_type) {
+	case ASD_COMMAND_SET_ATA:
+		hd_driveidp = &target->ata_cmdset.adp_hd_driveid;
+		features_state = target->ata_cmdset.features_state;
+		break;
+
+	case ASD_COMMAND_SET_ATAPI:
+		hd_driveidp = &target->atapi_cmdset.adp_hd_driveid;
+		features_state = target->atapi_cmdset.features_state;
+		break;
+
+	default:
+		return bufptr + INFORMATIONAL_EXCEPTION_CONTROL_PAGE_LEN;
+	}
+
+	bufptr[0] = INFORMATIONAL_EXCEPTION_CONTROL_PAGE;	// PS == 0
+	bufptr[1] = INFORMATIONAL_EXCEPTION_CONTROL_PAGE_LEN - 2;
+
+	if ((features_state & SMART_FEATURE_ENABLED) == 0) {
+		/*
+		 * LOGERR == 0, TEST == 0, EWASC == 0, EBF == 0, PERF == 0
+		 */
+		bufptr[2] |= SCSI_DEXCPT;		// disabled
+	}
+
+	bufptr[3] = 0x06;	// MRIE - report on request
+
+	bufptr[4] = 0;	// INTERVAL_TIMER == 0
+	bufptr[5] = 0;
+	bufptr[6] = 0;
+	bufptr[7] = 0;
+
+	bufptr[8] = 0;	// REPORT_COUNT == 0
+	bufptr[9] = 0;
+	bufptr[10] = 0;
+	bufptr[11] = 0;
+
+	return bufptr + bufptr[1] + 2;
+}
+
+/* -----------------------------------
+ * READ_6: (translated)
+ * READ_10: (translated)
+ * READ_12: (translated)
+ */
+
+// RST - we need a tag somehow
+ASD_COMMAND_BUILD_STATUS
+asd_sata_read_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	uint32_t			lba;
+	unsigned			sectors;
+	unsigned			fis_command;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	switch (cmd->cmnd[0]) {
+	case READ_6:
+		lba = (cmd->cmnd[2] << 8) | cmd->cmnd[3];
+		sectors = cmd->cmnd[4];
+
+		break;
+
+	case READ_10:
+		if (cmd->cmnd[9] != 0) {
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+		}
+
+		lba = (cmd->cmnd[2] << 24) | (cmd->cmnd[3] << 16) |
+			(cmd->cmnd[4] << 8) | cmd->cmnd[5];
+		sectors = (cmd->cmnd[7] << 8) | cmd->cmnd[8];
+
+		break;
+
+	case READ_12:
+		if (cmd->cmnd[11] != 0) {
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+		}
+
+		lba = (cmd->cmnd[2] << 24) | (cmd->cmnd[3] << 16) |
+			(cmd->cmnd[4] << 8) | cmd->cmnd[5];
+		sectors = (cmd->cmnd[6] << 24) | (cmd->cmnd[7] << 16) |
+			(cmd->cmnd[8] << 8) | cmd->cmnd[9];
+
+		break;
+
+	default:
+		lba = 0;
+		sectors = 0;
+		break;
+	}
+
+	switch (cmd->cmnd[0]) {
+	case READ_10:
+	case READ_12:
+		if (cmd->cmnd[1] & 0x01) {
+			/*
+			 * Obsolete field that we will fail.
+			 */
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors == 0) {
+			asd_cmd_set_host_status(cmd, DID_OK);
+
+			return ASD_COMMAND_BUILD_FINISHED;
+		}
+	}
+
+	fis_command = 0;
+
+	// RST - what about FUA??
+
+	switch (dev->target->ata_cmdset.features_state & 
+		(SATA_USES_DMA | SATA_USES_48BIT | SATA_USES_QUEUEING)) {
+	case 0:
+		fis_command = WIN_READ;
+		break;
+	case SATA_USES_DMA:
+		fis_command = WIN_READDMA;
+		break;
+	case SATA_USES_48BIT:
+		fis_command = WIN_READ_EXT;
+		break;
+	case SATA_USES_QUEUEING:
+		// Doesn't exist;
+		break;
+	case SATA_USES_DMA | SATA_USES_48BIT:
+		fis_command = WIN_READDMA_EXT;
+		break;
+	case SATA_USES_DMA | SATA_USES_QUEUEING:
+		// RST - sector and feature are swapped for this command
+		fis_command = WIN_READDMA_QUEUED;
+		break;
+	case SATA_USES_48BIT | SATA_USES_QUEUEING:
+		// Doesn't exist
+		break;
+	case SATA_USES_DMA | SATA_USES_48BIT | SATA_USES_QUEUEING:
+		// RST - sector and feature are swapped for this command
+		fis_command = WIN_READDMA_QUEUED_EXT;
+		break;
+	default:
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	asd_sata_setup_fis(ata_hscb, fis_command);
+
+	if (dev->target->ata_cmdset.features_state & SATA_USES_DMA) {
+		SET_DMA_MODE(ata_hscb);
+	} else {
+		SET_PIO_MODE(ata_hscb);
+	}
+
+	if (dev->target->ata_cmdset.features_state & SATA_USES_48BIT) {
+
+		asd_sata_setup_lba_ext(ata_hscb, lba, sectors);
+
+	} else {
+		if (lba >= RW_DMA_LBA_SIZE) {
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors > RW_DMA_MAX_SECTORS) {
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors == RW_DMA_MAX_SECTORS) {
+			sectors = 0;
+		}
+
+		asd_sata_setup_lba(ata_hscb, lba, sectors);
+	}
+
+	ata_hscb->ata_flags |= DATA_DIR_INBOUND;
+
+	asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_read_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_read_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb		*ata_resp_edbp;
+	struct scsi_cmnd		*cmd;
+#if 0
+	unsigned			sectors;
+	uint32_t			lba;
+	struct asd_ata_task_hscb	*ata_hscb;
+	unsigned			remain;
+#endif
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+#if 0
+	ata_hscb = &scb->hscb->ata_task;
+#endif
+
+	switch (done_listp->opcode) {
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	case TASK_COMP_WO_ERR:
+		break;
+
+	default:
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	}
+
+#if 0
+	if (remain > 0) {
+
+		sectors = remain / RW_DMA_MAX_SECTORS ;
+
+		if (sectors >= RW_DMA_MAX_SECTORS) {
+			scb->transfer_length = RW_DMA_MAX_SECTORS *
+				ATA_BLOCK_SIZE;
+
+			sectors = 0;
+		} else {
+
+			scb->transfer_length = remain;
+		}
+
+
+		lba = (ASD_H2D_FIS(ata_hscb)->lba2  << 16) |
+			(ASD_H2D_FIS(ata_hscb)->lba1 << 8) |
+			(ASD_H2D_FIS(ata_hscb)->lba0);
+
+		lba = lba + sectors;
+
+		printk("%s:%d: finishing request lba 0x%x sectors 0x%x\n",
+			__FUNCTION__, __LINE__,
+			lba, sectors);
+
+		asd_sata_setup_lba(ata_hscb, lba, sectors);
+
+		asd_hwi_post_scb(asd, scb);
+
+		return;
+	}
+#endif
+
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+/* -----------------------------------
+ * WRITE_BUFFER: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_write_buffer_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	unsigned			buffer_offset;
+	unsigned			len;
+	unsigned			allocation_length;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	if ((dev->target->ata_cmdset.features_state & 
+		SATA_USES_WRITE_BUFFER) == 0) {
+
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	if ((cmd->cmnd[1] & BUFFER_MODE_MASK) != DATA_ONLY_MODE) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	/*
+	 * Only support Buffer ID of 0
+	 */
+	if (cmd->cmnd[2] != 0) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	buffer_offset = 
+		(cmd->cmnd[3] << 16) | (cmd->cmnd[4] << 8) | cmd->cmnd[5];
+
+	if (buffer_offset >= ATA_BUFFER_SIZE) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	allocation_length = 
+		(cmd->cmnd[6] << 16) | (cmd->cmnd[7] << 8) | cmd->cmnd[8];
+
+	len = MIN(ATA_BUFFER_SIZE - buffer_offset, cmd->request_bufflen);
+	len = MIN(len, allocation_length);
+
+	/*
+	 * We can't transfer more than the size of the buffer.
+	 */
+	cmd->request_bufflen = len;
+
+	asd_sata_setup_fis(ata_hscb, WIN_WRITE_BUFFER);
+
+	ata_hscb->ata_flags |= DATA_DIR_OUTBOUND;
+	SET_PIO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_write_buffer_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_write_buffer_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb	*ata_resp_edbp;
+	struct scsi_cmnd	*cmd;
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+	switch (done_listp->opcode) {
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	case TASK_COMP_WO_ERR:
+		asd_cmd_set_host_status(cmd, DID_OK);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	default:
+		break;
+	}
+
+	asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+/* -----------------------------------
+ * READ_BUFFER: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_read_buffer_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	uint8_t				*read_buffer_descriptor;
+	unsigned			buffer_offset;
+	unsigned			allocation_length;
+	unsigned			len;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	if ((dev->target->ata_cmdset.features_state & 
+		SATA_USES_READ_BUFFER) == 0) {
+
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	if (cmd->cmnd[2] != 0) {
+		/*
+		 * We are only supporting 1 buffer ID (== 0).
+		 */
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	allocation_length = 0;
+
+	if ((cmd->cmnd[1] & BUFFER_MODE_MASK) == DESCRIPTOR_MODE) {
+
+		if (cmd->request_bufflen < READ_BUFFER_DESCRIPTOR_LENGTH) {
+
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		read_buffer_descriptor = (uint8_t *)cmd->request_buffer;
+
+		memset(read_buffer_descriptor, 0, 
+			READ_BUFFER_DESCRIPTOR_LENGTH);
+
+		/*
+		 * ATA only supports ATA_BUFFER_SIZE byte buffer writes.
+		 */
+		read_buffer_descriptor[0] = 0;
+		read_buffer_descriptor[1] = (ATA_BUFFER_SIZE >> 16) & 0xff;
+		read_buffer_descriptor[2] = (ATA_BUFFER_SIZE >> 8) & 0xff;
+		read_buffer_descriptor[3] = ATA_BUFFER_SIZE & 0xff;
+
+		asd_cmd_set_host_status(cmd, DID_OK);
+
+		return ASD_COMMAND_BUILD_FINISHED;
+	}
+
+	if ((cmd->cmnd[1] & BUFFER_MODE_MASK) != DATA_ONLY_MODE) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	buffer_offset = 
+		(cmd->cmnd[3] << 16) | (cmd->cmnd[4] << 8) | cmd->cmnd[5];
+
+	if (buffer_offset >= ATA_BUFFER_SIZE) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	len = MIN(ATA_BUFFER_SIZE - buffer_offset, cmd->request_bufflen);
+	len = MIN(len, allocation_length);
+
+	/*
+	 * We can't transfer more than the size of the buffer.
+	 */
+	cmd->request_bufflen = len;
+
+	asd_sata_setup_fis(ata_hscb, WIN_READ_BUFFER);
+
+	ata_hscb->ata_flags |= DATA_DIR_INBOUND;
+	SET_PIO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_read_buffer_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_read_buffer_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb	*ata_resp_edbp;
+	struct scsi_cmnd	*cmd;
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+	switch (done_listp->opcode) {
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	case TASK_COMP_WO_ERR:
+		asd_cmd_set_host_status(cmd, DID_OK);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	default:
+		break;
+	}
+
+	asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+/* -----------------------------------
+ * READ_CAPACITY: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_read_capacity_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	struct hd_driveid		*hd_driveidp;
+	u_char				*read_capacity_data;
+	uint64_t			lba_capacity;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+	if (cmd->request_bufflen < READ_CAPACITY_DATA_LEN) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	read_capacity_data = (u_char *)cmd->request_buffer;
+
+	lba_capacity = *((uint64_t *)&hd_driveidp->lba_capacity_2);
+
+	/*
+	 * Ignore the Logical Block Address field and the PMI bit
+	 */
+	if (lba_capacity == 0) {
+		lba_capacity = *((uint32_t *)&hd_driveidp->lba_capacity);
+	}
+
+	*((uint32_t *)&read_capacity_data[0]) = ATA2SCSI_4(lba_capacity - 1);
+
+	*((uint32_t *)&read_capacity_data[4]) = ATA2SCSI_4(ATA_BLOCK_SIZE);
+
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * REPORT_LUNS: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_report_luns_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	u_char		report_luns_data[REPORT_LUNS_SIZE];
+	unsigned	len;
+
+	memset(report_luns_data, 0, REPORT_LUNS_SIZE);
+
+	*((uint32_t *)&report_luns_data[0]) = asd_htobe32(8);
+
+	len = MIN(cmd->request_bufflen, REPORT_LUNS_SIZE);
+
+	memcpy(cmd->request_buffer, &report_luns_data[0], len);
+
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * REQUEST_SENSE: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_request_sense_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * REZERO_UNIT: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_rezero_unit_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * SEEK_6: (emulated)
+ * SEEK_10: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_seek_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	switch (cmd->cmnd[0]) {
+	case SEEK_6:
+	case SEEK_10:
+		break;
+	}
+
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * SEND_DIAGNOSTIC: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_send_diagnostic_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	/*
+	 * "Execute Device Diagnostic"
+	 */
+	asd_sata_setup_fis(ata_hscb, WIN_DIAGNOSE);
+
+	ata_hscb->ata_flags |= DATA_DIR_NO_XFER;
+	SET_NO_IO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)cmd,
+		asd_sata_send_diagnostic_post);
+
+	if ((cmd->cmnd[1] & SELFTEST) == 0) {
+
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	if ((cmd->cmnd[1] & SELFTEST_CODE_MASK) != 0) {
+
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_send_diagnostic_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb	*ata_resp_edbp;
+	struct scsi_cmnd	*cmd;
+	COMMAND_SET_TYPE	command_set_type;
+	struct asd_device	*dev;
+	unsigned		error;
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+	dev = scb->platform_data->dev;
+
+	switch (done_listp->opcode) {
+	case CSMI_TASK_COMP_WO_ERR:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		break;
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+	default:
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+	}
+
+	command_set_type = asd_sata_get_type(ASD_D2H_FIS(ata_resp_edbp));
+
+	if (command_set_type != dev->target->command_set_type) {
+		/*
+		 * Signatures don't match.
+		 */
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+	}
+
+	asd_pop_post_stack(asd, scb, done_listp);
+
+	error = ASD_D2H_FIS(ata_resp_edbp)->error;
+	
+	if (error == 0x01) {
+		/*
+		 * device 0 passed
+		 */
+		asd_cmd_set_host_status(cmd, DID_OK);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+	}
+
+	if ((error == 0x00) || (error > 0x02)) {
+		/*
+		 * The device failed internal tests.
+		 */
+		asd_sata_set_check_condition(cmd, HARDWARE_ERROR,
+			LOGICAL_UNIT_FAILURE, FAILED_SELF_TEST);
+
+		asd_pop_post_stack(asd, scb, done_listp);
+
+		return;
+	}
+
+	asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+/* -----------------------------------
+ * START_STOP: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_start_stop_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	if (cmd->cmnd[4] & START_STOP_LOEJ) {
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	if (cmd->cmnd[4] & START_STOP_START) {
+		asd_sata_setup_fis(ata_hscb, WIN_IDLEIMMEDIATE);
+	} else {
+		/*
+		 * "Standby Immediate"
+		 */
+		asd_sata_setup_fis(ata_hscb, WIN_STANDBYNOW1);
+	}
+
+	ata_hscb->ata_flags |= DATA_DIR_NO_XFER;
+	SET_NO_IO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_start_stop_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_start_stop_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb	*ata_resp_edbp;
+	struct scsi_cmnd	*cmd;
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+	switch (done_listp->opcode) {
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	case TASK_COMP_WO_ERR:
+		asd_cmd_set_host_status(cmd, DID_OK);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	default:
+		break;
+	}
+
+	asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+/* -----------------------------------
+ * SYNCHRONIZE_CACHE: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_synchronize_cache_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	asd_sata_setup_fis(ata_hscb, WIN_FLUSH_CACHE);
+
+	ata_hscb->ata_flags |= DATA_DIR_NO_XFER;
+	SET_NO_IO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)cmd,
+		asd_sata_synchronize_cache_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_synchronize_cache_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb	*ata_resp_edbp;
+	struct scsi_cmnd	*cmd;
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+	switch (done_listp->opcode) {
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	case TASK_COMP_WO_ERR:
+		asd_cmd_set_host_status(cmd, DID_OK);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	default:
+		break;
+	}
+
+	asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_COMMAND_OPERATION, 0);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+/* -----------------------------------
+ * TEST_UNIT_READY: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_test_unit_ready_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	struct hd_driveid		*hd_driveidp;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+	if ((hd_driveidp->command_set_1 & POWER_MANAGEMENT_SUPPORTED) == 0) {
+
+		asd_cmd_set_host_status(cmd, DID_OK);
+
+		return ASD_COMMAND_BUILD_FINISHED;
+	}
+
+	asd_sata_setup_fis(ata_hscb, WIN_CHECKPOWERMODE1);
+
+	/*
+	 * The CSMI task bit is a misnomer.  It really means to return an
+	 * ATA Response Empty Buffer done list entry even though there was no
+	 * error.  Normally this buffer gets returned from a ATA TASK COMPLETE
+	 * WITH RESPONSE done list entry.
+	 */
+	ata_hscb->ata_flags |= (DATA_DIR_NO_XFER | CSMI_TASK);
+	SET_NO_IO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)cmd,
+		asd_sata_test_unit_ready_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+
+void
+asd_sata_test_unit_ready_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb		*ata_resp_edbp;
+	struct scsi_cmnd		*cmd;
+	struct asd_ata_task_hscb	*ata_hscb;
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+	switch (done_listp->opcode) {
+	case CSMI_TASK_COMP_WO_ERR:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		break;
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+	default:
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+	}
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	switch (ASD_H2D_FIS(ata_hscb)->sector_count) {
+#if 0
+	// RST - this doesn't seem to work
+	case ATA_STANDBY_MODE:
+		asd_sata_set_check_condition(cmd, NOT_READY,
+			LOGICAL_UNIT_NOT_READY, NOTIFY_REQUIRED);
+		break;
+#endif
+
+	default:
+	case ATA_IDLE_MODE:
+	case ATA_ACTIVE:
+		asd_cmd_set_host_status(cmd, DID_OK);
+		break;
+	}
+
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+
+#ifdef T10_04_136
+/* -----------------------------------
+ * VERIFY: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_verify_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	if (target->ata_cmdset.features_state & SATA_USES_48BIT) {
+		asd_sata_setup_fis(ata_hscb, WIN_VERIFY_EXT);
+	} else {
+		asd_sata_setup_fis(ata_hscb, WIN_VERIFY);
+	}
+
+	ata_hscb->ata_flags |= DATA_DIR_NO_XFER;
+	SET_NO_IO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)acmd,
+		asd_sata_verify_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_verify_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	// RST - fill this in
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+#endif
+
+
+/* -----------------------------------
+ * WRITE_6: (translated)
+ * WRITE_10: (translated)
+ * WRITE_12: (translated)
+ */
+
+// RST - we need a tag somehow
+ASD_COMMAND_BUILD_STATUS
+asd_sata_write_build(
+struct asd_softc	*asd,
+struct asd_device	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	uint32_t			lba;
+	unsigned			sectors;
+	unsigned			fis_command;
+	unsigned			fua_bit;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	switch (cmd->cmnd[0]) {
+	case WRITE_6:
+		lba = (cmd->cmnd[2] << 8) | cmd->cmnd[3];
+		sectors = cmd->cmnd[4];
+
+		break;
+
+	case WRITE_10:
+		lba = (cmd->cmnd[2] << 24) | (cmd->cmnd[3] << 16) |
+			(cmd->cmnd[4] << 8) | cmd->cmnd[5];
+		sectors = (cmd->cmnd[7] << 8) | cmd->cmnd[8];
+
+		break;
+
+	case WRITE_12:
+		lba = (cmd->cmnd[2] << 24) | (cmd->cmnd[3] << 16) |
+			(cmd->cmnd[4] << 8) | cmd->cmnd[5];
+		sectors = (cmd->cmnd[6] << 24) | (cmd->cmnd[7] << 16) |
+			(cmd->cmnd[8] << 8) | cmd->cmnd[9];
+
+		break;
+	default:
+		lba = 0;
+		sectors = 0;
+	}
+
+	fua_bit = 0;
+
+	switch (cmd->cmnd[0]) {
+	case WRITE_10:
+	case WRITE_12:
+		if (cmd->cmnd[1] & 0x01) {
+			/*
+			 * Obsolete field that we will fail.
+			 */
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors == 0) {
+			asd_cmd_set_host_status(cmd, DID_OK);
+
+			return ASD_COMMAND_BUILD_FINISHED;
+		}
+
+		if ((dev->target->ata_cmdset.features_state & 
+			SATA_USES_WRITE_FUA) != 0) {
+
+			fua_bit = cmd->cmnd[1] & SCSI_WRITE_FUA_BIT;
+		}
+		break;
+	}
+
+	fis_command = 0;
+
+	switch (dev->target->ata_cmdset.features_state & 
+		(SATA_USES_DMA | SATA_USES_48BIT | SATA_USES_QUEUEING)) {
+	case 0:
+		fis_command = WIN_WRITE;
+		break;
+	case SATA_USES_DMA:
+		fis_command = WIN_WRITEDMA;
+		break;
+	case SATA_USES_48BIT:
+		fis_command = WIN_WRITE_EXT;
+		break;
+	case SATA_USES_QUEUEING:
+		// Doesn't exist;
+		break;
+	case SATA_USES_DMA | SATA_USES_48BIT:
+		if (fua_bit) {
+			fis_command = WIN_WRITE_DMA_FUA_EXT;
+		} else {
+			fis_command = WIN_WRITEDMA_EXT;
+		}
+		break;
+	case SATA_USES_DMA | SATA_USES_QUEUEING:
+		fis_command = WIN_WRITEDMA_QUEUED;
+		break;
+	case SATA_USES_48BIT | SATA_USES_QUEUEING:
+		// Doesn't exist
+		break;
+	case SATA_USES_DMA | SATA_USES_48BIT | SATA_USES_QUEUEING:
+		if (fua_bit) {
+			fis_command = WIN_WRITE_DMA_QUEUED_FUA_EXT;
+		} else {
+			fis_command = WIN_WRITEDMA_QUEUED_EXT;
+		}
+		break;
+	default:
+		fis_command = 0;
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+			INVALID_FIELD_IN_CDB, 0);
+		return ASD_COMMAND_BUILD_FAILED;
+	}
+
+	asd_sata_setup_fis(ata_hscb, fis_command);
+
+	if (dev->target->ata_cmdset.features_state & SATA_USES_DMA) {
+		SET_DMA_MODE(ata_hscb);
+	} else {
+		SET_PIO_MODE(ata_hscb);
+	}
+
+	if (dev->target->ata_cmdset.features_state & SATA_USES_48BIT) {
+
+		asd_sata_setup_lba_ext(ata_hscb, lba, sectors);
+
+	} else {
+		if (lba >= RW_DMA_LBA_SIZE) {
+
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors > RW_DMA_MAX_SECTORS) {
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors == RW_DMA_MAX_SECTORS) {
+			sectors = 0;
+		}
+
+		asd_sata_setup_lba(ata_hscb, lba, sectors);
+	}
+
+	ata_hscb->ata_flags |= DATA_DIR_OUTBOUND;
+
+	asd_push_post_stack(asd, scb, (void *)cmd,
+		asd_sata_write_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_write_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	struct ata_resp_edb		*ata_resp_edbp;
+	struct scsi_cmnd		*cmd;
+#if 0
+	uint32_t			lba;
+	struct asd_ata_task_hscb	*ata_hscb;
+	unsigned			sectors;
+#endif
+
+	cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+#if 0
+	ata_hscb = &scb->hscb->ata_task;
+#endif
+
+	switch (done_listp->opcode) {
+	case ATA_TASK_COMP_W_RESP:
+		ata_resp_edbp = asd_sata_get_edb(asd, done_listp);
+		asd_sata_check_registers(ata_resp_edbp, scb, cmd);
+		asd_pop_post_stack(asd, scb, done_listp);
+		return;
+
+	case TASK_COMP_WO_ERR:
+		break;
+
+	default:
+		asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_COMMAND_OPERATION, 0);
+
+		asd_pop_post_stack(asd, scb, done_listp);
+
+		return;
+	}
+
+#if 0
+	if (scb->sectors_remaining != 0) {
+
+		sectors = scb->sectors_remaining;
+
+		if (sectors >= RW_DMA_MAX_SECTORS) {
+			scb->sectors_remaining = 
+				scb->sectors_remaining - RW_DMA_MAX_SECTORS;
+
+			sectors = 0;
+		} else {
+
+			scb->sectors_remaining = 0;
+		}
+
+		lba = (ASD_H2D_FIS(ata_hscb)->lba2  << 16) |
+			(ASD_H2D_FIS(ata_hscb)->lba1 << 8) |
+			(ASD_H2D_FIS(ata_hscb)->lba0);
+
+		lba = lba + sectors;
+
+		printk("%s:%d: finishing request lba 0x%x sectors 0x%x "
+			"remaining 0x%x\n",
+			__FUNCTION__, __LINE__,
+			lba, sectors, scb->sectors_remaining);
+
+		asd_sata_setup_lba(ata_hscb, lba, sectors);
+
+		ret = asd_setup_data(asd, scb, cmd);
+
+		asd_hwi_post_scb(asd, scb);
+
+
+		return;
+	}
+#endif
+
+	asd_cmd_set_host_status(cmd, DID_OK);
+
+	asd_pop_post_stack(asd, scb, done_listp);
+
+	return;
+}
+
+#ifdef T10_04_136
+/* -----------------------------------
+ * WRITE_VERIFY: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_write_verify_build(
+struct asd_softc	*asd,
+struct asd_device 	*dev,
+struct scb		*scb,
+struct scsi_cmnd	*cmd
+)
+{
+	struct asd_ata_task_hscb	*ata_hscb;
+	uint32_t			lba;
+	unsigned			sectors;
+
+	ata_hscb = &scb->hscb->ata_task;
+
+	lba = (cmd->cmnd[2] << 24) | (cmd->cmnd[3] << 16) |
+		(cmd->cmnd[4] << 8) | cmd->cmnd[5];
+
+	sectors = (cmd->cmnd[7] << 8) | cmd->cmnd[8];
+
+	// RST - this should be a WRITE with FUA followed by a read verify.
+	// ... we will want to set affiliation bits in the ata_hscb
+
+	if (target->ata_cmdset.features_state & SATA_USES_48BIT) {
+		/*
+		 * Read Verify Sectors
+		 */
+		asd_sata_setup_fis(ata_hscb, WIN_VERIFY_EXT);
+	} else {
+		if (sectors > RW_DMA_MAX_SECTORS) {
+			asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+				INVALID_FIELD_IN_CDB, 0);
+
+			return ASD_COMMAND_BUILD_FAILED;
+		}
+
+		if (sectors == RW_DMA_MAX_SECTORS) {
+			sectors = 0;
+		}
+
+		asd_sata_setup_fis(ata_hscb, WIN_VERIFY);
+	}
+
+	ata_hscb->ata_flags |= DATA_DIR_NO_XFER;
+	SET_NO_IO_MODE(ata_hscb);
+
+	asd_push_post_stack(asd, scb, (void *)acmd,
+		asd_sata_write_verify_post);
+
+	return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_write_verify_post(
+struct asd_softc	*asd,
+struct scb		*scb,
+struct asd_done_list	*done_listp
+)
+{
+	asd_pop_post_stack(asd, scb, done_listp);
+}
+#endif
+
+#if 0
+void
+asd_print_hex(
+unsigned char	*s,
+unsigned	len
+)
+{
+	unsigned	i;
+	unsigned	count;
+
+	while (len != 0) {
+		count = (len > 16) ? 16 : len;
+
+		for (i = 0 ; i < count ; i++) {
+			printk("%02x ", *(s + i));
+		}
+
+		for ( ; i < 16 ; i++) {
+			printk("   ");
+		}
+
+		for (i = 0 ; i < count ; i++) {
+			if (((*(s + i) >= 'a') && (*(s + i) <= 'z')) ||
+			    ((*(s + i) >= 'A') && (*(s + i) <= 'Z')) ||
+			    ((*(s + i) >= '0') && (*(s + i) <= '9'))) {
+				printk("%c", *(s + i));
+			} else {
+				printk(".");
+			}
+		}
+
+		printk("\n");
+
+		len = len - count;
+
+		s = s + count;
+	}
+}
+#endif


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

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

Comment:  You should use libata for your ATA<->SCSI translation.

Doing so eliminates duplicate code, and ensures that you are compliant 
with the SAT and ATA passthru specifications from T10.

	Jeff




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

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

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-17 17:37 [ANNOUNCE] Adaptec SAS/SATA device driver [22/27] Luben Tuikov
2005-02-17 20:11 ` Jeff Garzik

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