Commit c79dd287 authored by Jeff Garzik's avatar Jeff Garzik

[libata] support commands SYNCHRONIZE CACHE, VERIFY, VERIFY(16)

parent e4babc07
...@@ -218,6 +218,124 @@ int ata_scsi_error(struct Scsi_Host *host) ...@@ -218,6 +218,124 @@ int ata_scsi_error(struct Scsi_Host *host)
return 0; 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)))
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 * ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one
* @qc: Storage for translated ATA taskfile * @qc: Storage for translated ATA taskfile
...@@ -244,10 +362,6 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) ...@@ -244,10 +362,6 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; 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->protocol = qc->dev->xfer_protocol;
tf->device |= ATA_LBA; tf->device |= ATA_LBA;
...@@ -1086,6 +1200,7 @@ ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd) ...@@ -1086,6 +1200,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 * ata_get_xlat_func - check if SCSI to ATA translation is possible
* @dev: ATA device
* @cmd: SCSI command opcode to consider * @cmd: SCSI command opcode to consider
* *
* Look up the SCSI command given, and determine whether the * Look up the SCSI command given, and determine whether the
...@@ -1095,7 +1210,7 @@ ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd) ...@@ -1095,7 +1210,7 @@ ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd)
* Pointer to translation function if possible, %NULL if not. * 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) { switch (cmd) {
case READ_6: case READ_6:
...@@ -1106,6 +1221,15 @@ static inline ata_xlat_func_t ata_get_xlat_func(u8 cmd) ...@@ -1106,6 +1221,15 @@ static inline ata_xlat_func_t ata_get_xlat_func(u8 cmd)
case WRITE_10: case WRITE_10:
case WRITE_16: case WRITE_16:
return ata_scsi_rw_xlat; 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; return NULL;
...@@ -1170,7 +1294,8 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) ...@@ -1170,7 +1294,8 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
} }
if (dev->class == ATA_DEV_ATA) { 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) if (xlat_func)
ata_scsi_translate(ap, dev, cmd, done, xlat_func); ata_scsi_translate(ap, dev, cmd, done, xlat_func);
...@@ -1211,7 +1336,7 @@ static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev, ...@@ -1211,7 +1336,7 @@ static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev,
switch(scsicmd[0]) { switch(scsicmd[0]) {
/* no-op's, complete with success */ /* no-op's, complete with success */
case SYNCHRONIZE_CACHE: /* FIXME: temporary */ case SYNCHRONIZE_CACHE:
case REZERO_UNIT: case REZERO_UNIT:
case SEEK_6: case SEEK_6:
case SEEK_10: case SEEK_10:
......
...@@ -215,6 +215,8 @@ struct ata_taskfile { ...@@ -215,6 +215,8 @@ struct ata_taskfile {
#define ata_id_is_ata(dev) (((dev)->id[0] & (1 << 15)) == 0) #define ata_id_is_ata(dev) (((dev)->id[0] & (1 << 15)) == 0)
#define ata_id_rahead_enabled(dev) ((dev)->id[85] & (1 << 6)) #define ata_id_rahead_enabled(dev) ((dev)->id[85] & (1 << 6))
#define ata_id_wcache_enabled(dev) ((dev)->id[85] & (1 << 5)) #define ata_id_wcache_enabled(dev) ((dev)->id[85] & (1 << 5))
#define ata_id_has_flush(dev) ((dev)->id[83] & (1 << 12))
#define ata_id_has_flush_ext(dev) ((dev)->id[83] & (1 << 13))
#define ata_id_has_lba48(dev) ((dev)->id[83] & (1 << 10)) #define ata_id_has_lba48(dev) ((dev)->id[83] & (1 << 10))
#define ata_id_has_wcache(dev) ((dev)->id[82] & (1 << 5)) #define ata_id_has_wcache(dev) ((dev)->id[82] & (1 << 5))
#define ata_id_has_pm(dev) ((dev)->id[82] & (1 << 3)) #define ata_id_has_pm(dev) ((dev)->id[82] & (1 << 3))
......
...@@ -607,4 +607,11 @@ static inline u8 ata_bmdma_status(struct ata_port *ap) ...@@ -607,4 +607,11 @@ static inline u8 ata_bmdma_status(struct ata_port *ap)
return host_stat; return host_stat;
} }
static inline int ata_try_flush_cache(struct ata_device *dev)
{
return ata_id_wcache_enabled(dev) ||
ata_id_has_flush(dev) ||
ata_id_has_flush_ext(dev);
}
#endif /* __LINUX_LIBATA_H__ */ #endif /* __LINUX_LIBATA_H__ */
...@@ -108,6 +108,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; ...@@ -108,6 +108,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
#define WRITE_LONG_2 0xea #define WRITE_LONG_2 0xea
#define READ_16 0x88 #define READ_16 0x88
#define WRITE_16 0x8a #define WRITE_16 0x8a
#define VERIFY_16 0x8f
#define SERVICE_ACTION_IN 0x9e #define SERVICE_ACTION_IN 0x9e
/* values for service action in */ /* values for service action in */
#define SAI_READ_CAPACITY_16 0x10 #define SAI_READ_CAPACITY_16 0x10
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment