* [ANNOUNCE] Adaptec SAS/SATA device driver [21/27]
@ 2005-02-17 17:37 Luben Tuikov
0 siblings, 0 replies; only message in thread
From: Luben Tuikov @ 2005-02-17 17:37 UTC (permalink / raw)
To: SCSI Mailing List
Anything SATA related. Part 1/2.
diff -Nru a/drivers/scsi/adp94xx/adp94xx_sata.c b/drivers/scsi/adp94xx/adp94xx_sata.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/scsi/adp94xx/adp94xx_sata.c 2005-02-16 16:08:12 -05:00
@@ -0,0 +1,1891 @@
+/*
+ * Adaptec ADP94xx SAS HBA device driver for Linux.
+ *
+ * Copyright (c) 2004 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Written by : Robert Tarte <robt@PacificCodeWorks.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/razor/linux/src/adp94xx_sata.c#39 $
+ *
+ */
+#define KDB_ENABLE 0
+
+#include "adp94xx_osm.h"
+#include "adp94xx_inline.h"
+#include "adp94xx_sata.h"
+#include "adp94xx_discover.h"
+
+#if KDB_ENABLE
+#include "linux/kdb.h"
+#endif
+
+// RST:
+// - What if the device doesn't support LBA???
+// - What if the number of outstanding (tagged) requests exceeds the
+// number of Empty SCBs
+// - figure out tagged queueing modes
+// - overlap == disconnect???
+
+static void asd_sata_linux_scb_done(struct asd_softc *asd, struct scb *scb,
+ struct asd_done_list *dl);
+
+
+ASD_COMMAND_BUILD_STATUS
+asd_build_ata_scb(
+struct asd_softc *asd,
+struct scb *scb,
+union asd_cmd *acmd
+)
+{
+ ASD_COMMAND_BUILD_STATUS ret;
+ struct asd_ata_task_hscb *ata_hscb;
+ struct asd_target *target;
+ struct scsi_cmnd *cmd;
+ struct asd_device *dev;
+
+ dev = scb->platform_data->dev;
+
+ cmd = &acmd->scsi_cmd;
+
+ /*
+ * Guard against stale sense data. The Linux mid-layer assumes that
+ * sense was retrieved anytime the first byte of the sense buffer
+ * looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ cmd->resid = 0;
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ target = dev->target;
+
+ ata_hscb->header.opcode = SCB_INITIATE_ATA_TASK;
+ ata_hscb->protocol_conn_rate =
+ PROTOCOL_TYPE_SATA | target->ddb_profile.conn_rate;
+ ata_hscb->xfer_len = asd_htole32(cmd->request_bufflen);
+
+ ata_hscb->data_offset = 0;
+ ata_hscb->sister_scb = 0xffff;
+ ata_hscb->conn_handle = target->ddb_profile.conn_handle;
+ ata_hscb->retry_cnt = TASK_RETRY_CNT;
+ ata_hscb->affiliation_policy = 0;
+
+#ifdef TAGGED_QUEUING
+ // RST - add support for SATA II queueing
+ ata_hscb->ata_flags = LEGACY_QUEUING;
+#else
+ ata_hscb->ata_flags = UNTAGGED;
+#endif
+ /*
+ * Guard against stale sense data. The Linux mid-layer assumes that
+ * sense was retrieved anytime the first byte of the sense buffer
+ * looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ cmd->resid = 0;
+
+ asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_linux_scb_done);
+
+ switch (cmd->cmnd[0]) {
+ case FORMAT_UNIT:
+ ret = asd_sata_format_unit_build(asd, dev, scb, cmd);
+
+ /*
+ * We will hand build the scb, no need to call asd_setup_data.
+ */
+ return ret;
+
+ case INQUIRY:
+ ret = asd_sata_inquiry_build(asd, dev, scb, cmd);
+ break;
+ case LOG_SENSE:
+ ret = asd_sata_log_sense_build(asd, dev, scb, cmd);
+ break;
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ ret = asd_sata_mode_select_build(asd, dev, scb, cmd);
+ break;
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ ret = asd_sata_mode_sense_build(asd, dev, scb, cmd);
+ break;
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ ret = asd_sata_read_build(asd, dev, scb, cmd);
+ break;
+ case WRITE_BUFFER:
+ ret = asd_sata_write_buffer_build(asd, dev, scb, cmd);
+ break;
+ case READ_BUFFER:
+ ret = asd_sata_read_buffer_build(asd, dev, scb, cmd);
+ break;
+ case READ_CAPACITY:
+ ret = asd_sata_read_capacity_build(asd, dev, scb, cmd);
+ break;
+ case REPORT_LUNS:
+ ret = asd_sata_report_luns_build(asd, dev, scb, cmd);
+ break;
+ case REQUEST_SENSE:
+ ret = asd_sata_request_sense_build(asd, dev, scb, cmd);
+ break;
+ case REZERO_UNIT:
+ ret = asd_sata_rezero_unit_build(asd, dev, scb, cmd);
+ break;
+ case SEEK_6:
+ case SEEK_10:
+ ret = asd_sata_seek_build(asd, dev, scb, cmd);
+ break;
+ case SEND_DIAGNOSTIC:
+ ret = asd_sata_send_diagnostic_build(asd, dev, scb, cmd);
+ break;
+ case START_STOP:
+ ret = asd_sata_start_stop_build(asd, dev, scb, cmd);
+ break;
+ case SYNCHRONIZE_CACHE:
+ ret = asd_sata_synchronize_cache_build(asd, dev, scb, cmd);
+ break;
+ case TEST_UNIT_READY:
+ ret = asd_sata_test_unit_ready_build(asd, dev, scb, cmd);
+ break;
+#ifdef T10_04_136
+ case VERIFY:
+ ret = asd_sata_verify_build(asd, dev, scb, cmd);
+ break;
+#endif
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ ret = asd_sata_write_build(asd, dev, scb, cmd);
+ break;
+#ifdef T10_04_136
+ case WRITE_VERIFY:
+ ret = asd_sata_write_verify_build(asd, dev, scb, cmd);
+ break;
+#endif
+ default:
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPERATION, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ if (ret != ASD_COMMAND_BUILD_OK) {
+ return ret;
+ }
+
+ ret = asd_setup_data(asd, scb, cmd);
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+
+ASD_COMMAND_BUILD_STATUS
+asd_build_atapi_scb(
+struct asd_softc *asd,
+struct scb *scb,
+union asd_cmd *acmd
+)
+{
+ ASD_COMMAND_BUILD_STATUS ret;
+ struct asd_atapi_task_hscb *atapi_hscb;
+ struct hd_driveid *hd_driveidp;
+ struct asd_target *target;
+ struct scsi_cmnd *cmd;
+ struct asd_device *dev;
+
+ dev = scb->platform_data->dev;
+
+ cmd = &acmd->scsi_cmd;
+
+ /*
+ * Guard against stale sense data. The Linux mid-layer assumes that
+ * sense was retrieved anytime the first byte of the sense buffer
+ * looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ cmd->resid = 0;
+
+ atapi_hscb = &scb->hscb->atapi_task;
+
+ target = dev->target;
+
+ atapi_hscb->header.opcode = SCB_INITIATE_ATAPI_TASK;
+ atapi_hscb->protocol_conn_rate =
+ PROTOCOL_TYPE_SATA | target->ddb_profile.conn_rate;
+ atapi_hscb->xfer_len = asd_htole32(cmd->request_bufflen);
+
+ atapi_hscb->data_offset = 0;
+ atapi_hscb->sister_scb = 0xffff;
+ atapi_hscb->conn_handle = target->ddb_profile.conn_handle;
+ atapi_hscb->retry_cnt = TASK_RETRY_CNT;
+ atapi_hscb->affiliation_policy = 0;
+
+ hd_driveidp = &dev->target->atapi_cmdset.adp_hd_driveid;
+
+#ifdef TAGGED_QUEUING
+ // RST - add support for SATA II queueing
+ atapi_hscb->ata_flags = LEGACY_QUEUING;
+#else
+ atapi_hscb->ata_flags = UNTAGGED;
+#endif
+
+ memcpy(atapi_hscb->atapi_packet, cmd->cmnd, MAX_COMMAND_SIZE);
+
+ // issue a PACKET command 6.24 of the ATA/ATAPI spec
+ asd_sata_setup_fis((struct asd_ata_task_hscb *)atapi_hscb,
+ WIN_PACKETCMD);
+
+ switch (cmd->sc_data_direction)
+ {
+ case SCSI_DATA_READ:
+ atapi_hscb->ata_flags |= DATA_DIR_INBOUND;
+ break;
+
+ case SCSI_DATA_WRITE:
+ atapi_hscb->ata_flags |= DATA_DIR_OUTBOUND;
+ break;
+
+ case SCSI_DATA_NONE:
+ atapi_hscb->ata_flags |= DATA_DIR_NO_XFER;
+ break;
+
+ case SCSI_DATA_UNKNOWN:
+ atapi_hscb->ata_flags |= DATA_DIR_UNSPECIFIED;
+ break;
+ }
+
+ if ((cmd->sc_data_direction != SCSI_DATA_NONE)) {
+ SET_NO_IO_MODE(atapi_hscb);
+ }
+ else if (target->atapi_cmdset.features_state & SATA_USES_DMA) {
+ ASD_H2D_FIS(atapi_hscb)->features = PACKET_DMA_BIT;
+
+ if (hd_driveidp->dma_1word & DMADIR_BIT_NEEDED) {
+ if (cmd->sc_data_direction == SCSI_DATA_READ) {
+ ASD_H2D_FIS(atapi_hscb)->features |=
+ DMADIR_BIT_DEV_TO_HOST;
+ } else {
+ ASD_H2D_FIS(atapi_hscb)->features &=
+ ~DMADIR_BIT_DEV_TO_HOST;
+ }
+ }
+
+ SET_DMA_MODE(atapi_hscb);
+ } else {
+ SET_PIO_MODE(atapi_hscb);
+ }
+
+ ASD_H2D_FIS(atapi_hscb)->byte_count_lo = cmd->request_bufflen & 0xff;
+ ASD_H2D_FIS(atapi_hscb)->byte_count_hi =
+ (cmd->request_bufflen >> 8) & 0xff;
+
+ asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_linux_scb_done);
+ asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_atapi_post);
+
+ ret = asd_setup_data(asd, scb, cmd);
+
+ return ret;
+}
+
+void
+asd_sata_atapi_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_set_check_condition(cmd,
+ (ASD_D2H_FIS(ata_resp_edbp)->error & 0xf0) >> 4, 0, 0);
+
+ 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:
+ /*
+ * The response will be handled by the routine that we are
+ * "popping" to.
+ */
+ 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);
+}
+
+static void
+asd_sata_linux_scb_done(
+struct asd_softc *asd,
+struct scb *scb,
+struct asd_done_list *dl
+)
+{
+ Scsi_Cmnd *cmd;
+ struct asd_device *dev;
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ asd_print("SCB %d done'd twice\n", SCB_GET_INDEX(scb));
+ panic("Stopping for safety");
+ }
+
+ list_del(&scb->owner_links);
+
+ cmd = (struct scsi_cmnd *)scb->io_ctx;
+ dev = scb->platform_data->dev;
+ dev->active--;
+ dev->openings++;
+ if ((scb->flags & SCB_DEV_QFRZN) != 0) {
+ scb->flags &= ~SCB_DEV_QFRZN;
+ dev->qfrozen--;
+ }
+
+ asd_unmap_scb(asd, scb);
+
+ if (dl != NULL) {
+ switch (dl->opcode) {
+ case TASK_COMP_W_UNDERRUN:
+ cmd->resid = asd_le32toh(dl->stat_blk.data.res_len);
+ asd_cmd_set_host_status(cmd, DID_OK);
+ break;
+
+ case TASK_ABORTED_ON_REQUEST:
+ asd_cmd_set_host_status(cmd, DID_ABORT);
+ break;
+
+ case TASK_CLEARED:
+ {
+ struct task_cleared_sb *task_clr;
+
+ task_clr = &dl->stat_blk.task_cleared;
+
+ asd_log(ASD_DBG_ERROR," Task Cleared for Tag: 0x%x, "
+ "TC: 0x%x.\n",
+ task_clr->tag_of_cleared_task,
+ SCB_GET_INDEX(scb));
+
+ /*
+ * Pending command at the firmware's queues aborted
+ * upon request. If the device is offline then failed
+ * the IO.
+ * Otherwise, have the command retried again.
+ */
+ if (task_clr->clr_nxs_ctx == ASD_TARG_HOT_REMOVED) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+ asd_cmd_set_host_status(cmd, DID_NO_CONNECT);
+#else
+ asd_cmd_set_offline_status(cmd);
+#endif
+ } else
+ asd_cmd_set_host_status(cmd, DID_SOFT_ERROR);
+
+ break;
+ }
+
+ case TASK_INT_W_BRK_RCVD:
+ asd_log(ASD_DBG_ERROR,
+ "TASK INT. WITH BREAK RECEIVED.\n");
+ asd_cmd_set_host_status(cmd, DID_SOFT_ERROR);
+ break;
+
+ case TASK_ABORTED_BY_ITNL_EXP:
+ {
+ struct itnl_exp_sb *itnl_exp;
+
+ itnl_exp = &dl->stat_blk.itnl_exp;
+
+ asd_log(ASD_DBG_ERROR,
+ "ITNL EXP for SCB 0x%x Reason = 0x%x.\n",
+ SCB_GET_INDEX(scb), itnl_exp->reason);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+ asd_cmd_set_host_status(cmd, DID_NO_CONNECT);
+#else
+ asd_cmd_set_offline_status(cmd);
+#endif
+ break;
+ }
+
+ default:
+ asd_cmd_set_host_status(cmd, DID_SOFT_ERROR);
+ break;
+ }
+ }
+
+ if ((dev->target->flags & ASD_TARG_HOT_REMOVED) != 0) {
+ /*
+ * If the target had been removed and all active IOs on
+ * the device have been completed, schedule the device to
+ * be destroyed.
+ */
+ if (list_empty(&dev->busyq) && (dev->active == 0) &&
+ ((dev->flags & ASD_DEV_DESTROY_WAS_ACTIVE) != 0)) {
+ /*
+ * Schedule a deferred process task to destroy
+ * the device.
+ */
+ asd_setup_dev_dpc_task(dev, asd_destroy_device);
+ }
+ } else {
+ if ((dev->flags & ASD_DEV_ON_RUN_LIST) == 0) {
+ list_add_tail(&dev->links,
+ &asd->platform_data->device_runq);
+ dev->flags |= ASD_DEV_ON_RUN_LIST;
+ }
+ }
+
+ /*
+ * Only free the scb if it hasn't timedout.
+ * For SCB that has timedout, error recovery has invoked and
+ * the timedout SCB will be freed in the error recovery path.
+ */
+ if ((scb->flags & SCB_TIMEDOUT) == 0)
+ asd_hwi_free_scb(asd, scb);
+
+ cmd->scsi_done(cmd);
+}
+
+/* -------------------------------------------------------------------------- */
+
+INLINE
+void
+asd_sata_setup_fis(
+struct asd_ata_task_hscb *ata_hscb,
+uint8_t command
+)
+{
+ memset(&ASD_H2D_FIS(ata_hscb)->features, 0,
+ FIS_LENGTH - FIS_OFFSET(features));
+
+ ASD_H2D_FIS(ata_hscb)->fis_type = FIS_HOST_TO_DEVICE;
+ ASD_H2D_FIS(ata_hscb)->cmd_devcontrol = FIS_COMMAND;
+ ASD_H2D_FIS(ata_hscb)->command = command;
+}
+
+void
+asd_sata_compute_support(
+struct asd_softc *asd,
+struct asd_target *target
+)
+{
+ struct hd_driveid *hd_driveidp;
+ unsigned *features_state;
+ unsigned *features_enabled;
+ unsigned dma_mode_bits;
+ unsigned *dma_mode_level;
+
+ 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;
+ features_enabled = &target->ata_cmdset.features_enabled;
+ dma_mode_level = &target->ata_cmdset.dma_mode_level;
+ break;
+
+ case ASD_COMMAND_SET_ATAPI:
+ hd_driveidp = &target->atapi_cmdset.adp_hd_driveid;
+ features_state = &target->atapi_cmdset.features_state;
+ features_enabled = &target->atapi_cmdset.features_enabled;
+ dma_mode_level = &target->atapi_cmdset.dma_mode_level;
+ break;
+
+ default:
+ return;
+ }
+
+ *features_state = (hd_driveidp->cfs_enable_2 & IDENT_48BIT_SUPPORT) ?
+ SATA_USES_48BIT : 0;
+
+#ifdef TAGGED_QUEUING
+ *features_state |= (hd_driveidp->cfs_enable_2 & IDENT_QUEUED_SUPPORT) ?
+ SATA_USES_QUEUEING : 0;
+#endif
+
+ *features_state |= (hd_driveidp->capability & IDENT_DMA_SUPPORT) ?
+ SATA_USES_DMA : 0;
+
+ *features_state |= (hd_driveidp->cfsse & IDENT_WRITE_FUA_SUPPORT) ?
+ SATA_USES_WRITE_FUA : 0;
+
+ *features_state |= (hd_driveidp->config & ATA_REMOVABLE) ?
+ SATA_USES_REMOVABLE : 0;
+
+ if ((hd_driveidp->command_set_1 & ATA_WRITE_BUFFER_CAPABLE) &&
+ (hd_driveidp->cfs_enable_1 & ATA_WRITE_BUFFER_ENABLED)) {
+ *features_state |= SATA_USES_WRITE_BUFFER;
+ }
+
+ if ((hd_driveidp->command_set_1 & ATA_READ_BUFFER_CAPABLE) &&
+ (hd_driveidp->cfs_enable_1 & ATA_READ_BUFFER_ENABLED)) {
+ *features_state |= SATA_USES_READ_BUFFER;
+ }
+
+ if ((hd_driveidp->command_set_1 & ATA_SMART_CAPABLE) &&
+ (hd_driveidp->cfs_enable_1 & ATA_SMART_ENABLED)) {
+ *features_state |= SATA_USES_SMART;
+ }
+
+ if ((hd_driveidp->command_set_1 & ATA_WRITE_CACHE_CAPABLE) &&
+ (hd_driveidp->cfs_enable_1 & ATA_WRITE_CACHE_ENABLED)) {
+ *features_state |= SATA_USES_WRITE_CACHE;
+ }
+
+ if ((hd_driveidp->command_set_1 & ATA_LOOK_AHEAD_CAPABLE) &&
+ (hd_driveidp->cfs_enable_1 & ATA_LOOK_AHEAD_ENABLED)) {
+ *features_state |= SATA_USES_READ_AHEAD;
+ }
+
+ *features_enabled = 0;
+
+ /*
+ * features_enabled represents the desired state of the feature.
+ */
+ *features_enabled =
+ (hd_driveidp->command_set_1 & ATA_WRITE_CACHE_CAPABLE) ?
+ WRITE_CACHE_FEATURE_ENABLED : 0;
+
+ *features_enabled |=
+ (hd_driveidp->command_set_1 & ATA_LOOK_AHEAD_CAPABLE) ?
+ READ_AHEAD_FEATURE_ENABLED : 0;
+
+ *features_enabled |=
+ (hd_driveidp->command_set_1 & ATA_SMART_ENABLED) ?
+ SMART_FEATURE_ENABLED : 0;
+
+ *dma_mode_level = 0;
+
+ if (hd_driveidp->field_valid & ATA_DMA_ULTRA_VALID) {
+
+ dma_mode_bits = hd_driveidp->dma_ultra & DMA_ULTRA_MODE_MASK;
+
+ if (dma_mode_bits != 0) {
+ *features_enabled |= SATA_USES_UDMA;
+ *features_state |= NEEDS_XFER_SETFEATURES;
+
+ dma_mode_bits = dma_mode_bits >> 1;
+
+ while (dma_mode_bits != 0) {
+ dma_mode_bits = dma_mode_bits >> 1;
+ *dma_mode_level = *dma_mode_level + 1;
+ }
+
+ *dma_mode_level |= UDMA_XFER_MODE;
+ }
+ }
+
+#if 0
+ ... next check byte (field_valid) 53 to see if words 70:64 are valid
+ ... word 64 - eide_pio_modes
+#endif
+}
+
+INLINE
+void
+asd_sata_setup_lba_ext(
+struct asd_ata_task_hscb *ata_hscb,
+uint32_t lba,
+unsigned sectors
+)
+{
+ ASD_H2D_FIS(ata_hscb)->lba0 = lba & 0xff;
+ ASD_H2D_FIS(ata_hscb)->lba1 = (lba >> 8) & 0xff;
+ ASD_H2D_FIS(ata_hscb)->lba2 = (lba >> 16) & 0xff;
+ ASD_H2D_FIS(ata_hscb)->lba3 = (lba >> 24);
+
+ ASD_H2D_FIS(ata_hscb)->sector_count = sectors & 0xff;
+ ASD_H2D_FIS(ata_hscb)->sector_count_exp = sectors >> 8;
+
+ /*
+ * Section 3.1.39 of ATA-ATAPI-7 says:
+ * In a serial implementation, the device ignores the DEV bit.
+ */
+ ASD_H2D_FIS(ata_hscb)->dev_head = LBA_MODE;
+}
+
+INLINE
+void
+asd_sata_setup_lba(
+struct asd_ata_task_hscb *ata_hscb,
+uint32_t lba,
+unsigned sectors
+)
+{
+ ASD_H2D_FIS(ata_hscb)->lba0 = lba & 0xff;
+ ASD_H2D_FIS(ata_hscb)->lba1 = (lba >> 8) & 0xff;
+ ASD_H2D_FIS(ata_hscb)->lba2 = (lba >> 16) & 0xff;
+
+ ASD_H2D_FIS(ata_hscb)->sector_count = sectors & 0xff;
+ ASD_H2D_FIS(ata_hscb)->dev_head = LBA_MODE | ((lba >> 24) & 0x0f);
+}
+
+void
+asd_sata_set_check_condition(
+struct scsi_cmnd *cmd,
+unsigned sense_key,
+unsigned additional_sense,
+unsigned additional_sense_qualifier
+)
+{
+ u_char sense_data[SENSE_DATA_SIZE];
+
+ memset(sense_data, 0, SENSE_DATA_SIZE);
+
+ asd_cmd_set_driver_status(cmd, DRIVER_SENSE);
+ asd_cmd_set_host_status(cmd, DID_OK);
+ asd_cmd_set_scsi_status(cmd, CHECK_CONDITION << 1);
+
+ sense_data[0] = 0x70;
+ sense_data[2] = sense_key;
+ sense_data[7] = SENSE_DATA_SIZE - 7;
+
+ sense_data[12] = additional_sense;
+ sense_data[13] = additional_sense_qualifier;
+
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer, sense_data, sizeof(cmd->sense_buffer));
+
+ // RST - we need to save away the sense data
+}
+
+void
+asd_sata_check_registers(
+struct ata_resp_edb *ata_resp_edbp,
+struct scb *scb,
+struct scsi_cmnd *cmd
+)
+{
+ /*
+ * Check the interface state registers.
+ */
+#if 0
+ if ((ata_resp_edbp->sstatus & SSTATUS_IPM_DET_MASK) !=
+ (SSTATUS_DET_COM_ESTABLISHED | SSTATUS_IPM_ACTIVE)) {
+ printk("sstatus = 0x%x\n", ata_resp_edbp->sstatus);
+
+ asd_sata_set_check_condition(cmd, NOT_READY,
+ LOGICAL_UNIT_ASC, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ if ((ata_resp_edbp->sstatus & SSTATUS_SPD_MASK) == 0) {
+ printk("sstatus = 0x%x\n", ata_resp_edbp->sstatus);
+
+ asd_sata_set_check_condition(cmd, NOT_READY,
+ LOGICAL_UNIT_ASC, 0);
+
+ return;
+ }
+#endif
+
+ if (ata_resp_edbp->serror != 0) {
+ asd_print("serror = 0x%x\n", ata_resp_edbp->serror);
+
+ // RST - make this better
+ asd_sata_set_check_condition(cmd, NOT_READY,
+ LOGICAL_UNIT_ASC, 0);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->status) & (ERR_STAT | WRERR_STAT)) {
+ asd_print("status = 0x%x\n",
+ ASD_D2H_FIS(ata_resp_edbp)->status);
+
+ asd_sata_completion_status(cmd, ata_resp_edbp);
+
+ return;
+ }
+
+ return;
+}
+
+void
+asd_sata_completion_status(
+struct scsi_cmnd *cmd,
+struct ata_resp_edb *ata_resp_edbp
+)
+{
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & NM_ERR) {
+ /*
+ * no medium
+ */
+ asd_sata_set_check_condition(cmd, MEDIUM_ERROR,
+ MEDIUM_NOT_PRESENT, 0);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & ABRT_ERR) {
+ /*
+ * command aborted
+ */
+ asd_sata_set_check_condition(cmd, ABORTED_COMMAND,
+ IO_PROCESS_TERMINATED_ASC, IO_PROCESS_TERMINATED_ASCQ);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & MCR_ERR) {
+ /*
+ * medium change request
+ */
+ asd_sata_set_check_condition(cmd, UNIT_ATTENTION,
+ OPERATOR_ASC, MEDIUM_REMOVAL_REQUEST);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & ID_ERR) {
+ /*
+ * address out of bounds
+ */
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & MC_ERR) {
+ /*
+ * medium changed
+ */
+ asd_sata_set_check_condition(cmd, UNIT_ATTENTION,
+ MEDIUM_CHANGED, 0);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & UNC_ERR) {
+ /*
+ * uncorrectable ECC error
+ */
+ asd_sata_set_check_condition(cmd, MEDIUM_ERROR,
+ LOGICAL_UNIT_ASC, 0);
+
+ return;
+ }
+
+ if ((ASD_D2H_FIS(ata_resp_edbp)->error) & ICRC_ERR) {
+ /*
+ * CRC error
+ */
+ asd_sata_set_check_condition(cmd, MEDIUM_ERROR,
+ LOGICAL_UNIT_ASC, COMMUNICATION_CRC_ERROR);
+
+ return;
+ }
+
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPERATION, 0);
+
+ return;
+}
+
+INLINE
+struct ata_resp_edb *
+asd_sata_get_edb(
+struct asd_softc *asd,
+struct asd_done_list *done_listp
+)
+{
+ struct response_sb *responsep;
+ unsigned buflen;
+ struct scb *escb;
+ u_int escb_index;
+ u_int edb_index;
+ struct ata_resp_edb *ata_resp_edbp;
+ union edb *edbp;
+
+ responsep = &done_listp->stat_blk.response;
+
+ buflen = (responsep->empty_buf_elem & EDB_ELEM_MASK) << 4 |
+ responsep->empty_buf_len;
+
+ if (buflen == sizeof(struct ata_resp_edb)) {
+ asd_print("buflen is %d - should be %u\n",
+ buflen, (unsigned)sizeof(struct ata_resp_edb));
+ }
+
+ escb_index = asd_le16toh(responsep->empty_scb_tc);
+
+ edb_index = RSP_EDB_ELEM(responsep) - 1;
+
+ edbp = asd_hwi_indexes_to_edb(asd, &escb, escb_index, edb_index);
+
+ ata_resp_edbp = &edbp->ata_resp;
+
+ return ata_resp_edbp;
+}
+
+/*
+ * This routine will only get called when a command that was issued by the
+ * state machine has completed.
+ */
+void
+asd_sata_mode_select_wakeup_state_machine(
+struct state_machine_context *sm_contextp
+)
+{
+ DISCOVER_RESULTS results;
+ struct asd_softc *asd;
+ struct scb *scb;
+ struct asd_device *dev;
+ struct asd_target *target;
+ struct scsi_cmnd *cmd;
+
+ results = asd_run_state_machine(sm_contextp);
+
+ if (results == DISCOVER_OK) {
+ /*
+ * The state machine is still running. We will get called
+ * again.
+ */
+ return;
+ }
+
+ scb = (struct scb *)sm_contextp->state_handle;
+
+ dev = scb->platform_data->dev;
+
+ target = dev->target;
+
+ asd = (struct asd_softc *)target->softc;
+
+ cmd = (struct scsi_cmnd *)scb->io_ctx;
+
+ if (results == DISCOVER_FINISHED) {
+ asd_cmd_set_host_status(cmd, DID_OK);
+ } else {
+ asd_cmd_set_host_status(cmd, DID_ERROR);
+ }
+
+ asd_pop_post_stack(asd, scb, NULL);
+}
+
+
+int
+asd_sata_identify_build(
+struct asd_softc *asd,
+struct asd_target *target,
+struct scb *scb
+)
+{
+ struct asd_ata_task_hscb *ata_hscb;
+ int dir;
+ int error;
+ dma_addr_t addr;
+#if 0
+ unsigned i;
+#endif
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ ata_hscb->header.opcode = SCB_INITIATE_ATA_TASK;
+ ata_hscb->protocol_conn_rate =
+ PROTOCOL_TYPE_SATA | target->ddb_profile.conn_rate;
+ ata_hscb->xfer_len = asd_htole32(sizeof(struct hd_driveid));
+
+ switch (target->command_set_type) {
+ case ASD_COMMAND_SET_ATAPI:
+ //printk("ATAPI!\n");
+ asd_sata_setup_fis(ata_hscb, WIN_PIDENTIFY);
+ break;
+ case ASD_COMMAND_SET_ATA:
+ //printk("ATA!\n");
+ asd_sata_setup_fis(ata_hscb, WIN_IDENTIFY);
+ break;
+ default:
+ //printk("command_set_type = 0x%x\n", target->command_set_type);
+ return 1;
+ }
+
+ ata_hscb->data_offset = 0;
+ ata_hscb->sister_scb = 0xffff;
+ ata_hscb->conn_handle = target->ddb_profile.conn_handle;
+ ata_hscb->retry_cnt = TASK_RETRY_CNT;
+
+ ata_hscb->ata_flags = UNTAGGED | DATA_DIR_INBOUND;
+ SET_PIO_MODE(ata_hscb);
+ ata_hscb->affiliation_policy = 0;
+
+ dir = scsi_to_pci_dma_dir(SCSI_DATA_READ);
+
+ addr = asd_map_single(asd,
+ &target->ata_cmdset.adp_hd_driveid,
+ sizeof(struct hd_driveid), dir);
+
+ scb->platform_data->buf_busaddr = addr;
+
+ error = asd_sg_setup(scb->sg_list, addr, sizeof(struct hd_driveid),
+ 1);
+
+ if (error != 0) {
+ asd_unmap_single(asd, addr, sizeof(struct hd_driveid), dir);
+
+ return 1;
+ }
+
+ scb->sg_count = 1;
+
+ memcpy(ata_hscb->sg_elements, scb->sg_list, sizeof(struct sg_element));
+
+ asd_push_post_stack(asd, scb, (void *)target, asd_sata_identify_post);
+
+#if 0
+ for (i = 0 ; i < 0x80 ; i++) {
+ printk("%02x ", *(((unsigned char *)ata_hscb) + i));
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ if (((i + 1) % 16) == 0) {
+ printk("\n");
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/* shamelessly stolen from ide-iops.c */
+static void
+asd_sata_ide_fixstring(
+u8 *s,
+const int bytecount,
+const int byteswap
+)
+{
+ u8 *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+ if (byteswap) {
+ /* convert from big-endian to host byte order */
+ for (p = end ; p != s;) {
+ unsigned short *pp = (unsigned short *) (p -= 2);
+ *pp = ntohs(*pp);
+ }
+ }
+ /* strip leading blanks */
+ while (s != end && *s == ' ')
+ ++s;
+ /* compress internal blanks and strip trailing blanks */
+ while (s != end && *s) {
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ }
+ /* wipe out trailing garbage */
+ while (p != end)
+ *p++ = '\0';
+}
+
+void
+asd_sata_identify_post(
+struct asd_softc *asd,
+struct scb *scb,
+struct asd_done_list *done_listp
+)
+{
+ struct ata_resp_edb *ata_resp_edbp;
+ // RST - declare this on the stack for now
+ struct scsi_cmnd cmd;
+ struct asd_target *target;
+
+ target = (struct asd_target *)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:
+ break;
+
+ default:
+ asd_pop_post_stack(asd, scb, done_listp);
+ return;
+ }
+
+ asd_sata_ide_fixstring((u8 *)&target->ata_cmdset.adp_hd_driveid.model,
+ sizeof(target->ata_cmdset.adp_hd_driveid.model), 1);
+
+ asd_sata_ide_fixstring((u8 *)&target->ata_cmdset.adp_hd_driveid.fw_rev,
+ sizeof(target->ata_cmdset.adp_hd_driveid.fw_rev), 1);
+
+ asd_sata_ide_fixstring((u8 *)&target->ata_cmdset.adp_hd_driveid.serial_no,
+ sizeof(target->ata_cmdset.adp_hd_driveid.serial_no), 1);
+
+ asd_pop_post_stack(asd, scb, done_listp);
+
+ up(&asd->platform_data->discovery_sem);
+}
+
+int
+asd_sata_set_features_build(
+struct asd_softc *asd,
+struct asd_target *target,
+struct scb *scb,
+uint8_t feature,
+uint8_t sector_count
+)
+{
+ struct asd_ata_task_hscb *ata_hscb;
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ ata_hscb->header.opcode = SCB_INITIATE_ATA_TASK;
+ ata_hscb->protocol_conn_rate =
+ PROTOCOL_TYPE_SATA | target->ddb_profile.conn_rate;
+ ata_hscb->xfer_len = asd_htole32(sizeof(struct hd_driveid));
+
+ asd_sata_setup_fis(ata_hscb, WIN_SETFEATURES);
+
+ ASD_H2D_FIS(ata_hscb)->features = feature;
+ ASD_H2D_FIS(ata_hscb)->sector_count = sector_count;
+
+ ata_hscb->data_offset = 0;
+ ata_hscb->sister_scb = 0xffff;
+ ata_hscb->conn_handle = target->ddb_profile.conn_handle;
+ ata_hscb->retry_cnt = TASK_RETRY_CNT;
+ ata_hscb->affiliation_policy = 0;
+
+ ata_hscb->ata_flags = UNTAGGED | DATA_DIR_NO_XFER;
+ SET_NO_IO_MODE(ata_hscb);
+
+ scb->sg_count = 0;
+
+ memcpy(ata_hscb->sg_elements, scb->sg_list, sizeof(struct sg_element));
+
+ asd_push_post_stack(asd, scb, (void *)target, asd_sata_identify_post);
+
+ return 0;
+}
+
+void
+asd_sata_configure_post(
+struct asd_softc *asd,
+struct scb *scb,
+struct asd_done_list *done_listp
+)
+{
+ struct ata_resp_edb *ata_resp_edbp;
+ // RST - declare this on the stack for now
+ struct scsi_cmnd cmd;
+ struct asd_target *target;
+
+ target = (struct asd_target *)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:
+ break;
+
+ default:
+ asd_pop_post_stack(asd, scb, done_listp);
+ return;
+ }
+
+ asd_sata_ide_fixstring((u8 *)&target->ata_cmdset.adp_hd_driveid.model,
+ sizeof(target->ata_cmdset.adp_hd_driveid.model), 1);
+
+ asd_sata_ide_fixstring((u8 *)&target->ata_cmdset.adp_hd_driveid.fw_rev,
+ sizeof(target->ata_cmdset.adp_hd_driveid.fw_rev), 1);
+
+ asd_sata_ide_fixstring((u8 *)&target->ata_cmdset.adp_hd_driveid.serial_no,
+ sizeof(target->ata_cmdset.adp_hd_driveid.serial_no), 1);
+
+ asd_pop_post_stack(asd, scb, done_listp);
+
+ up(&asd->platform_data->discovery_sem);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* -----------------------------------
+ * FORMAT_UNIT: (emulated)
+ *
+ */
+ASD_COMMAND_BUILD_STATUS
+asd_sata_format_unit_build(
+struct asd_softc *asd,
+struct asd_device *dev,
+struct scb *scb,
+struct scsi_cmnd *cmd
+)
+{
+ struct asd_ata_task_hscb *ata_hscb;
+ struct scsi_cmnd *write_cmd;
+ int dir;
+ int error;
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ write_cmd = asd_alloc_mem(sizeof(struct scsi_cmnd), GFP_KERNEL);
+
+ if (write_cmd == NULL) {
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ asd_sata_set_check_condition(cmd, HARDWARE_ERROR,
+ RESOURCE_FAILURE, MEMORY_OUT_OF_SPACE);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ /*
+ * Since this is an internal command, we can initialize these fields
+ * to whatever we want.
+ */
+ write_cmd->host_scribble = (unsigned char *)cmd;
+ write_cmd->sc_request = NULL;
+ write_cmd->request_buffer = NULL;
+
+ if (asd_sata_write_build(asd, dev, scb, write_cmd) !=
+ ASD_COMMAND_BUILD_OK) {
+
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ asd_sata_set_check_condition(cmd, HARDWARE_ERROR,
+ FORMAT_FAILED, FORMAT_COMMAND_FAILED);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ /*
+ * We have to do this after sata_write_build, because it will initialize
+ * scb->post also.
+ */
+ asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_format_unit_post);
+
+ write_cmd->use_sg = 0;
+
+ write_cmd->request_bufflen = FORMAT_WRITE_BUFFER_LEN;
+
+ write_cmd->sc_request = (Scsi_Request *)
+ asd_alloc_mem(sizeof(struct map_node), GFP_KERNEL);
+
+ if (write_cmd->sc_request == NULL) {
+
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ asd_sata_set_check_condition(cmd, HARDWARE_ERROR,
+ RESOURCE_FAILURE, MEMORY_OUT_OF_SPACE);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ if (asd_alloc_dma_mem(asd, FORMAT_WRITE_BUFFER_LEN,
+ &write_cmd->request_buffer,
+ &scb->platform_data->buf_busaddr,
+ (bus_dma_tag_t *)&write_cmd->device,
+ (struct map_node *)write_cmd->sc_request) != 0) {
+
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ asd_sata_set_check_condition(cmd, HARDWARE_ERROR,
+ RESOURCE_FAILURE, MEMORY_OUT_OF_SPACE);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ memset(write_cmd->request_buffer, 0, FORMAT_WRITE_BUFFER_LEN);
+
+ write_cmd->sc_data_direction = SCSI_DATA_WRITE;
+
+ scb->sg_count = 1;
+
+ dir = scsi_to_pci_dma_dir(write_cmd->sc_data_direction);
+
+ error = asd_sg_setup(scb->sg_list, scb->platform_data->buf_busaddr,
+ FORMAT_WRITE_BUFFER_LEN, 1);
+
+ if (error != 0) {
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ asd_sata_set_check_condition(cmd, HARDWARE_ERROR,
+ RESOURCE_FAILURE, MEMORY_OUT_OF_SPACE);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ memcpy(ata_hscb->sg_elements, scb->sg_list,
+ scb->sg_count * sizeof(struct sg_element));
+
+ ata_hscb->ata_flags |= DATA_DIR_OUTBOUND;
+
+ write_cmd->cmnd[0] = WRITE_10;
+ write_cmd->cmnd[1] = 0;
+
+ // lba
+ write_cmd->cmnd[2] = 0;
+ write_cmd->cmnd[3] = 0;
+ write_cmd->cmnd[4] = 0;
+ write_cmd->cmnd[5] = 0;
+
+ // length
+ write_cmd->cmnd[7] = (FORMAT_SECTORS >> 8) & 0xff;
+ write_cmd->cmnd[8] = FORMAT_SECTORS & 0xff;
+ write_cmd->cmnd[9] = 0;
+
+ return ASD_COMMAND_BUILD_FINISHED;
+}
+
+void
+asd_sata_format_unit_free_memory(
+struct asd_softc *asd,
+struct scsi_cmnd *write_cmd
+)
+{
+ if (write_cmd == NULL) {
+ return;
+ }
+
+ if (write_cmd->sc_request != NULL) {
+ asd_free_mem(write_cmd->sc_request);
+ }
+
+ if (write_cmd->request_buffer != NULL) {
+ asd_free_dma_mem(asd, (bus_dma_tag_t)write_cmd->device,
+ (struct map_node *)write_cmd->sc_request);
+ }
+
+ if (write_cmd->sc_request != NULL) {
+ asd_free_mem(write_cmd->sc_request);
+ }
+
+ asd_free_mem(write_cmd);
+}
+
+void
+asd_sata_format_unit_post(
+struct asd_softc *asd,
+struct scb *scb,
+struct asd_done_list *done_listp
+)
+{
+ struct ata_resp_edb *ata_resp_edbp;
+ struct scsi_cmnd *write_cmd;
+ struct asd_device *dev;
+ uint32_t lba;
+ struct hd_driveid *hd_driveidp;
+
+ write_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, write_cmd);
+
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ scb->io_ctx = (asd_io_ctx_t)write_cmd->host_scribble;
+ asd_pop_post_stack(asd, scb, done_listp);
+ return;
+
+ case TASK_COMP_WO_ERR:
+ break;
+
+ default:
+ asd_sata_set_check_condition(write_cmd, ILLEGAL_REQUEST,
+ FORMAT_FAILED, FORMAT_COMMAND_FAILED);
+
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+
+ scb->io_ctx = (asd_io_ctx_t)write_cmd->host_scribble;
+ asd_pop_post_stack(asd, scb, done_listp);
+ return;
+ }
+
+ dev = scb->platform_data->dev;
+
+ hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+ lba = (write_cmd->cmnd[2] << 24) | (write_cmd->cmnd[3] << 16) |
+ (write_cmd->cmnd[4] << 8) | write_cmd->cmnd[5];
+
+ if (lba == (hd_driveidp->lba_capacity_2 - 1)) {
+ /*
+ * We have finished formatting the drive.
+ */
+ asd_cmd_set_host_status(write_cmd, DID_OK);
+
+ asd_sata_format_unit_free_memory(asd, write_cmd);
+ scb->io_ctx = (asd_io_ctx_t)write_cmd->host_scribble;
+ asd_pop_post_stack(asd, scb, done_listp);
+
+ return;
+ }
+
+ lba = lba + FORMAT_SECTORS;
+
+ // lba
+ write_cmd->cmnd[2] = (lba >> 24) & 0xff;
+ write_cmd->cmnd[3] = (lba >> 16) & 0xff;
+ write_cmd->cmnd[4] = (lba >> 8) & 0xff;
+ write_cmd->cmnd[5] = lba & 0xff;
+
+ list_add_tail(&((union asd_cmd *)write_cmd)->acmd_links, &dev->busyq);
+
+ if ((dev->flags & ASD_DEV_ON_RUN_LIST) == 0) {
+
+ list_add_tail(&dev->links, &asd->platform_data->device_runq);
+
+ dev->flags |= ASD_DEV_ON_RUN_LIST;
+
+ /*
+ * We don't need to run the device queues at this point because
+ * asd_isr (which ulimately called this routine) will call
+ * asd_next_device_to_run() / asd_schedule_runq() which
+ * will schedule the runq tasklet.
+ */
+ }
+}
+
+/* -----------------------------------
+ * INQUIRY: (emulated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_inquiry_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 inquiry_data[INQUIRY_RESPONSE_SIZE];
+ ASD_COMMAND_BUILD_STATUS ret;
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+ if (cmd->cmnd[1] & CMD_DT) {
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ if (cmd->cmnd[1] & LUN_FIELD) {
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ if (cmd->cmnd[1] & EVPD) {
+ ret = asd_sata_inquiry_evd_build(asd, dev, scb, cmd,
+ inquiry_data);
+
+ return ret;
+ }
+
+ if (cmd->cmnd[2] != 0x00) {
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ // RST -
+ // A good strategy for this might be to issue the
+ // WIN_IDENTIFY & WIN_PIDENTIFY
+ // ... we might want to set affiliation bits in the ata_hscb
+ // ... before we send them out
+
+ memset(inquiry_data, 0, INQUIRY_RESPONSE_SIZE);
+
+ inquiry_data[0] = TYPE_DISK;
+ inquiry_data[1] = (dev->target->ata_cmdset.features_state &
+ SATA_USES_REMOVABLE) ? SCSI_REMOVABLE : 0;
+ inquiry_data[2] = SCSI_3;
+ inquiry_data[3] = SCSI_3_RESPONSE_DATA_FORMAT;
+ inquiry_data[4] = INQUIRY_RESPONSE_SIZE - 4;
+ inquiry_data[5] = 0; // PROTECT | 3PC | ALUA | ACC | SCCS
+
+ inquiry_data[6] = 0; // ADDR16 | MCHNGR | MULTIP | ENCSERV | BQUE
+
+#ifdef TAGGED_QUEUING
+ // LINKED | SUNC | WBUS16
+ inquiry_data[7] = (target->ata_cmdset.features_state & SATA_USES_QUEUEING) ?
+ CMD_QUEUE_BIT : 0;
+#else
+ inquiry_data[7] = 0; // CMD_QUEUE_BIT | LINKED | SUNC | WBUS16
+#endif
+
+ memcpy(&inquiry_data[8], hd_driveidp->model, 8);
+ memcpy(&inquiry_data[16], hd_driveidp->model + 8, 16);
+ memcpy(&inquiry_data[32], hd_driveidp->fw_rev, 4);
+
+ inquiry_data[56] = 0; // IUS | QAS | CLOCKING
+
+ memcpy(cmd->request_buffer, inquiry_data, cmd->request_bufflen);
+
+ asd_cmd_set_host_status(cmd, DID_OK);
+
+ return ASD_COMMAND_BUILD_FINISHED;
+}
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_inquiry_evd_build(
+struct asd_softc *asd,
+struct asd_device *dev,
+struct scb *scb,
+struct scsi_cmnd *cmd,
+u_char *inquiry_data
+)
+{
+ struct hd_driveid *hd_driveidp;
+
+ hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+ memset(inquiry_data, 0, INQUIRY_RESPONSE_SIZE);
+
+ switch (cmd->cmnd[2]) {
+ case SUPPORTED_VPD:
+ inquiry_data[0] = TYPE_DISK;
+ inquiry_data[1] = SUPPORTED_VPD;
+ inquiry_data[3] = 2;
+ inquiry_data[4] = SUPPORTED_VPD;
+ inquiry_data[5] = UNIT_SERIAL_VPD;
+ break;
+
+ case UNIT_SERIAL_VPD:
+ inquiry_data[0] = TYPE_DISK;
+ inquiry_data[1] = UNIT_SERIAL_VPD;
+ inquiry_data[3] = ATA_PRODUCT_SERIAL_LENGTH;
+ memcpy(&inquiry_data[4], hd_driveidp->serial_no,
+ ATA_PRODUCT_SERIAL_LENGTH);
+ break;
+
+ default:
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ memcpy(cmd->request_buffer, inquiry_data, cmd->request_bufflen);
+
+ asd_cmd_set_host_status(cmd, DID_OK);
+
+ return ASD_COMMAND_BUILD_FINISHED;
+}
+
+/* -----------------------------------
+ * LOG_SENSE: (translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_log_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_control;
+ struct hd_driveid *hd_driveidp;
+
+ hd_driveidp = &dev->target->ata_cmdset.adp_hd_driveid;
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ if ((cmd->cmnd[2] & PAGE_CODE_MASK) != SMART_DATA) {
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ if ((hd_driveidp->command_set_1 & ATA_SMART_CAPABLE) == 0) {
+
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPERATION, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ /*
+ * "Smart"
+ */
+ asd_sata_setup_fis(ata_hscb, WIN_SMART);
+
+ ASD_H2D_FIS(ata_hscb)->features = SMART_READ_DATA;
+ ASD_H2D_FIS(ata_hscb)->lba1 = 0x4f;
+ ASD_H2D_FIS(ata_hscb)->lba2 = 0xc2;
+
+ page_control = cmd->cmnd[2] & ~PAGE_CODE_MASK;
+
+ switch (page_control) {
+ case PAGE_CONTROL_CUMULATIVE:
+ case PAGE_CONTROL_CURRENT:
+ break;
+ default:
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ ata_hscb->ata_flags |= DATA_DIR_INBOUND;
+ SET_PIO_MODE(ata_hscb);
+
+ asd_push_post_stack(asd, scb, (void *)cmd, asd_sata_log_sense_post);
+
+ return ASD_COMMAND_BUILD_OK;
+}
+
+void
+asd_sata_log_sense_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);
+}
+
+/* -----------------------------------
+ * MODE_SELECT: (emulated / translated)
+ * MODE_SELECT_10: (emulated / translated)
+ */
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_mode_select_build(
+struct asd_softc *asd,
+struct asd_device *dev,
+struct scb *scb,
+struct scsi_cmnd *cmd
+)
+{
+ struct asd_ata_task_hscb *ata_hscb;
+ unsigned parameter_list_length;
+ unsigned len;
+ unsigned page_len;
+ uint8_t *bufptr;
+ unsigned command_translated;
+ unsigned buf_offset;
+ ASD_COMMAND_BUILD_STATUS ret;
+ uint8_t page_code;
+
+ ata_hscb = &scb->hscb->ata_task;
+
+ if ((cmd->cmnd[1] & SP_BIT) || ((cmd->cmnd[1] & PF_BIT) != 0)) {
+ asd_sata_set_check_condition(cmd, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+ }
+
+ command_translated = 0;
+ parameter_list_length = 0;
+ page_len = 0;
+
+ switch (cmd->cmnd[0]) {
+ case MODE_SELECT:
+ parameter_list_length = cmd->cmnd[4];
+ break;
+ case MODE_SELECT_10:
+ parameter_list_length = asd_be32toh(
+ *((uint16_t *)&cmd->cmnd[7]));
+ break;
+ }
+
+ len = MIN(parameter_list_length, cmd->request_bufflen);
+
+ for (buf_offset = 0 ; buf_offset < len ;
+ buf_offset = buf_offset + page_len ) {
+
+ bufptr = cmd->request_buffer + buf_offset;
+
+ page_code = *bufptr;
+
+ switch (page_code) {
+ case READ_WRITE_ERROR_RECOVERY_MODE_PAGE:
+ page_len = READ_WRITE_ERROR_RECOVERY_MODE_PAGE_LEN;
+ break;
+
+ case CACHING_MODE_PAGE:
+ page_len = CACHING_MODE_PAGE_LEN;
+ break;
+
+ case CONTROL_MODE_PAGE:
+ page_len = CONTROL_MODE_PAGE_LEN;
+ break;
+
+ case INFORMATIONAL_EXCEPTION_CONTROL_PAGE:
+ page_len = INFORMATIONAL_EXCEPTION_CONTROL_PAGE_LEN;
+ break;
+ }
+
+ if ((page_len + buf_offset) > len) {
+ /*
+ * We will fail any Page Select commands that span
+ * over the end of our request.
+ */
+ break;
+ }
+
+
+ switch (page_code) {
+ case READ_WRITE_ERROR_RECOVERY_MODE_PAGE:
+ ret = asd_sata_read_write_error_recovery_mode_select(
+ asd, dev, scb, bufptr);
+ break;
+
+ case CACHING_MODE_PAGE:
+ ret = asd_sata_caching_mode_select(asd, dev, scb,
+ bufptr);
+ break;
+
+ case CONTROL_MODE_PAGE:
+ ret = asd_sata_control_mode_select(asd, dev, scb,
+ bufptr);
+ break;
+
+ case INFORMATIONAL_EXCEPTION_CONTROL_PAGE:
+ ret = asd_sata_informational_exception_control_select(
+ asd, dev, scb, bufptr);
+ break;
+ default:
+ ret = ASD_COMMAND_BUILD_FAILED;
+ break;
+ }
+
+ switch (ret) {
+ case ASD_COMMAND_BUILD_OK:
+ /*
+ * This command is being translated, so we have to
+ * wait for it to finish.
+ */
+ command_translated = 1;
+ break;
+
+ case ASD_COMMAND_BUILD_FINISHED:
+ /*
+ * The command was emulated.
+ */
+ break;
+
+ case ASD_COMMAND_BUILD_FAILED:
+ default:
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+ }
+
+ if (command_translated != 0) {
+ /*
+ * This command is being translated, so we have to
+ * wait for it to finish.
+ */
+ return ASD_COMMAND_BUILD_OK;
+ }
+
+ asd_cmd_set_host_status(cmd, DID_OK);
+
+ return ASD_COMMAND_BUILD_FINISHED;
+}
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_read_write_error_recovery_mode_select(
+struct asd_softc *asd,
+struct asd_device *dev,
+struct scb *scb,
+uint8_t *bufptr
+)
+{
+ if (bufptr[1] != (READ_WRITE_ERROR_RECOVERY_MODE_PAGE_LEN - 2)) {
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ /*
+ * T10/04-136r0: Everything about this mode select is ignored for now.
+ */
+
+ return ASD_COMMAND_BUILD_FINISHED;
+}
+
+
+ASD_COMMAND_BUILD_STATUS
+asd_sata_caching_mode_select(
+struct asd_softc *asd,
+struct asd_device *dev,
+struct scb *scb,
+uint8_t *bufptr
+)
+{
+ struct asd_target *target;
+ unsigned state_changed;
+ unsigned *features_enabled;
+ unsigned features_state;
+ DISCOVER_RESULTS results;
+ struct asd_ConfigureATA_SM_Arguments args;
+ struct state_machine_context *sm_contextp;
+
+ if (bufptr[1] != (CACHING_MODE_PAGE_LEN - 2)) {
+ return ASD_COMMAND_BUILD_FAILED;
+ }
+
+ target = dev->target;
+
+ state_changed = 0;
+
+ 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;
+ }
+
+ if (bufptr[12] & SCSI_DRA) {
+ if ((*features_enabled & READ_AHEAD_FEATURE_ENABLED) == 0) {
+ state_changed = 1;
+ *features_enabled |= READ_AHEAD_FEATURE_ENABLED;
+ }
+ } else {
+ if (*features_enabled & READ_AHEAD_FEATURE_ENABLED) {
+ state_changed = 1;
+ *features_enabled &= ~READ_AHEAD_FEATURE_ENABLED;
+ }
+ }
+
+ if (bufptr[2] & SCSI_WCE) {
+ if ((*features_enabled & WRITE_CACHE_FEATURE_ENABLED) == 0) {
+ state_changed = 1;
+ *features_enabled |= WRITE_CACHE_FEATURE_ENABLED;
+ }
+ } else {
+ if (*features_enabled & WRITE_CACHE_FEATURE_ENABLED) {
+ state_changed = 1;
+ *features_enabled &= ~WRITE_CACHE_FEATURE_ENABLED;
+ }
+ }
+
+ if (state_changed == 0) {
+ /*
+ * We didn't change anything, so we are done.
+ */
+ return ASD_COMMAND_BUILD_FINISHED;
+ }
+
+ sm_contextp = asd_alloc_mem(sizeof(struct state_machine_context),
+ GFP_KERNEL);
+
+ memset(sm_contextp, 0, sizeof(struct state_machine_context));
+
+ sm_contextp->state_handle = (void *)scb;
+
+ sm_contextp->wakeup_state_machine =
+ asd_sata_mode_select_wakeup_state_machine;
+
+ args.target = target;
+
+ results = ASD_PUSH_STATE_MACHINE(sm_contextp,
+ &asd_ConfigureATA_SM, (void *)&args);
+
+ switch (results) {
+ case DISCOVER_FINISHED:
+ return ASD_COMMAND_BUILD_FINISHED;
+ break;
+
+ case DISCOVER_FAILED:
+ default:
+ return ASD_COMMAND_BUILD_FAILED;
+ break;
+
+ case DISCOVER_OK:
+ break;
+ }
+
+ return ASD_COMMAND_BUILD_OK;
+}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2005-02-17 17:37 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:37 [ANNOUNCE] Adaptec SAS/SATA device driver [21/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.