The READ/WRITE LONG commands are theoretically obsolete, but most (all?) drives to date still implement them. Of these, WRITE_LONG and WRITE_LONG_ONCE are of particular interest for fault injection testing -- eg. creating "media errors" at specific locations on a disk. The fussy bit is that these commands require a non-standard sector size, usually 520 bytes instead of 512. This patch adds support to libata for READ/WRITE LONG commands issued via SG_IO/ATA_16. Signed-off-by: Mark Lord <mlord@xxxxxxxxx> --- diff -u --recursive --new-file --exclude='.*' --exclude='*.[osa]' --exclude='*.ko' --exclude='*.mod.c' linux-2.6.20/drivers/ata/libata-core.c new/drivers/ata/libata-core.c --- linux-2.6.20/drivers/ata/libata-core.c 2007-02-04 13:44:54.000000000 -0500 +++ new/drivers/ata/libata-core.c 2007-02-12 19:41:55.000000000 -0500 @@ -1249,7 +1249,7 @@ buflen += sg[i].length; ata_sg_init(qc, sg, n_elem); - qc->nsect = buflen / ATA_SECT_SIZE; + qc->nsect = buflen / qc->sect_size; qc->nbytes = buflen; } @@ -4004,7 +4004,7 @@ /** - * ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data. + * ata_pio_sector - Transfer a "sector" of data. * @qc: Command on going * * Transfer ATA_SECT_SIZE of data from/to the ATA device. @@ -4021,12 +4021,13 @@ struct page *page; unsigned int offset; unsigned char *buf; + unsigned int sect_size = qc->sect_size; if (qc->cursect == (qc->nsect - 1)) ap->hsm_task_state = HSM_ST_LAST; page = sg[qc->cursg].page; - offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE; + offset = sg[qc->cursg].offset + qc->cursg_ofs * sect_size; /* get the current page and offset */ page = nth_page(page, (offset >> PAGE_SHIFT)); @@ -4042,30 +4043,30 @@ buf = kmap_atomic(page, KM_IRQ0); /* do the actual data transfer */ - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(qc->dev, buf + offset, sect_size, do_write); kunmap_atomic(buf, KM_IRQ0); local_irq_restore(flags); } else { buf = page_address(page); - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(qc->dev, buf + offset, sect_size, do_write); } qc->cursect++; qc->cursg_ofs++; - if ((qc->cursg_ofs * ATA_SECT_SIZE) == (&sg[qc->cursg])->length) { + if ((qc->cursg_ofs * sect_size) == (&sg[qc->cursg])->length) { qc->cursg++; qc->cursg_ofs = 0; } } /** - * ata_pio_sectors - Transfer one or many 512-byte sectors. + * ata_pio_sectors - Transfer one or many sectors. * @qc: Command on going * - * Transfer one or many ATA_SECT_SIZE of data from/to the - * ATA device for the DRQ request. + * Transfer one or many qc->sect_size chunks of data + * from/to the ATA device for the DRQ request. * * LOCKING: * Inherited from caller. diff -u --recursive --new-file --exclude='.*' --exclude='*.[osa]' --exclude='*.ko' --exclude='*.mod.c' linux-2.6.20/drivers/ata/libata-scsi.c new/drivers/ata/libata-scsi.c --- linux-2.6.20/drivers/ata/libata-scsi.c 2007-02-04 13:44:54.000000000 -0500 +++ new/drivers/ata/libata-scsi.c 2007-02-12 19:38:55.000000000 -0500 @@ -2634,6 +2634,19 @@ tf->device = qc->dev->devno ? tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1; + /* READ/WRITE LONG use a non-standard sect_size */ + qc->sect_size = ATA_SECT_SIZE; + switch (tf->command) { + case ATA_CMD_READ_LONG: + case ATA_CMD_READ_LONG_ONCE: + case ATA_CMD_WRITE_LONG: + case ATA_CMD_WRITE_LONG_ONCE: + if (tf->protocol != ATA_PROT_PIO + || tf->nsect != 1) + goto invalid_fld; + qc->sect_size = cmd->request_bufflen; + } + /* * Filter SET_FEATURES - XFER MODE command -- otherwise, * SET_FEATURES - XFER MODE must be preceded/succeeded @@ -2661,7 +2674,7 @@ * TODO: find out if we need to do more here to * cover scatter/gather case. */ - qc->nsect = scmd->request_bufflen / ATA_SECT_SIZE; + qc->nsect = scmd->request_bufflen / qc->sect_size; /* request result TF */ qc->flags |= ATA_QCFLAG_RESULT_TF; diff -u --recursive --new-file --exclude='.*' --exclude='*.[osa]' --exclude='*.ko' --exclude='*.mod.c' linux-2.6.20/include/linux/ata.h new/include/linux/ata.h --- linux-2.6.20/include/linux/ata.h 2007-02-04 13:44:54.000000000 -0500 +++ new/include/linux/ata.h 2007-02-12 19:34:30.000000000 -0500 @@ -160,6 +160,12 @@ /* READ_LOG_EXT pages */ ATA_LOG_SATA_NCQ = 0x10, + /* READ/WRITE LONG (obsolete) */ + ATA_CMD_READ_LONG = 0x22, + ATA_CMD_READ_LONG_ONCE = 0x23, + ATA_CMD_WRITE_LONG = 0x32, + ATA_CMD_WRITE_LONG_ONCE = 0x33, + /* SETFEATURES stuff */ SETFEATURES_XFER = 0x03, XFER_UDMA_7 = 0x47, diff -u --recursive --new-file --exclude='.*' --exclude='*.[osa]' --exclude='*.ko' --exclude='*.mod.c' linux-2.6.20/include/linux/libata.h new/include/linux/libata.h --- linux-2.6.20/include/linux/libata.h 2007-02-04 13:44:54.000000000 -0500 +++ new/include/linux/libata.h 2007-02-12 19:32:57.000000000 -0500 @@ -430,6 +430,7 @@ int dma_dir; unsigned int pad_len; + unsigned int sect_size; unsigned int nsect; unsigned int cursect; @@ -1155,6 +1156,7 @@ qc->n_elem = 0; qc->err_mask = 0; qc->pad_len = 0; + qc->sect_size = 512; ata_tf_init(qc->dev, &qc->tf); - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html