Re: [PATCH libata-dev-2.6 1/3] Add CHS support
Jeff, > Applied patches 1 and 2 of the first CHS series, > and patches 3-1 through 3-3 of the second CHS series, to libata-dev queue. > Thanks. > As a follow-up, I believe libata-core should be modified to issue the > INITIALIZE DEVICE PARAMETERS command for all CHS devices. > I will work on this, according to the discussion on the issue. Albert - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
Applied patches 1 and 2 of the first CHS series, and patches 3-1 through 3-3 of the second CHS series, to libata-dev queue. As a follow-up, I believe libata-core should be modified to issue the INITIALIZE DEVICE PARAMETERS command for all CHS devices. Thanks, Jeff - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
> 2) Patch #3 needs to be split up into three patches: > a) rename variables (s/sector/block/), > b) reorganize read/write translation > c) add CHS support > Patch 3-3: - add CHS support to ata_scsi_verify_xlat(), ata_scsi_rw_xlat() and ata_scsiop_read_cap(). Albert Signed-off-by: Albert Lee <[EMAIL PROTECTED]> -- --- libata-dev-2.6/drivers/scsi/libata-scsi.c 2005-02-14 17:18:22.0 +0800 +++ libata-dev-2.6-mod/drivers/scsi/libata-scsi.c 2005-02-14 17:25:43.0 +0800 @@ -689,6 +689,8 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) { struct ata_taskfile *tf = &qc->tf; + struct ata_device *dev = qc->dev; + unsigned int lba = tf->flags & ATA_TFLAG_LBA; unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; u64 dev_sectors = qc->dev->n_sectors; u64 block = 0; @@ -696,7 +698,6 @@ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->protocol = ATA_PROT_NODATA; - tf->device |= ATA_LBA; if (scsicmd[0] == VERIFY) { block |= ((u64)scsicmd[2]) << 24; @@ -741,25 +742,53 @@ return 1; } - if (lba48) { - tf->command = ATA_CMD_VERIFY_EXT; + if (lba) { + if (lba48) { + tf->command = ATA_CMD_VERIFY_EXT; + + tf->hob_nsect = (n_block >> 8) & 0xff; + + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + } else { + tf->command = ATA_CMD_VERIFY; - tf->hob_nsect = (n_block >> 8) & 0xff; + tf->device |= (block >> 24) & 0xf; + } - tf->hob_lbah = (block >> 40) & 0xff; - tf->hob_lbam = (block >> 32) & 0xff; - tf->hob_lbal = (block >> 24) & 0xff; - } else { - tf->command = ATA_CMD_VERIFY; + tf->nsect = n_block & 0xff; - tf->device |= (block >> 24) & 0xf; - } + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; - tf->nsect = n_block & 0xff; + tf->device |= ATA_LBA; + } else { + /* CHS */ + u32 sect, head, cyl, track; - tf->lbah = (block >> 16) & 0xff; - tf->lbam = (block >> 8) & 0xff; - tf->lbal = block & 0xff; + /* Convert LBA to CHS */ + track = (u32)block / dev->sectors; + cyl = track / dev->heads; + head = track % dev->heads; + sect = (u32)block % dev->sectors + 1; + + DPRINTK("block[%u] track[%u] cyl[%u] head[%u] sect[%u] \n", (u32)block, track, cyl, head, sect); + + /* Check whether the converted CHS can fit. + Cylinder: 0-65535 + Head: 0-15 + Sector: 1-255*/ + if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) + return 1; + + tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ + tf->lbal = sect; + tf->lbam = cyl; + tf->lbah = cyl >> 8; + tf->device |= head; + } return 0; } @@ -787,6 +816,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) { struct ata_taskfile *tf = &qc->tf; + struct ata_device *dev = qc->dev; + unsigned int lba = tf->flags & ATA_TFLAG_LBA; unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; u64 block = 0; u32 n_block = 0; @@ -845,34 +876,66 @@ /* In ATA, sector count 0 are 256 or 65536 sectors, not 0 sectors. */ return 1; - if (lba48) { - /* The request -may- be too large for LBA48. */ - if ((block >> 48) || (n_block > 65536)) - return 1; + if (lba) { + if (lba48) { + /* The request -may- be too large for LBA48. */ + if ((block >> 48) || (n_block > 65536)) +return 1; + + tf->hob_nsect = (n_block >> 8) & 0xff; + + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + } else { + /* LBA28 */ + + /* The request -may- be too large for LBA28. */ + if ((block >> 28) || (n_block > 256)) +return 1; + + tf->device |= (block >> 24) & 0xf; + } + + qc->nsect = n_block; + tf->nsect = n_block & 0xff; - tf->hob_nsect = (n_block >> 8) & 0xff; + 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; } else { - /* LBA28 */ + /* CHS */ + u32 sect, head, cyl, track; - /* The request -may- be too large for LBA28. */ + /* The request -may- be too large for CHS addressing. */ if ((block >> 28) || (n_block > 256)) return 1; - - tf->device |= (block >> 24) & 0xf; + + /* Convert LBA to CHS */ + track = (u32)block / dev->sectors; + cyl = track / dev->heads; + head = track % dev->heads; + sect = (u32)block % dev->sectors + 1; + + DPRINTK("block[%u] track[%u] cyl[%u] head[%u] sect[%u] \n", + (u32)block, track, cyl, head, sect); + + /* Check whether the converted CHS can fit. + Cylinder: 0-65535 + Head: 0-15 + Sector: 1-255*/ + if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) + return 1; + + qc->nsect = n_block; + tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sect
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
> 2) Patch #3 needs to be split up into three patches: > a) rename variables (s/sector/block/), > b) reorganize read/write translation > c) add CHS support > Patch 3-2: - reorganize read/write translation in ata_scsi_rw_xlat() Albert Signed-off-by: Albert Lee <[EMAIL PROTECTED]> -- --- libata-dev-2.6/drivers/scsi/libata-scsi.c 2005-02-14 16:54:47.0 +0800 +++ libata-dev-2.6-mod/drivers/scsi/libata-scsi.c 2005-02-14 17:05:12.0 +0800 @@ -788,10 +788,11 @@ { struct ata_taskfile *tf = &qc->tf; unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; + u64 block = 0; + u32 n_block = 0; tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->protocol = qc->dev->xfer_protocol; - tf->device |= ATA_LBA; if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 || scsicmd[0] == READ_16) { @@ -801,80 +802,79 @@ tf->flags |= ATA_TFLAG_WRITE; } + /* Calculate the SCSI LBA and transfer length. */ if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) { - if (lba48) { - tf->hob_nsect = scsicmd[7]; - tf->hob_lbal = scsicmd[2]; - - qc->nsect = ((unsigned int)scsicmd[7] << 8) | - scsicmd[8]; - } else { - /* if we don't support LBA48 addressing, the request -* -may- be too large. */ - if ((scsicmd[2] & 0xf0) || scsicmd[7]) -return 1; - - /* stores LBA27:24 in lower 4 bits of device reg */ - tf->device |= scsicmd[2]; + block |= ((u64)scsicmd[2]) << 24; + block |= ((u64)scsicmd[3]) << 16; + block |= ((u64)scsicmd[4]) << 8; + block |= ((u64)scsicmd[5]); - qc->nsect = scsicmd[8]; - } - - tf->nsect = scsicmd[8]; - tf->lbal = scsicmd[5]; - tf->lbam = scsicmd[4]; - tf->lbah = scsicmd[3]; + n_block |= ((u32)scsicmd[7]) << 8; + n_block |= ((u32)scsicmd[8]); VPRINTK("ten-byte command\n"); - return 0; - } - - if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) { - qc->nsect = tf->nsect = scsicmd[4]; - tf->lbal = scsicmd[3]; - tf->lbam = scsicmd[2]; - tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */ - + } else if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) { + block |= ((u64)scsicmd[2]) << 8; + block |= ((u64)scsicmd[3]); + n_block |= ((u32)scsicmd[4]); + VPRINTK("six-byte command\n"); - return 0; - } + } else if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) { + block |= ((u64)scsicmd[2]) << 56; + block |= ((u64)scsicmd[3]) << 48; + block |= ((u64)scsicmd[4]) << 40; + block |= ((u64)scsicmd[5]) << 32; + block |= ((u64)scsicmd[6]) << 24; + block |= ((u64)scsicmd[7]) << 16; + block |= ((u64)scsicmd[8]) << 8; + block |= ((u64)scsicmd[9]); + + n_block |= ((u32)scsicmd[10]) << 24; + n_block |= ((u32)scsicmd[11]) << 16; + n_block |= ((u32)scsicmd[12]) << 8; + n_block |= ((u32)scsicmd[13]); - if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) { - /* rule out impossible LBAs and sector counts */ - if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11]) - return 1; + VPRINTK("sixteen-byte command\n"); + } else { + DPRINTK("no-byte command\n"); + return 1; + } - if (lba48) { - tf->hob_nsect = scsicmd[12]; - tf->hob_lbal = scsicmd[6]; - tf->hob_lbam = scsicmd[5]; - tf->hob_lbah = scsicmd[4]; + /* Check and compose ATA command */ + if (!n_block) + /* In ATA, sector count 0 are 256 or 65536 sectors, not 0 sectors. */ + return 1; - qc->nsect = ((unsigned int)scsicmd[12] << 8) | - scsicmd[13]; - } else { - /* once again, filter out impossible non-zero values */ - if (scsicmd[4] || scsicmd[5] || scsicmd[12] || - (scsicmd[6] & 0xf0)) -return 1; + if (lba48) { + /* The request -may- be too large for LBA48. */ + if ((block >> 48) || (n_block > 65536)) + return 1; - /* stores LBA27:24 in lower 4 bits of device reg */ - tf->device |= scsicmd[6]; + tf->hob_nsect = (n_block >> 8) & 0xff; - qc->nsect = scsicmd[13]; - } + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + } else { + /* LBA28 */ - tf->nsect = scsicmd[13]; - tf->lbal = scsicmd[9]; - tf->lbam = scsicmd[8]; - tf->lbah = scsicmd[7]; + /* The request -may- be too large for LBA28. */ + if ((block >> 28) || (n_block > 256)) + return 1; - VPRINTK("sixteen-byte command\n"); - return 0; + tf->device |= (block >> 24) & 0xf; } + + qc->nsect = n_block; + tf->nsect = n_block & 0xff; + + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; - DPRINTK("no-byte command\n"); - return 1; + tf->device |= ATA_LBA; + + return 0; } static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) chs3-2.diff Description: Binary data
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
Dear all: > > Comments: > > 1) Patches #1 and #2 look OK. > > 2) Patch #3 needs to be split up into three patches: > a) rename variables (s/sector/block/), > b) reorganize read/write translation > c) add CHS support > Happy lunar new year. Attached pls find the split patch #3 for your review. Patch 3-1: - rename variables (s/sector/block/) in ata_scsi_verify_xlat() Albert Signed-off-by: Albert Lee <[EMAIL PROTECTED]> --- libata-dev-2.6/drivers/scsi/libata-scsi.c 2005-02-14 16:26:44.0 +0800 +++ libata-dev-2.6-mod/drivers/scsi/libata-scsi.c 2005-02-14 16:45:33.0 +0800 @@ -691,75 +691,75 @@ 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; + u64 block = 0; + u32 n_block = 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]); + block |= ((u64)scsicmd[2]) << 24; + block |= ((u64)scsicmd[3]) << 16; + block |= ((u64)scsicmd[4]) << 8; + block |= ((u64)scsicmd[5]); - n_sect |= ((u32)scsicmd[7]) << 8; - n_sect |= ((u32)scsicmd[8]); + n_block |= ((u32)scsicmd[7]) << 8; + n_block |= ((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]); + block |= ((u64)scsicmd[2]) << 56; + block |= ((u64)scsicmd[3]) << 48; + block |= ((u64)scsicmd[4]) << 40; + block |= ((u64)scsicmd[5]) << 32; + block |= ((u64)scsicmd[6]) << 24; + block |= ((u64)scsicmd[7]) << 16; + block |= ((u64)scsicmd[8]) << 8; + block |= ((u64)scsicmd[9]); + + n_block |= ((u32)scsicmd[10]) << 24; + n_block |= ((u32)scsicmd[11]) << 16; + n_block |= ((u32)scsicmd[12]) << 8; + n_block |= ((u32)scsicmd[13]); } else return 1; - if (!n_sect) + if (!n_block) return 1; - if (sect >= dev_sectors) + if (block >= dev_sectors) return 1; - if ((sect + n_sect) > dev_sectors) + if ((block + n_block) > dev_sectors) return 1; if (lba48) { - if (n_sect > (64 * 1024)) + if (n_block > (64 * 1024)) return 1; } else { - if (n_sect > 256) + if (n_block > 256) return 1; } if (lba48) { tf->command = ATA_CMD_VERIFY_EXT; - tf->hob_nsect = (n_sect >> 8) & 0xff; + tf->hob_nsect = (n_block >> 8) & 0xff; - tf->hob_lbah = (sect >> 40) & 0xff; - tf->hob_lbam = (sect >> 32) & 0xff; - tf->hob_lbal = (sect >> 24) & 0xff; + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; } else { tf->command = ATA_CMD_VERIFY; - tf->device |= (sect >> 24) & 0xf; + tf->device |= (block >> 24) & 0xf; } - tf->nsect = n_sect & 0xff; + tf->nsect = n_block & 0xff; - tf->lbah = (sect >> 16) & 0xff; - tf->lbam = (sect >> 8) & 0xff; - tf->lbal = sect & 0xff; + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; return 0; } chs3-1.diff Description: Binary data
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
The exact sequence expected by certain pre-ATA4 (or earlier?) drives was this: SRST RESET IDENTIFY INITIALIZE DRIVE PARAMETERS anything else.. Some drives were very specific about that exact sequence. Nowadays, I don't bother with it for anything new I do, as it is obsolete, and may even get rejected by modern drives. But for the mainstream Linux ATA support, we should issue it for drives which IDENTIFY themselves as conforming to pre-ATAx, where the safest value for "x" is probably "4". Cheers -- Mark Lord Real-Time Remedies Inc. [EMAIL PROTECTED] Jeff Garzik wrote: Mark Lord wrote: Jeff Garzik wrote: 3) In Promise's GPL'd driver for their SATA (w/ PATA) cards, they unconditionally execute the INITIALIZE DEVICE PARAMETERS command. I wonder if we should do the same. That command was *necessary* for certain drives at one time, which refused all other commands until they received the IDP one. Except IDENTIFY DEVICE, presumably? Thanks, Jeff - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
Mark Lord wrote: Jeff Garzik wrote: 3) In Promise's GPL'd driver for their SATA (w/ PATA) cards, they unconditionally execute the INITIALIZE DEVICE PARAMETERS command. I wonder if we should do the same. That command was *necessary* for certain drives at one time, which refused all other commands until they received the IDP one. Except IDENTIFY DEVICE, presumably? Thanks, Jeff - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
Jeff Garzik wrote: 3) In Promise's GPL'd driver for their SATA (w/ PATA) cards, they unconditionally execute the INITIALIZE DEVICE PARAMETERS command. I wonder if we should do the same. That command was *necessary* for certain drives at one time, which refused all other commands until they received the IDP one. Cheers -- Mark Lord Real-Time Remedies Inc. [EMAIL PROTECTED] - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH libata-dev-2.6 1/3] Add CHS support
Thanks for doing this work. Comments: 1) Patches #1 and #2 look OK. 2) Patch #3 needs to be split up into three patches: a) rename variables (s/sector/block/), b) reorganize read/write translation c) add CHS support Otherwise, patch #3 (content-wise) is OK. 3) In Promise's GPL'd driver for their SATA (w/ PATA) cards, they unconditionally execute the INITIALIZE DEVICE PARAMETERS command. I wonder if we should do the same. 4) I will apply patches #1 and #2 after I receive feedback on my comments in item #3. I have not applied them yet, even though they are acceptable. - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH libata-dev-2.6 1/3] Add CHS support
Hi Jeff, These are the patches for libata CHS support. Tested OK with my old 1994 ST-5850A hardddisk. Changes: 1. Add the flags, utility function and CHS fields to the and headers. 2. ata_dev_identify() pathed to report and initialize CHS fields in ata_device. ata_qc_new_init() patched to initialize the ATA_TFLAG_LBA to the ata_taskfile. 3. ata_scsi_verify_xlat(), ata_scsi_rw_xlat() patched to support CHS translation. ata_scsiop_read_cap() patched to report capacity for CHS-only drives. Attached please find the patch 1/3 against the libata-dev-2.6 tree for your review. Thanks. Albert Signed-off-by: Albert Lee <[EMAIL PROTECTED]> -- diff -Nru libata-dev-2.6/include/linux/ata.h libata-dev-2.6-mod/include/linux/ata.h --- libata-dev-2.6/include/linux/ata.h 2005-02-05 10:42:06.0 +0800 +++ libata-dev-2.6-mod/include/linux/ata.h 2005-02-05 11:04:33.0 +0800 @@ -172,6 +172,7 @@ ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */ ATA_TFLAG_DEVICE = (1 << 2), /* enable r/w to device reg */ ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */ + ATA_TFLAG_LBA = (1 << 4), /* enable LBA */ }; enum ata_tf_protocols { @@ -241,6 +242,18 @@ ((u64) (id)[(n) + 1] << 16) | \ ((u64) (id)[(n) + 0]) ) +static inline int ata_id_current_chs_valid(u16 *id) +{ + /* For ATA-1 devices, if the INITIALIZE DEVICE PARAMETERS command +has not been issued to the device then the values of +id[54] to id[56] are vendor specific. */ + return (id[53] & 0x01) && /* Current translation valid */ + id[54] && /* cylinders in current translation */ + id[55] && /* heads in current translation */ + id[55] <= 16 && + id[56];/* sectors in current translation */ +} + static inline int atapi_cdb_len(u16 *dev_id) { u16 tmp = dev_id[0] & 0x3; diff -Nru libata-dev-2.6/include/linux/libata.h libata-dev-2.6-mod/include/linux/libata.h --- libata-dev-2.6/include/linux/libata.h 2005-02-05 10:42:07.0 +0800 +++ libata-dev-2.6-mod/include/linux/libata.h 2005-02-05 11:05:58.0 +0800 @@ -95,6 +95,7 @@ ATA_DFLAG_LBA48 = (1 << 0), /* device supports LBA48 */ ATA_DFLAG_PIO = (1 << 1), /* device currently in PIO mode */ ATA_DFLAG_LOCK_SECTORS = (1 << 2), /* don't adjust max_sectors */ + ATA_DFLAG_LBA = (1 << 3), /* device supports LBA */ ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ @@ -279,6 +280,11 @@ u8 xfer_protocol; /* taskfile xfer protocol */ u8 read_cmd; /* opcode to use on read */ u8 write_cmd; /* opcode to use on write */ + + /* for CHS addressing */ + u16 cylinders; /* Number of cylinders */ + u16 heads; /* Number of heads */ + u16 sectors; /* Number of sectors per track */ }; struct ata_port { chs1.diff Description: Binary data