vserver 1.9.5.x5
[linux-2.6.git] / drivers / scsi / libata-scsi.c
index 7b5a978..7f59ef6 100644 (file)
 #include <linux/spinlock.h>
 #include <scsi/scsi.h>
 #include "scsi.h"
-#include "hosts.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);
 
 
 /**
@@ -46,8 +46,8 @@ static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
  *     @geom: location to which geometry will be output
  *
  *     Generic bios head/sector/cylinder calculator
- *     used by sd. Most BIOSes nowadays expect a XXX/255/16  (CHS) 
- *     mapping. Some situations may arise where the disk is not 
+ *     used by sd. Most BIOSes nowadays expect a XXX/255/16  (CHS)
+ *     mapping. Some situations may arise where the disk is not
  *     bootable if this is not used.
  *
  *     LOCKING:
@@ -57,7 +57,7 @@ static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
  *     Zero.
  */
 int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev,
-                      sector_t capacity, int geom[]) 
+                      sector_t capacity, int geom[])
 {
        geom[0] = 255;
        geom[1] = 63;
@@ -67,6 +67,43 @@ int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev,
        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
@@ -119,35 +156,161 @@ struct ata_queued_cmd *ata_scsi_qc_new(struct ata_port *ap,
 /**
  *     ata_to_sense_error - convert ATA error to SCSI error
  *     @qc: Command that we are erroring out
+ *     @drv_stat: value contained in ATA status register
  *
- *     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);
+
+       i = 0;
+       /* 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" */
        }
 }
 
@@ -167,8 +330,28 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
 {
        sdev->use_10_for_rw = 1;
        sdev->use_10_for_ms = 1;
+
        blk_queue_max_phys_segments(sdev->request_queue, LIBATA_MAX_PRD);
 
+       if (sdev->id < ATA_MAX_DEVICES) {
+               struct ata_port *ap;
+               struct ata_device *dev;
+
+               ap = (struct ata_port *) &sdev->host->hostdata[0];
+               dev = &ap->device[sdev->id];
+
+               /* TODO: 1024 is an arbitrary number, not the
+                * hardware maximum.  This should be increased to
+                * 65534 when Jens Axboe's patch for dynamically
+                * determining max_sectors is merged.
+                */
+               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);
+               }
+       }
+
        return 0;       /* scsi layer doesn't check return value, sigh */
 }
 
@@ -194,10 +377,139 @@ int ata_scsi_error(struct Scsi_Host *host)
        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->command = ATA_CMD_VERIFY_EXT;
+
+               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->command = ATA_CMD_VERIFY;
+
+               tf->device |= (sect >> 24) & 0xf;
+       }
+
+       tf->nsect = n_sect & 0xff;
+
+       tf->lbah = (sect >> 16) & 0xff;
+       tf->lbam = (sect >> 8) & 0xff;
+       tf->lbal = sect & 0xff;
+
+       return 0;
+}
+
 /**
  *     ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one
  *     @qc: Storage for translated ATA taskfile
@@ -224,10 +536,6 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
        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;
 
@@ -297,7 +605,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
                                return 1;
 
                        /* stores LBA27:24 in lower 4 bits of device reg */
-                       tf->device |= scsicmd[2];
+                       tf->device |= scsicmd[6];
 
                        qc->nsect = scsicmd[13];
                }
@@ -315,12 +623,27 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
        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
  *     @dev: ATA device to which the command is addressed
  *     @cmd: SCSI command to execute
  *     @done: SCSI command completion function
+ *     @xlat_func: Actor which translates @cmd to an ATA taskfile
  *
  *     Our ->queuecommand() function has decided that the SCSI
  *     command issued can be directly translated into an ATA
@@ -343,19 +666,29 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev,
 
        VPRINTK("ENTER\n");
 
-       if (unlikely(cmd->request_bufflen < 1)) {
-               printk(KERN_WARNING "ata%u(%u): empty request buffer\n",
-                      ap->id, dev->devno);
-               goto err_out;
-       }
-
        qc = ata_scsi_qc_new(ap, dev, cmd, done);
        if (!qc)
                return;
 
