]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/libata-scsi.c
libata-pmp-prep: implement sata_async_notification()
[linux-2.6-omap-h63xx.git] / drivers / ata / libata-scsi.c
index 598ef1018e192589a67abed0745ec230cf7fcc8f..df2e05738f3b3806238e10d5cc78bdb222270217 100644 (file)
@@ -71,11 +71,10 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
 #define ALL_SUB_MPAGES 0xff
 
 
-static const u8 def_rw_recovery_mpage[] = {
+static const u8 def_rw_recovery_mpage[RW_RECOVERY_MPAGE_LEN] = {
        RW_RECOVERY_MPAGE,
        RW_RECOVERY_MPAGE_LEN - 2,
-       (1 << 7) |      /* AWRE, sat-r06 say it shall be 0 */
-           (1 << 6),   /* ARRE (auto read reallocation) */
+       (1 << 7),       /* AWRE */
        0,              /* read retry count */
        0, 0, 0, 0,
        0,              /* write retry count */
@@ -450,8 +449,8 @@ static struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev,
                qc->scsicmd = cmd;
                qc->scsidone = done;
 
-               qc->__sg = (struct scatterlist *) cmd->request_buffer;
-               qc->n_elem = cmd->use_sg;
+               qc->__sg = scsi_sglist(cmd);
+               qc->n_elem = scsi_sg_count(cmd);
        } else {
                cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1);
                done(cmd);
@@ -750,6 +749,13 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
 {
        sdev->use_10_for_rw = 1;
        sdev->use_10_for_ms = 1;
+
+       /* Schedule policy is determined by ->qc_defer() callback and
+        * it needs to see every deferred qc.  Set dev_blocked to 1 to
+        * prevent SCSI midlayer from automatically deferring
+        * requests.
+        */
+       sdev->max_device_blocked = 1;
 }
 
 static void ata_scsi_dev_config(struct scsi_device *sdev,
@@ -938,6 +944,13 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
                goto invalid_fld;       /* LOEJ bit set not supported */
        if (((cdb[4] >> 4) & 0xf) != 0)
                goto invalid_fld;       /* power conditions not supported */
+
+       if (qc->dev->horkage & ATA_HORKAGE_SKIP_PM) {
+               /* the device lacks PM support, finish without doing anything */
+               scmd->result = SAM_STAT_GOOD;
+               return 1;
+       }
+
        if (cdb[4] & 0x1) {
                tf->nsect = 1;  /* 1 sector, lba=0 */
 
@@ -1416,37 +1429,6 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
        ata_qc_free(qc);
 }
 
-/**
- *     ata_scmd_need_defer - Check whether we need to defer scmd
- *     @dev: ATA device to which the command is addressed
- *     @is_io: Is the command IO (and thus possibly NCQ)?
- *
- *     NCQ and non-NCQ commands cannot run together.  As upper layer
- *     only knows the queue depth, we are responsible for maintaining
- *     exclusion.  This function checks whether a new command can be
- *     issued to @dev.
- *
- *     LOCKING:
- *     spin_lock_irqsave(host lock)
- *
- *     RETURNS:
- *     1 if deferring is needed, 0 otherwise.
- */
-static int ata_scmd_need_defer(struct ata_device *dev, int is_io)
-{
-       struct ata_link *link = dev->link;
-       int is_ncq = is_io && ata_ncq_enabled(dev);
-
-       if (is_ncq) {
-               if (!ata_tag_valid(link->active_tag))
-                       return 0;
-       } else {
-               if (!ata_tag_valid(link->active_tag) && !link->sactive)
-                       return 0;
-       }
-       return 1;
-}
-
 /**
  *     ata_scsi_translate - Translate then issue SCSI command to ATA device
  *     @dev: ATA device to which the command is addressed
@@ -1478,14 +1460,12 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
                              void (*done)(struct scsi_cmnd *),
                              ata_xlat_func_t xlat_func)
 {
+       struct ata_port *ap = dev->link->ap;
        struct ata_queued_cmd *qc;
-       int is_io = xlat_func == ata_scsi_rw_xlat;
+       int rc;
 
        VPRINTK("ENTER\n");
 
-       if (unlikely(ata_scmd_need_defer(dev, is_io)))
-               goto defer;
-
        qc = ata_scsi_qc_new(dev, cmd, done);
        if (!qc)
                goto err_mem;
@@ -1493,13 +1473,13 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
        /* data is present; dma-map it */
        if (cmd->sc_data_direction == DMA_FROM_DEVICE ||
            cmd->sc_data_direction == DMA_TO_DEVICE) {
-               if (unlikely(cmd->request_bufflen < 1)) {
+               if (unlikely(scsi_bufflen(cmd) < 1)) {
                        ata_dev_printk(dev, KERN_WARNING,
                                       "WARNING: zero len r/w req\n");
                        goto err_did;
                }
 
-               ata_sg_init(qc, cmd->request_buffer, cmd->use_sg);
+               ata_sg_init(qc, scsi_sglist(cmd), scsi_sg_count(cmd));
 
                qc->dma_dir = cmd->sc_data_direction;
        }
@@ -1509,6 +1489,11 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
        if (xlat_func(qc))
                goto early_finish;
 
+       if (ap->ops->qc_defer) {
+               if ((rc = ap->ops->qc_defer(qc)))
+                       goto defer;
+       }
+
        /* select device, send command to hardware */
        ata_qc_issue(qc);
 
@@ -1530,8 +1515,12 @@ err_mem:
        return 0;
 
 defer:
+       ata_qc_free(qc);
        DPRINTK("EXIT - defer\n");
-       return SCSI_MLQUEUE_DEVICE_BUSY;
+       if (rc == ATA_DEFER_LINK)
+               return SCSI_MLQUEUE_DEVICE_BUSY;
+       else
+               return SCSI_MLQUEUE_HOST_BUSY;
 }
 
 /**
@@ -1553,7 +1542,7 @@ static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out)
        u8 *buf;
        unsigned int buflen;
 
-       struct scatterlist *sg = (struct scatterlist *) cmd->request_buffer;
+       struct scatterlist *sg = scsi_sglist(cmd);
 
        if (sg) {
                buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset;
@@ -1580,7 +1569,7 @@ static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out)
 
 static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf)
 {
-       struct scatterlist *sg = (struct scatterlist *) cmd->request_buffer;
+       struct scatterlist *sg = scsi_sglist(cmd);
        if (sg)
                kunmap_atomic(buf - sg->offset, KM_IRQ0);
 }
@@ -1803,6 +1792,62 @@ unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,
        return 0;
 }
 
+/**
+ *     ata_scsiop_inq_89 - Simulate INQUIRY VPD page 89, ATA info
+ *     @args: device IDENTIFY data / SCSI command of interest.
+ *     @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *     @buflen: Response buffer length.
+ *
+ *     Yields SAT-specified ATA VPD page.
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ */
+
+unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf,
+                             unsigned int buflen)
+{
+       u8 pbuf[60];
+       struct ata_taskfile tf;
+       unsigned int i;
+
+       if (!buflen)
+               return 0;
+
+       memset(&pbuf, 0, sizeof(pbuf));
+       memset(&tf, 0, sizeof(tf));
+
+       pbuf[1] = 0x89;                 /* our page code */
+       pbuf[2] = (0x238 >> 8);         /* page size fixed at 238h */
+       pbuf[3] = (0x238 & 0xff);
+
+       memcpy(&pbuf[8], "linux   ", 8);
+       memcpy(&pbuf[16], "libata          ", 16);
+       memcpy(&pbuf[32], DRV_VERSION, 4);
+       ata_id_string(args->id, &pbuf[32], ATA_ID_FW_REV, 4);
+
+       /* we don't store the ATA device signature, so we fake it */
+
+       tf.command = ATA_DRDY;          /* really, this is Status reg */
+       tf.lbal = 0x1;
+       tf.nsect = 0x1;
+
+       ata_tf_to_fis(&tf, 0, 1, &pbuf[36]);    /* TODO: PMP? */
+       pbuf[36] = 0x34;                /* force D2H Reg FIS (34h) */
+
+       pbuf[56] = ATA_CMD_ID_ATA;
+
+       i = min(buflen, 60U);
+       memcpy(rbuf, &pbuf[0], i);
+       buflen -= i;
+
+       if (!buflen)
+               return 0;
+
+       memcpy(&rbuf[60], &args->id[0], min(buflen, 512U));
+       return 0;
+}
+
 /**
  *     ata_scsiop_noop - Command handler that simply returns success.
  *     @args: device IDENTIFY data / SCSI command of interest.
@@ -2383,7 +2428,7 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
        }
 
        qc->tf.command = ATA_CMD_PACKET;
-       qc->nbytes = scmd->request_bufflen;
+       qc->nbytes = scsi_bufflen(scmd);
 
        /* check whether ATAPI DMA is safe */
        if (!using_pio && ata_check_atapi_dma(qc))
@@ -2633,7 +2678,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
        case ATA_CMD_WRITE_LONG_ONCE:
                if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1)
                        goto invalid_fld;
