#include "scsi.h"
#include <scsi/scsi_host.h>
#include <linux/libata.h>
+#include <asm/uaccess.h>
#include "libata.h"
typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, u8 *scsicmd);
-static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
- struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *));
+static struct ata_device *
+ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev);
/**
return 0;
}
+int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
+{
+ struct ata_port *ap;
+ struct ata_device *dev;
+ int val = -EINVAL, rc = -EINVAL;
+
+ ap = (struct ata_port *) &scsidev->host->hostdata[0];
+ if (!ap)
+ goto out;
+
+ dev = ata_scsi_find_dev(ap, scsidev);
+ if (!dev) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ switch (cmd) {
+ case ATA_IOC_GET_IO32:
+ val = 0;
+ if (copy_to_user(arg, &val, 1))
+ return -EFAULT;
+ return 0;
+
+ case ATA_IOC_SET_IO32:
+ val = (unsigned long) arg;
+ if (val != 0)
+ return -EINVAL;
+ return 0;
+
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+out:
+ return rc;
+}
/**
* ata_scsi_qc_new - acquire new ata_queued_cmd reference
* ata_to_sense_error - convert ATA error to SCSI error
* @qc: Command that we are erroring out
*
- * Converts an ATA error into a SCSI error.
- *
- * Right now, this routine is laughably primitive. We
- * don't even examine what ATA told us, we just look at
- * the command data direction, and return a fatal SCSI
- * sense error based on that.
+ * Converts an ATA error into a SCSI error. While we are at it
+ * we decode and dump the ATA error for the user so that they
+ * have some idea what really happened at the non make-believe
+ * layer.
*
* LOCKING:
* spin_lock_irqsave(host_set lock)
*/
-void ata_to_sense_error(struct ata_queued_cmd *qc)
+void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat)
{
struct scsi_cmnd *cmd = qc->scsicmd;
+ u8 err = 0;
+ unsigned char *sb = cmd->sense_buffer;
+ /* Based on the 3ware driver translation table */
+ static unsigned char sense_table[][4] = {
+ /* BBD|ECC|ID|MAR */
+ {0xd1, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command
+ /* BBD|ECC|ID */
+ {0xd0, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command
+ /* ECC|MC|MARK */
+ {0x61, HARDWARE_ERROR, 0x00, 0x00}, // Device fault Hardware error
+ /* ICRC|ABRT */ /* NB: ICRC & !ABRT is BBD */
+ {0x84, ABORTED_COMMAND, 0x47, 0x00}, // Data CRC error SCSI parity error
+ /* MC|ID|ABRT|TRK0|MARK */
+ {0x37, NOT_READY, 0x04, 0x00}, // Unit offline Not ready
+ /* MCR|MARK */
+ {0x09, NOT_READY, 0x04, 0x00}, // Unrecovered disk error Not ready
+ /* Bad address mark */
+ {0x01, MEDIUM_ERROR, 0x13, 0x00}, // Address mark not found Address mark not found for data field
+ /* TRK0 */
+ {0x02, HARDWARE_ERROR, 0x00, 0x00}, // Track 0 not found Hardware error
+ /* Abort & !ICRC */
+ {0x04, ABORTED_COMMAND, 0x00, 0x00}, // Aborted command Aborted command
+ /* Media change request */
+ {0x08, NOT_READY, 0x04, 0x00}, // Media change request FIXME: faking offline
+ /* SRV */
+ {0x10, ABORTED_COMMAND, 0x14, 0x00}, // ID not found Recorded entity not found
+ /* Media change */
+ {0x08, NOT_READY, 0x04, 0x00}, // Media change FIXME: faking offline
+ /* ECC */
+ {0x40, MEDIUM_ERROR, 0x11, 0x04}, // Uncorrectable ECC error Unrecovered read error
+ /* BBD - block marked bad */
+ {0x80, MEDIUM_ERROR, 0x11, 0x04}, // Block marked bad Medium error, unrecovered read error
+ {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ };
+ static unsigned char stat_table[][4] = {
+ /* Must be first because BUSY means no other bits valid */
+ {0x80, ABORTED_COMMAND, 0x47, 0x00}, // Busy, fake parity for now
+ {0x20, HARDWARE_ERROR, 0x00, 0x00}, // Device fault
+ {0x08, ABORTED_COMMAND, 0x47, 0x00}, // Timed out in xfer, fake parity for now
+ {0x04, RECOVERED_ERROR, 0x11, 0x00}, // Recovered ECC error Medium error, recovered
+ {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ };
+ int i = 0;
cmd->result = SAM_STAT_CHECK_CONDITION;
-
- cmd->sense_buffer[0] = 0x70;
- cmd->sense_buffer[2] = MEDIUM_ERROR;
- cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */
-
+
+ /*
+ * Is this an error we can process/parse
+ */
+
+ if(drv_stat & ATA_ERR)
+ /* Read the err bits */
+ err = ata_chk_err(qc->ap);
+
+ /* Display the ATA level error info */
+
+ printk(KERN_WARNING "ata%u: status=0x%02x { ", qc->ap->id, drv_stat);
+ if(drv_stat & 0x80)
+ {
+ printk("Busy ");
+ err = 0; /* Data is not valid in this case */
+ }
+ else {
+ if(drv_stat & 0x40) printk("DriveReady ");
+ if(drv_stat & 0x20) printk("DeviceFault ");
+ if(drv_stat & 0x10) printk("SeekComplete ");
+ if(drv_stat & 0x08) printk("DataRequest ");
+ if(drv_stat & 0x04) printk("CorrectedError ");
+ if(drv_stat & 0x02) printk("Index ");
+ if(drv_stat & 0x01) printk("Error ");
+ }
+ printk("}\n");
+
+ if(err)
+ {
+ printk(KERN_WARNING "ata%u: error=0x%02x { ", qc->ap->id, err);
+ if(err & 0x04) printk("DriveStatusError ");
+ if(err & 0x80)
+ {
+ if(err & 0x04)
+ printk("BadCRC ");
+ else
+ printk("Sector ");
+ }
+ if(err & 0x40) printk("UncorrectableError ");
+ if(err & 0x10) printk("SectorIdNotFound ");
+ if(err & 0x02) printk("TrackZeroNotFound ");
+ if(err & 0x01) printk("AddrMarkNotFound ");
+ printk("}\n");
+
+ /* Should we dump sector info here too ?? */
+ }
+
+
+ /* Look for err */
+ while(sense_table[i][0] != 0xFF)
+ {
+ /* Look for best matches first */
+ if((sense_table[i][0] & err) == sense_table[i][0])
+ {
+ sb[0] = 0x70;
+ sb[2] = sense_table[i][1];
+ sb[7] = 0x0a;
+ sb[12] = sense_table[i][2];
+ sb[13] = sense_table[i][3];
+ return;
+ }
+ i++;
+ }
+ /* No immediate match */
+ if(err)
+ printk(KERN_DEBUG "ata%u: no sense translation for 0x%02x\n", qc->ap->id, err);
+
+ /* Fall back to interpreting status bits */
+ while(stat_table[i][0] != 0xFF)
+ {
+ if(stat_table[i][0] & drv_stat)
+ {
+ sb[0] = 0x70;
+ sb[2] = stat_table[i][1];
+ sb[7] = 0x0a;
+ sb[12] = stat_table[i][2];
+ sb[13] = stat_table[i][3];
+ return;
+ }
+ i++;
+ }
+ /* No error ?? */
+ printk(KERN_ERR "ata%u: called with no error (%02X)!\n", qc->ap->id, drv_stat);
/* additional-sense-code[-qualifier] */
+
+ sb[0] = 0x70;
+ sb[2] = MEDIUM_ERROR;
+ sb[7] = 0x0A;
if (cmd->sc_data_direction == SCSI_DATA_READ) {
- cmd->sense_buffer[12] = 0x11; /* "unrecovered read error" */
- cmd->sense_buffer[13] = 0x04;
+ sb[12] = 0x11; /* "unrecovered read error" */
+ sb[13] = 0x04;
} else {
- cmd->sense_buffer[12] = 0x0C; /* "write error - */
- cmd->sense_buffer[13] = 0x02; /* auto-reallocation failed" */
+ sb[12] = 0x0C; /* "write error - */
+ sb[13] = 0x02; /* auto-reallocation failed" */
}
}
* 65534 when Jens Axboe's patch for dynamically
* determining max_sectors is merged.
*/
- if (dev->flags & ATA_DFLAG_LBA48) {
+ if ((dev->flags & ATA_DFLAG_LBA48) &&
+ ((dev->flags & ATA_DFLAG_LOCK_SECTORS) == 0)) {
sdev->host->max_sectors = 2048;
blk_queue_max_sectors(sdev->request_queue, 2048);
}
ap = (struct ata_port *) &host->hostdata[0];
ap->ops->eng_timeout(ap);
+ /* TODO: this is per-command; when queueing is supported
+ * this code will either change or move to a more
+ * appropriate place
+ */
+ host->host_failed--;
+
DPRINTK("EXIT\n");
return 0;
}
+/**
+ * ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate (ignored)
+ *
+ * Sets up an ATA taskfile to issue FLUSH CACHE or
+ * FLUSH CACHE EXT.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+
+ tf->flags |= ATA_TFLAG_DEVICE;
+ tf->protocol = ATA_PROT_NODATA;
+
+ if ((tf->flags & ATA_TFLAG_LBA48) &&
+ (ata_id_has_flush_ext(qc->dev->id)))
+ tf->command = ATA_CMD_FLUSH_EXT;
+ else
+ tf->command = ATA_CMD_FLUSH;
+
+ return 0;
+}
+
+/**
+ * ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate
+ *
+ * Converts SCSI VERIFY command to an ATA READ VERIFY command.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;
+ u64 dev_sectors = qc->dev->n_sectors;
+ u64 sect = 0;
+ u32 n_sect = 0;
+
+ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf->protocol = ATA_PROT_NODATA;
+ tf->device |= ATA_LBA;
+
+ if (scsicmd[0] == VERIFY) {
+ sect |= ((u64)scsicmd[2]) << 24;
+ sect |= ((u64)scsicmd[3]) << 16;
+ sect |= ((u64)scsicmd[4]) << 8;
+ sect |= ((u64)scsicmd[5]);
+
+ n_sect |= ((u32)scsicmd[7]) << 8;
+ n_sect |= ((u32)scsicmd[8]);
+ }
+
+ else if (scsicmd[0] == VERIFY_16) {
+ sect |= ((u64)scsicmd[2]) << 56;
+ sect |= ((u64)scsicmd[3]) << 48;
+ sect |= ((u64)scsicmd[4]) << 40;
+ sect |= ((u64)scsicmd[5]) << 32;
+ sect |= ((u64)scsicmd[6]) << 24;
+ sect |= ((u64)scsicmd[7]) << 16;
+ sect |= ((u64)scsicmd[8]) << 8;
+ sect |= ((u64)scsicmd[9]);
+
+ n_sect |= ((u32)scsicmd[10]) << 24;
+ n_sect |= ((u32)scsicmd[11]) << 16;
+ n_sect |= ((u32)scsicmd[12]) << 8;
+ n_sect |= ((u32)scsicmd[13]);
+ }
+
+ else
+ return 1;
+
+ if (!n_sect)
+ return 1;
+ if (sect >= dev_sectors)
+ return 1;
+ if ((sect + n_sect) > dev_sectors)
+ return 1;
+ if (lba48) {
+ if (n_sect > (64 * 1024))
+ return 1;
+ } else {
+ if (n_sect > 256)
+ return 1;
+ }
+
+ if (lba48) {
+ tf->hob_nsect = (n_sect >> 8) & 0xff;
+
+ tf->hob_lbah = (sect >> 40) & 0xff;
+ tf->hob_lbam = (sect >> 32) & 0xff;
+ tf->hob_lbal = (sect >> 24) & 0xff;
+ } else
+ tf->device |= (sect >> 24) & 0xf;
+
+ tf->nsect = n_sect & 0xff;
+
+ tf->hob_lbah = (sect >> 16) & 0xff;
+ tf->hob_lbam = (sect >> 8) & 0xff;
+ tf->hob_lbal = sect & 0xff;
+
+ return 0;
+}
+
/**
* ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one
* @qc: Storage for translated ATA taskfile
unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
- tf->hob_nsect = 0;
- tf->hob_lbal = 0;
- tf->hob_lbam = 0;
- tf->hob_lbah = 0;
tf->protocol = qc->dev->xfer_protocol;
tf->device |= ATA_LBA;
return 1;
}
+static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ)))
+ ata_to_sense_error(qc, drv_stat);
+ else
+ cmd->result = SAM_STAT_GOOD;
+
+ qc->scsidone(cmd);
+
+ return 0;
+}
+
/**
* ata_scsi_translate - Translate then issue SCSI command to ATA device
* @ap: ATA port to which the command is addressed
if (!qc)
return;
+ /* data is present; dma-map it */
if (cmd->sc_data_direction == SCSI_DATA_READ ||
cmd->sc_data_direction == SCSI_DATA_WRITE) {
if (unlikely(cmd->request_bufflen < 1)) {
goto err_out;
}
- qc->flags |= ATA_QCFLAG_SG; /* data is present; dma-map it */
+ if (cmd->use_sg)
+ ata_sg_init(qc, cmd->request_buffer, cmd->use_sg);
+ else
+ ata_sg_init_one(qc, cmd->request_buffer,
+ cmd->request_bufflen);
+
+ qc->pci_dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
}
+ qc->complete_fn = ata_scsi_qc_complete;
+
if (xlat_func(qc, scsicmd))
goto err_out;
buflen = cmd->request_bufflen;
}
- memset(buf, 0, buflen);
*buf_out = buf;
return buflen;
}
* spin_lock_irqsave(host_set lock)
*/
-static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd)
+static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf)
{
if (cmd->use_sg) {
struct scatterlist *sg;
sg = (struct scatterlist *) cmd->request_buffer;
- kunmap_atomic(sg->page, KM_USER0);
+ kunmap_atomic(buf - sg->offset, KM_USER0);
}
}
/**
* ata_scsi_rbuf_fill - wrapper for SCSI command simulators
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @actor: Callback hook for desired SCSI command simulator
*
* Takes care of the hard work of simulating a SCSI command...
struct scsi_cmnd *cmd = args->cmd;
buflen = ata_scsi_rbuf_get(cmd, &rbuf);
+ memset(rbuf, 0, buflen);
rc = actor(args, rbuf, buflen);
- ata_scsi_rbuf_put(cmd);
+ ata_scsi_rbuf_put(cmd, rbuf);
if (rc)
ata_bad_cdb(cmd, args->done);
/**
* ata_scsiop_inq_std - Simulate INQUIRY command
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen)
{
- struct ata_device *dev = args->dev;
-
u8 hdr[] = {
TYPE_DISK,
0,
0x5, /* claim SPC-3 version compatibility */
2,
- 96 - 4
+ 95 - 4
};
/* set scsi removeable (RMB) bit per ata bit */
- if (ata_id_removeable(dev))
+ if (ata_id_removeable(args->id))
hdr[1] |= (1 << 7);
VPRINTK("ENTER\n");
memcpy(rbuf, hdr, sizeof(hdr));
- if (buflen > 36) {
+ if (buflen > 35) {
memcpy(&rbuf[8], "ATA ", 8);
- ata_dev_id_string(dev, &rbuf[16], ATA_ID_PROD_OFS, 16);
- ata_dev_id_string(dev, &rbuf[32], ATA_ID_FW_REV_OFS, 4);
+ ata_dev_id_string(args->id, &rbuf[16], ATA_ID_PROD_OFS, 16);
+ ata_dev_id_string(args->id, &rbuf[32], ATA_ID_FW_REV_OFS, 4);
if (rbuf[32] == 0 || rbuf[32] == ' ')
memcpy(&rbuf[32], "n/a ", 4);
}
/**
* ata_scsiop_inq_00 - Simulate INQUIRY EVPD page 0, list of pages
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
/**
* ata_scsiop_inq_80 - Simulate INQUIRY EVPD page 80, device serial number
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
};
memcpy(rbuf, hdr, sizeof(hdr));
- if (buflen > (ATA_SERNO_LEN + 4))
- ata_dev_id_string(args->dev, (unsigned char *) &rbuf[4],
+ if (buflen > (ATA_SERNO_LEN + 4 - 1))
+ ata_dev_id_string(args->id, (unsigned char *) &rbuf[4],
ATA_ID_SERNO_OFS, ATA_SERNO_LEN);
return 0;
/**
* ata_scsiop_inq_83 - Simulate INQUIRY EVPD page 83, device identity
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
rbuf[3] = 4 + strlen(inq_83_str); /* page len */
/* our one and only identification descriptor (vendor-specific) */
- if (buflen > (strlen(inq_83_str) + 4 + 4)) {
+ if (buflen > (strlen(inq_83_str) + 4 + 4 - 1)) {
rbuf[4 + 0] = 2; /* code set: ASCII */
rbuf[4 + 3] = strlen(inq_83_str);
memcpy(rbuf + 4 + 4, inq_83_str, strlen(inq_83_str));
/**
* ata_scsiop_noop -
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
/**
* ata_msense_caching - Simulate MODE SENSE caching info page
- * @dev: Device associated with this MODE SENSE command
+ * @id: device IDENTIFY data
* @ptr_io: (input/output) Location to store more output data
* @last: End of output data buffer
*
* None.
*/
-static unsigned int ata_msense_caching(struct ata_device *dev, u8 **ptr_io,
+static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
const u8 *last)
{
u8 page[] = {
0, 0, 0, 0, 0, 0, 0, 0 /* 8 zeroes */
};
- if (ata_id_wcache_enabled(dev))
+ if (ata_id_wcache_enabled(id))
page[2] |= (1 << 2); /* write cache enable */
- if (!ata_id_rahead_enabled(dev))
+ if (!ata_id_rahead_enabled(id))
page[12] |= (1 << 5); /* disable read ahead */
ata_msense_push(ptr_io, last, page, sizeof(page));
/**
* ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
unsigned int buflen)
{
u8 *scsicmd = args->cmd->cmnd, *p, *last;
- struct ata_device *dev = args->dev;
unsigned int page_control, six_byte, output_len;
VPRINTK("ENTER\n");
break;
case 0x08: /* caching */
- output_len += ata_msense_caching(dev, &p, last);
+ output_len += ata_msense_caching(args->id, &p, last);
break;
case 0x0a: { /* control mode */
case 0x3f: /* all pages */
output_len += ata_msense_rw_recovery(&p, last);
- output_len += ata_msense_caching(dev, &p, last);
+ output_len += ata_msense_caching(args->id, &p, last);
output_len += ata_msense_ctl_mode(&p, last);
break;
/**
* ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen)
{
- u64 n_sectors = args->dev->n_sectors;
+ u64 n_sectors;
u32 tmp;
VPRINTK("ENTER\n");
+ if (ata_id_has_lba48(args->id))
+ n_sectors = ata_id_u64(args->id, 100);
+ else
+ n_sectors = ata_id_u32(args->id, 60);
n_sectors--; /* ATA TotalUserSectors - 1 */
tmp = n_sectors; /* note: truncates, if lba48 */
/**
* ata_scsiop_report_luns - Simulate REPORT LUNS command
- * @args: Port / device / SCSI command of interest.
+ * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
*
done(cmd);
}
+static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) {
+ DPRINTK("request check condition\n");
+
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ qc->scsidone(cmd);
+
+ return 1;
+ } else {
+ u8 *scsicmd = cmd->cmnd;
+
+ if (scsicmd[0] == INQUIRY) {
+ u8 *buf = NULL;
+ unsigned int buflen;
+
+ buflen = ata_scsi_rbuf_get(cmd, &buf);
+ buf[2] = 0x5;
+ buf[3] = (buf[3] & 0xf0) | 2;
+ ata_scsi_rbuf_put(cmd, buf);
+ }
+ cmd->result = SAM_STAT_GOOD;
+ }
+
+ qc->scsidone(cmd);
+
+ return 0;
+}
/**
* atapi_xlat - Initialize PACKET taskfile
* @qc: command structure to be initialized
static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
{
struct scsi_cmnd *cmd = qc->scsicmd;
+ struct ata_device *dev = qc->dev;
+ int using_pio = (dev->flags & ATA_DFLAG_PIO);
+ int nodata = (cmd->sc_data_direction == SCSI_DATA_NONE);
+
+ memcpy(&qc->cdb, scsicmd, qc->ap->cdb_len);
+
+ qc->complete_fn = atapi_qc_complete;
qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
if (cmd->sc_data_direction == SCSI_DATA_WRITE) {
qc->tf.command = ATA_CMD_PACKET;
- /* no data - interrupt-driven */
- if (cmd->sc_data_direction == SCSI_DATA_NONE)
- qc->tf.protocol = ATA_PROT_ATAPI;
-
- /* PIO data xfer - polling */
- else if ((qc->flags & ATA_QCFLAG_DMA) == 0) {
- ata_qc_set_polling(qc);
- qc->tf.protocol = ATA_PROT_ATAPI;
+ /* no data, or PIO data xfer */
+ if (using_pio || nodata) {
+ if (nodata)
+ qc->tf.protocol = ATA_PROT_ATAPI_NODATA;
+ else
+ qc->tf.protocol = ATA_PROT_ATAPI;
qc->tf.lbam = (8 * 1024) & 0xff;
qc->tf.lbah = (8 * 1024) >> 8;
+ }
- /* DMA data xfer - interrupt-driven */
- } else {
+ /* DMA data xfer */
+ else {
qc->tf.protocol = ATA_PROT_ATAPI_DMA;
qc->tf.feature |= ATAPI_PKT_DMA;
#endif
}
+ qc->nbytes = cmd->bufflen;
+
return 0;
}
* Associated ATA device, or %NULL if not found.
*/
-static inline struct ata_device *
-ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd)
+static struct ata_device *
+ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev)
{
struct ata_device *dev;
/* skip commands not addressed to targets we simulate */
- if (likely(cmd->device->id < ATA_MAX_DEVICES))
- dev = &ap->device[cmd->device->id];
+ if (likely(scsidev->id < ATA_MAX_DEVICES))
+ dev = &ap->device[scsidev->id];
else
return NULL;
- if (unlikely((cmd->device->channel != 0) ||
- (cmd->device->lun != 0)))
+ if (unlikely((scsidev->channel != 0) ||
+ (scsidev->lun != 0)))
return NULL;
if (unlikely(!ata_dev_present(dev)))
/**
* ata_get_xlat_func - check if SCSI to ATA translation is possible
+ * @dev: ATA device
* @cmd: SCSI command opcode to consider
*
* Look up the SCSI command given, and determine whether the
* Pointer to translation function if possible, %NULL if not.
*/
-static inline ata_xlat_func_t ata_get_xlat_func(u8 cmd)
+static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
{
switch (cmd) {
case READ_6:
case WRITE_10:
case WRITE_16:
return ata_scsi_rw_xlat;
+
+ case SYNCHRONIZE_CACHE:
+ if (ata_try_flush_cache(dev))
+ return ata_scsi_flush_xlat;
+ break;
+
+ case VERIFY:
+ case VERIFY_16:
+ return ata_scsi_verify_xlat;
}
return NULL;
struct scsi_cmnd *cmd)
{
#ifdef ATA_DEBUG
+ struct scsi_device *scsidev = cmd->device;
u8 *scsicmd = cmd->cmnd;
DPRINTK("CDB (%u:%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ap->id,
- cmd->device->channel, cmd->device->id, cmd->device->lun,
+ scsidev->channel, scsidev->id, scsidev->lun,
scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3],
scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7],
scsicmd[8]);
{
struct ata_port *ap;
struct ata_device *dev;
+ struct scsi_device *scsidev = cmd->device;
- ap = (struct ata_port *) &cmd->device->host->hostdata[0];
+ ap = (struct ata_port *) &scsidev->host->hostdata[0];
ata_scsi_dump_cdb(ap, cmd);
- dev = ata_scsi_find_dev(ap, cmd);
+ dev = ata_scsi_find_dev(ap, scsidev);
if (unlikely(!dev)) {
cmd->result = (DID_BAD_TARGET << 16);
done(cmd);
}
if (dev->class == ATA_DEV_ATA) {
- ata_xlat_func_t xlat_func = ata_get_xlat_func(cmd->cmnd[0]);
+ ata_xlat_func_t xlat_func = ata_get_xlat_func(dev,
+ cmd->cmnd[0]);
if (xlat_func)
ata_scsi_translate(ap, dev, cmd, done, xlat_func);
else
- ata_scsi_simulate(ap, dev, cmd, done);
+ ata_scsi_simulate(dev->id, cmd, done);
} else
ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);
/**
* ata_scsi_simulate - simulate SCSI command on ATA device
- * @ap: Port to which ATA device is attached.
- * @dev: Target device for CDB.
+ * @id: current IDENTIFY data for target device.
* @cmd: SCSI command being sent to device.
* @done: SCSI command completion function.
*
* spin_lock_irqsave(host_set lock)
*/
-static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
- struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
+void ata_scsi_simulate(u16 *id,
+ struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
{
struct ata_scsi_args args;
u8 *scsicmd = cmd->cmnd;
- args.ap = ap;
- args.dev = dev;
+ args.id = id;
args.cmd = cmd;
args.done = done;
switch(scsicmd[0]) {
/* no-op's, complete with success */
- case SYNCHRONIZE_CACHE: /* FIXME: temporary */
+ case SYNCHRONIZE_CACHE:
case REZERO_UNIT:
case SEEK_6:
case SEEK_10: