summaryrefslogtreecommitdiff
path: root/drivers/ata/libata-scsi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-11-26 19:18:22 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-11-26 19:18:22 -0800
commit6d272940537e834848d88c11b428e9973b8fa2bc (patch)
treef01b6d173c62e5f9daeb50937ff6d1d17e4320f6 /drivers/ata/libata-scsi.c
parentf4d53cedce872fe1439818d15e067b497b5d466f (diff)
parente190222d04cb1119c62876ac87cf9b9403ba3bd5 (diff)
Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev
* 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev: (21 commits) libata: bump transfer chunk size if it's odd libata: Return proper ATA INT status in pata_bf54x driver pata_ali: trim trailing whitespace (fix checkpatch complaints) pata_isapnp: Polled devices pata_hpt37x: Fix cable detect bug spotted by Sergei pata_ali: Lots of problems still showing up with small ATAPI DMA pata_ali: Add Mitac 8317 and derivatives libata-core: List more documentation sources for reference ata_piix: Invalid use of writel/readl with iomap sata_sil24: fix sg table sizing pata_jmicron: fix disabled port handling in jmicron_pre_reset() pata_sil680: kill bogus reset code (take 2) ata_piix: port enable for the first SATA controller of ICH8 is 0xf not 0x3 ata_piix: only enable the first port on apple macbook pro ata_piix: reorganize controller IDs pata_sis.c: Add Packard Bell EasyNote K5305 to laptops libata-scsi: be tolerant of 12-byte ATAPI commands in 16-byte CDBs libata: use ATA_HORKAGE_STUCK_ERR for ATAPI tape drives libata: workaround DRQ=1 ERR=1 for ATAPI tape drives libata: remove unused functions ...
Diffstat (limited to 'drivers/ata/libata-scsi.c')
-rw-r--r--drivers/ata/libata-scsi.c38
1 files changed, 34 insertions, 4 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 94144ed50a6b..a883bb03d4c7 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2485,11 +2485,40 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
if (!using_pio && ata_check_atapi_dma(qc))
using_pio = 1;
- /* Some controller variants snoop this value for Packet transfers
- to do state machine and FIFO management. Thus we want to set it
- properly, and for DMA where it is effectively meaningless */
+ /* Some controller variants snoop this value for Packet
+ * transfers to do state machine and FIFO management. Thus we
+ * want to set it properly, and for DMA where it is
+ * effectively meaningless.
+ */
nbytes = min(qc->nbytes, (unsigned int)63 * 1024);
+ /* Most ATAPI devices which honor transfer chunk size don't
+ * behave according to the spec when odd chunk size which
+ * matches the transfer length is specified. If the number of
+ * bytes to transfer is 2n+1. According to the spec, what
+ * should happen is to indicate that 2n+1 is going to be
+ * transferred and transfer 2n+2 bytes where the last byte is
+ * padding.
+ *
+ * In practice, this doesn't happen. ATAPI devices first
+ * indicate and transfer 2n bytes and then indicate and
+ * transfer 2 bytes where the last byte is padding.
+ *
+ * This inconsistency confuses several controllers which
+ * perform PIO using DMA such as Intel AHCIs and sil3124/32.
+ * These controllers use actual number of transferred bytes to
+ * update DMA poitner and transfer of 4n+2 bytes make those
+ * controller push DMA pointer by 4n+4 bytes because SATA data
+ * FISes are aligned to 4 bytes. This causes data corruption
+ * and buffer overrun.
+ *
+ * Always setting nbytes to even number solves this problem
+ * because then ATAPI devices don't have to split data at 2n
+ * boundaries.
+ */
+ if (nbytes & 0x1)
+ nbytes++;
+
qc->tf.lbam = (nbytes & 0xFF);
qc->tf.lbah = (nbytes >> 8);
@@ -2869,7 +2898,8 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
xlat_func = NULL;
if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
/* relay SCSI command to ATAPI device */
- if (unlikely(scmd->cmd_len > dev->cdb_len))
+ int len = COMMAND_SIZE(scsi_op);
+ if (unlikely(len > scmd->cmd_len || len > dev->cdb_len))
goto bad_cdb_len;
xlat_func = atapi_xlat;