-               qc->sect_size = scmd->request_bufflen;
+               qc->sect_size = scsi_bufflen(scmd);
        }
 
        /*
@@ -2663,7 +2708,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
         * TODO: find out if we need to do more here to
         *       cover scatter/gather case.
         */
-       qc->nbytes = scmd->request_bufflen;
+       qc->nbytes = scsi_bufflen(scmd);
 
        /* request result TF */
        qc->flags |= ATA_QCFLAG_RESULT_TF;
@@ -2857,6 +2902,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
 {
        struct ata_scsi_args args;
        const u8 *scsicmd = cmd->cmnd;
+       u8 tmp8;
 
        args.dev = dev;
        args.id = dev->id;
@@ -2864,15 +2910,9 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
        args.done = done;
 
        switch(scsicmd[0]) {
-               /* 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);
+               /* TODO: worth improving? */
+               case FORMAT_UNIT:
+                       ata_scsi_invalid_field(cmd, done);
                        break;
 
                case INQUIRY:
@@ -2880,14 +2920,23 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
                                ata_scsi_invalid_field(cmd, done);
                        else if ((scsicmd[1] & 1) == 0)    /* is EVPD clear? */
                                ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
-                       else if (scsicmd[2] == 0x00)
+                       else switch (scsicmd[2]) {
+                       case 0x00:
                                ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00);
-                       else if (scsicmd[2] == 0x80)
+                               break;
+                       case 0x80:
                                ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80);
-                       else if (scsicmd[2] == 0x83)
+                               break;
+                       case 0x83:
                                ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83);
