From: Luben Tuikov <luben_tuikov@adaptec.com>
To: SCSI Mailing List <linux-scsi@vger.kernel.org>
Subject: [ANNOUNCE] Adaptec SAS/SATA device driver [21/27]
Date: Thu, 17 Feb 2005 12:37:26 -0500 [thread overview]
Message-ID: <4214D656.1050002@adaptec.com> (raw)
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;
+}
reply other threads:[~2005-02-17 17:37 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4214D656.1050002@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.