+       /* data is present; dma-map it */
        if (cmd->sc_data_direction == SCSI_DATA_READ ||
-           cmd->sc_data_direction == SCSI_DATA_WRITE)
-               qc->flags |= ATA_QCFLAG_SG; /* data is present; dma-map it */
+           cmd->sc_data_direction == SCSI_DATA_WRITE) {
+               if (unlikely(cmd->request_bufflen < 1)) {
+                       printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n",
+                              ap->id, dev->devno);
+                       goto err_out;
+               }
+
+               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->dma_dir = cmd->sc_data_direction;
+       }
+
+       qc->complete_fn = ata_scsi_qc_complete;
 
        if (xlat_func(qc, scsicmd))
                goto err_out;
@@ -368,6 +701,7 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev,
        return;
 
 err_out:
+       ata_qc_free(qc);
        ata_bad_cdb(cmd, done);
        DPRINTK("EXIT - badcmd\n");
 }
@@ -402,7 +736,6 @@ static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out)
                buflen = cmd->request_bufflen;
        }
 
-       memset(buf, 0, buflen);
        *buf_out = buf;
        return buflen;
 }
@@ -410,6 +743,7 @@ static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out)
 /**
  *     ata_scsi_rbuf_put - Unmap response buffer.
  *     @cmd: SCSI command containing buffer to be unmapped.
+ *     @buf: buffer to unmap
  *
  *     Unmaps response buffer contained within @cmd.
  *
@@ -417,19 +751,19 @@ static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out)
  *     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...
@@ -451,8 +785,9 @@ void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
        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);
@@ -464,7 +799,7 @@ void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
 
 /**
  *     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.
  *
@@ -478,22 +813,28 @@ void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
 unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
                               unsigned int buflen)
 {
-       const u8 hdr[] = {
+       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(args->id))
+               hdr[1] |= (1 << 7);
+
        VPRINTK("ENTER\n");
 
        memcpy(rbuf, hdr, sizeof(hdr));
 
-       if (buflen > 36) {
-               memcpy(&rbuf[8], args->dev->vendor, 8);
-               memcpy(&rbuf[16], args->dev->product, 16);
-               memcpy(&rbuf[32], DRV_VERSION, 4);
+       if (buflen > 35) {
+               memcpy(&rbuf[8], "ATA     ", 8);
+               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);
        }
 
        if (buflen > 63) {
@@ -515,7 +856,7 @@ unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
 
 /**
  *     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.
  *
@@ -543,7 +884,7 @@ unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf,
 
 /**
  *     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.
  *
@@ -564,8 +905,8 @@ unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,
        };
        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;
@@ -575,7 +916,7 @@ static const char *inq_83_str = "Linux ATA-SCSI simulator";
 
 /**
  *     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.
  *
@@ -593,7 +934,7 @@ unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,
        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));
@@ -604,7 +945,7 @@ unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,
 
 /**
  *     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.
  *
@@ -652,7 +993,7 @@ static void ata_msense_push(u8 **ptr_io, const u8 *last,
 
 /**
  *     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
  *
@@ -664,12 +1005,20 @@ static void ata_msense_push(u8 **ptr_io, const u8 *last,
  *     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[7] = { 0xf, 0, 0x10, 0, 0x8, 0xa, 0 };
-       if (dev->flags & ATA_DFLAG_WCACHE)
-               page[6] = 0x4;
+       u8 page[] = {
+               0x8,                            /* page code */
+               0x12,                           /* page length */
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* 10 zeroes */
+               0, 0, 0, 0, 0, 0, 0, 0          /* 8 zeroes */
+       };
+
+       if (ata_id_wcache_enabled(id))
+               page[2] |= (1 << 2);    /* write cache enable */
+       if (!ata_id_rahead_enabled(id))
+               page[12] |= (1 << 5);   /* disable read ahead */
 
        ata_msense_push(ptr_io, last, page, sizeof(page));
        return sizeof(page);
@@ -695,9 +1044,34 @@ static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)
        return sizeof(page);
 }
 