-                       else
+                               break;
+                       case 0x89:
+                               ata_scsi_rbuf_fill(&args, ata_scsiop_inq_89);
+                               break;
+                       default:
                                ata_scsi_invalid_field(cmd, done);
+                               break;
+                       }
                        break;
 
                case MODE_SENSE:
@@ -2915,8 +2964,33 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
                        ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns);
                        break;
 
-               /* mandatory commands we haven't implemented yet */
                case REQUEST_SENSE:
+                       ata_scsi_set_sense(cmd, 0, 0, 0);
+                       cmd->result = (DRIVER_SENSE << 24);
+                       done(cmd);
+                       break;
+
+               /* if we reach this, then writeback caching is disabled,
+                * turning this into a no-op.
+                */
+               case SYNCHRONIZE_CACHE:
+                       /* fall through */
+
+               /* no-op's, complete with success */
+               case REZERO_UNIT:
+               case SEEK_6:
+               case SEEK_10:
+               case TEST_UNIT_READY:
+                       ata_scsi_rbuf_fill(&args, ata_scsiop_noop);
+                       break;
+
+               case SEND_DIAGNOSTIC:
+                       tmp8 = scsicmd[1] & ~(1 << 3);
+                       if ((tmp8 == 0x4) && (!scsicmd[3]) && (!scsicmd[4]))
+                               ata_scsi_rbuf_fill(&args, ata_scsiop_noop);
+                       else
+                               ata_scsi_invalid_field(cmd, done);
+                       break;
 
                /* all other commands */
                default:
@@ -2950,6 +3024,13 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
                shost->max_channel = 1;
                shost->max_cmd_len = 16;
 
+               /* Schedule policy is determined by ->qc_defer()
+                * callback and it needs to see every deferred qc.
+                * Set host_blocked to 1 to prevent SCSI midlayer from
+                * automatically deferring requests.
+                */
+               shost->max_host_blocked = 1;
+
                rc = scsi_add_host(ap->scsi_host, ap->host->dev);
                if (rc)
                        goto err_add;
@@ -3154,15 +3235,15 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
  *     event.
  *
  *     LOCKING:
- *     interrupt context, may not sleep.
+ *     spin_lock_irqsave(host lock)
  */
-void ata_scsi_media_change_notify(struct ata_device *atadev)
+void ata_scsi_media_change_notify(struct ata_device *dev)
 {
 #ifdef OTHER_AN_PATCHES_HAVE_BEEN_APPLIED
-       scsi_device_event_notify(atadev->sdev, SDEV_MEDIA_CHANGE);
+       if (dev->sdev)
+               scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE);
 #endif
 }
-EXPORT_SYMBOL_GPL(ata_scsi_media_change_notify);
 
 /**
  *     ata_scsi_hotplug - SCSI part of hotplug