diff options
author | Hannes Reinecke <hare@suse.de> | 2016-04-25 12:45:53 +0200 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2016-05-09 18:36:46 +0200 |
commit | 27708a9579ee069c6e0ebb6e61ac1114ed1d546c (patch) | |
tree | 1c3621cfb397453ea044274a5940b23dcd4d1856 /drivers/ata/libata-scsi.c | |
parent | libata: implement ZBC IN translation (diff) | |
download | linux-27708a9579ee069c6e0ebb6e61ac1114ed1d546c.tar.xz linux-27708a9579ee069c6e0ebb6e61ac1114ed1d546c.zip |
libata: Implement ZBC OUT translation
ZAC drives implement a 'ZAC Management Out' command template,
which maps onto the ZBC OUT command.
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata/libata-scsi.c')
-rw-r--r-- | drivers/ata/libata-scsi.c | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 06d5a62f507d..6afd0840ebbe 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3471,6 +3471,70 @@ invalid_param_len: return 1; } +static unsigned int ata_scsi_zbc_out_xlat(struct ata_queued_cmd *qc) +{ + struct ata_taskfile *tf = &qc->tf; + struct scsi_cmnd *scmd = qc->scsicmd; + struct ata_device *dev = qc->dev; + const u8 *cdb = scmd->cmnd; + u8 reset_all, sa; + u64 block; + u32 n_block; + u16 fp = (u16)-1; + + if (unlikely(scmd->cmd_len < 16)) { + fp = 15; + goto invalid_fld; + } + + sa = cdb[1] & 0x1f; + if ((sa != ZO_CLOSE_ZONE) && (sa != ZO_FINISH_ZONE) && + (sa != ZO_OPEN_ZONE) && (sa != ZO_RESET_WRITE_POINTER)) { + fp = 1; + goto invalid_fld; + } + + scsi_16_lba_len(cdb, &block, &n_block); + if (n_block) { + /* + * ZAC MANAGEMENT OUT doesn't define any length + */ + goto invalid_param_len; + } + if (block > dev->n_sectors) + goto out_of_range; + + reset_all = cdb[14] & 0x1; + + tf->protocol = ATA_PROT_NODATA; + tf->command = ATA_CMD_ZAC_MGMT_OUT; + tf->feature = sa; + tf->hob_feature = reset_all & 0x1; + + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + tf->device = ATA_LBA; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + + return 0; + + invalid_fld: + ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff); + return 1; + out_of_range: + /* "Logical Block Address out of range" */ + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x00); + return 1; +invalid_param_len: + /* "Parameter list length error" */ + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); + return 1; +} + /** * ata_mselect_caching - Simulate MODE SELECT for caching info page * @qc: Storage for translated ATA taskfile @@ -3789,6 +3853,9 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) case ZBC_IN: return ata_scsi_zbc_in_xlat; + case ZBC_OUT: + return ata_scsi_zbc_out_xlat; + case START_STOP: return ata_scsi_start_stop_xlat; } |