From: Luben Tuikov <luben_tuikov@adaptec.com>
To: SCSI Mailing List <linux-scsi@vger.kernel.org>
Subject: [ANNOUNCE] Adaptec SAS/SATA device driver [22/27]
Date: Thu, 17 Feb 2005 12:37:31 -0500 [thread overview]
Message-ID: <4214D65B.2030008@adaptec.com> (raw)
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
next reply other threads:[~2005-02-17 17:37 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-02-17 17:37 Luben Tuikov [this message]
2005-02-17 20:11 ` [ANNOUNCE] Adaptec SAS/SATA device driver [22/27] Jeff Garzik
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4214D65B.2030008@adaptec.com \
--to=luben_tuikov@adaptec.com \
--cc=linux-scsi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.