+/**
+ *     ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page
+ *     @dev: Device associated with this MODE SENSE command
+ *     @ptr_io: (input/output) Location to store more output data
+ *     @last: End of output data buffer
+ *
+ *     Generate a generic MODE SENSE r/w error recovery page.
+ *
+ *     LOCKING:
+ *     None.
+ */
+
+static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
+{
+       const u8 page[] = {
+               0x1,                      /* page code */
+               0xa,                      /* page length */
+               (1 << 7) | (1 << 6),      /* note auto r/w reallocation */
+               0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */
+       };
+
+       ata_msense_push(ptr_io, last, page, sizeof(page));
+       return 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.
  *
@@ -711,7 +1085,6 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
                                  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");
@@ -734,8 +1107,12 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
        last = rbuf + buflen - 1;
 
        switch(scsicmd[2] & 0x3f) {
+       case 0x01:              /* r/w error recovery */
+               output_len += ata_msense_rw_recovery(&p, last);
+               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 */
@@ -744,7 +1121,8 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
                }
 
        case 0x3f:              /* all pages */
-               output_len += ata_msense_caching(dev, &p, last);
+               output_len += ata_msense_rw_recovery(&p, last);
+               output_len += ata_msense_caching(args->id, &p, last);
                output_len += ata_msense_ctl_mode(&p, last);
                break;
 
@@ -766,7 +1144,7 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
 
 /**
  *     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.
  *
@@ -779,25 +1157,32 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
 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");
 
-       n_sectors--;            /* one off */
+       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 */
        if (args->cmd->cmnd[0] == READ_CAPACITY) {
+               /* sector count, 32-bit */
                rbuf[0] = tmp >> (8 * 3);
                rbuf[1] = tmp >> (8 * 2);
                rbuf[2] = tmp >> (8 * 1);
                rbuf[3] = tmp;
 
+               /* sector size */
                tmp = ATA_SECT_SIZE;
                rbuf[6] = tmp >> 8;
                rbuf[7] = tmp;
 
        } else {
+               /* sector count, 64-bit */
                rbuf[2] = n_sectors >> (8 * 7);
                rbuf[3] = n_sectors >> (8 * 6);
                rbuf[4] = n_sectors >> (8 * 5);
@@ -807,6 +1192,7 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
                rbuf[8] = tmp >> (8 * 1);
                rbuf[9] = tmp;
 
+               /* sector size */
                tmp = ATA_SECT_SIZE;
                rbuf[12] = tmp >> 8;
                rbuf[13] = tmp;
@@ -817,7 +1203,7 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
 
 /**
  *     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.
  *
@@ -865,57 +1251,64 @@ void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8
        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_scsi_queuecmd - Send CDB to ATAPI device
- *     @ap: Port to which ATAPI device is attached.
- *     @dev: Target device for CDB.
- *     @cmd: SCSI command being sent to device.
- *     @done: SCSI command completion function.
- *
- *     Sends CDB to ATAPI device.  If the Linux SCSI layer sends a
- *     non-data command, then this function handles the command
- *     directly, via polling.  Otherwise, the bmdma engine is started.
+ *     atapi_xlat - Initialize PACKET taskfile
+ *     @qc: command structure to be initialized
+ *     @scsicmd: SCSI CDB associated with this PACKET command
  *
  *     LOCKING:
  *     spin_lock_irqsave(host_set lock)
+ *
+ *     RETURNS:
+ *     Zero on success, non-zero on failure.
  */
 
-static void atapi_scsi_queuecmd(struct ata_port *ap, struct ata_device *dev,
-                              struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
 {
-       struct ata_queued_cmd *qc;
-       u8 *scsicmd = cmd->cmnd, status;
-       unsigned int doing_dma = 0;
+       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);
 
-       VPRINTK("ENTER, drv_stat = 0x%x\n", ata_chk_status(ap));
+       if (!using_pio)
+               /* Check whether ATAPI DMA is safe */
+               if (ata_check_atapi_dma(qc))
+                       using_pio = 1;
 
-       if (cmd->sc_data_direction == SCSI_DATA_UNKNOWN) {
-               DPRINTK("unknown data, scsicmd 0x%x\n", scsicmd[0]);
-               ata_bad_cdb(cmd, done);
-               return;
-       }
-
-       switch(scsicmd[0]) {
-       case READ_6:
-       case WRITE_6:
-       case MODE_SELECT:
-       case MODE_SENSE:
-               DPRINTK("read6/write6/modesel/modesense trap\n");
-               ata_bad_scsiop(cmd, done);
-               return;
+       memcpy(&qc->cdb, scsicmd, qc->ap->cdb_len);
 
-       default:
-               /* do nothing */
-               break;
-       }
-
-       qc = ata_scsi_qc_new(ap, dev, cmd, done);
-       if (!qc) {
-               printk(KERN_ERR "ata%u: command queue empty\n", ap->id);
-               return;
-       }
-
-       qc->flags |= ATA_QCFLAG_ATAPI;
+       qc->complete_fn = atapi_qc_complete;
 
        qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
        if (cmd->sc_data_direction == SCSI_DATA_WRITE) {
@@ -925,58 +1318,37 @@ static void atapi_scsi_queuecmd(struct ata_port *ap, struct ata_device *dev,
 
        qc->tf.command = ATA_CMD_PACKET;
 
-       /* set up SG table */
-       if (cmd->sc_data_direction == SCSI_DATA_NONE) {
-               ap->active_tag = qc->tag;
-               qc->flags |= ATA_QCFLAG_ACTIVE | ATA_QCFLAG_POLL;
-               qc->tf.protocol = ATA_PROT_ATAPI;
-
-               ata_dev_select(ap, dev->devno, 1, 0);
+       /* 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;
+       }
 
-               DPRINTK("direction: none\n");
-               qc->tf.ctl |= ATA_NIEN; /* disable interrupts */
-               ata_tf_to_host_nolock(ap, &qc->tf);
-       } else {
-               qc->flags |= ATA_QCFLAG_SG; /* data is present; dma-map it */
-               qc->tf.feature = ATAPI_PKT_DMA;
+       /* DMA data xfer */
+       else {
                qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+               qc->tf.feature |= ATAPI_PKT_DMA;
 
-               doing_dma = 1;
-
-               /* select device, send command to hardware */
-               if (ata_qc_issue(qc))
-                       goto err_out;
+#ifdef ATAPI_ENABLE_DMADIR
+               /* some SATA bridges need us to indicate data xfer direction */
+               if (cmd->sc_data_direction != SCSI_DATA_WRITE)
+                       qc->tf.feature |= ATAPI_DMADIR;
+#endif
        }
 
-       status = ata_busy_wait(ap, ATA_BUSY, 1000);
-       if (status & ATA_BUSY) {
-               ata_thread_wake(ap, THR_PACKET);
-               return;
-       }
-       if ((status & ATA_DRQ) == 0)
-               goto err_out;
+       qc->nbytes = cmd->bufflen;
 
-       /* FIXME: mmio-ize */
-       DPRINTK("writing cdb\n");
-       outsl(ap->ioaddr.data_addr, scsicmd, ap->host->max_cmd_len / 4);
-
-       if (!doing_dma)
-               ata_thread_wake(ap, THR_PACKET);
-
-       VPRINTK("EXIT\n");
-       return;
-
-err_out:
-       if (!doing_dma)
-               ata_irq_on(ap); /* re-enable interrupts */
-       ata_bad_cdb(cmd, done);
-       DPRINTK("EXIT - badcmd\n");
+       return 0;
 }
 
 /**
  *     ata_scsi_find_dev - lookup ata_device from scsi_cmnd
  *     @ap: ATA port to which the device is attached
- *     @cmd: SCSI command to be sent to the device
+ *     @scsidev: SCSI device from which we derive the ATA device
  *
  *     Given various information provided in struct scsi_cmnd,
  *     map that onto an ATA bus, and using that mapping
@@ -990,19 +1362,19 @@ err_out:
  *     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)))
@@ -1018,6 +1390,7 @@ ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd)
 
 /**
  *     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
@@ -1027,7 +1400,7 @@ ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd)
  *     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:
@@ -1038,6 +1411,15 @@ static inline ata_xlat_func_t ata_get_xlat_func(u8 cmd)
        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;
@@ -1055,11 +1437,12 @@ static inline void ata_scsi_dump_cdb(struct ata_port *ap,
                                     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]);
@@ -1089,12 +1472,13 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
 {
        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);
@@ -1102,14 +1486,15 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        }
 
        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
-               atapi_scsi_queuecmd(ap, dev, cmd, done);
+               ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);
 
 out_unlock:
        return 0;
@@ -1117,8 +1502,7 @@ out_unlock:
 
 /**
  *     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.
  *
@@ -1129,20 +1513,24 @@ out_unlock:
  *     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]) {
-               case TEST_UNIT_READY:           /* FIXME: correct? */
+               /* no-op's, complete with success */
+               case SYNCHRONIZE_CACHE:
+               case REZERO_UNIT:
+               case SEEK_6:
+               case SEEK_10:
+               case TEST_UNIT_READY:
                case FORMAT_UNIT:               /* FIXME: correct? */
                case SEND_DIAGNOSTIC:           /* FIXME: correct? */
                        ata_scsi_rbuf_fill(&args, ata_scsiop_noop);