Re: IBM 37GB drive

Andries.Brouwer@cwi.nl
Wed, 25 Aug 1999 21:29:01 +0200 (MET DST)


p0070621@brookes.ac.uk:

: I've just found your copy of the Large Disk HOWTO, 2.2c,
: dated Sunday.
:
: My 37Gb DPTA-353750 drive arrived yesterday, and caused me problems.
: It ought to have 72680 cylinders, but is clocking over at 64k, and
: only reporting 7144, with a resulting capacity of 3.4 Gb.
: Fortunately, the notes under 12.1 IDE problems with 34+ GB disks
: claim that a patch is available.

Try the one below. (It is not quite finished - I did some
work last Sunday and plan to do more but there are too many
things...)
Please report on success / non-success.

It is a patch on linux-2.3.14.
Similar changes work on other kernels.

(In fact 2.3.14 [patched or not] does not work for me,
but I have not yet investigated the cause. If it works
for you, fine. Otherwise a very similar patch should
work on 2.2.11 or so. And if the only goal is to make
this particular drive work a 1-line patch might suffice.)

Andries - aeb@cwi.nl

[Linus, Alan: this is not the submission of a patch -
do not use this.]

--------------------------------------------------------------------
diff -u --recursive --new-file ../linux-2.3.14/linux/drivers/block/ide-disk.c ./linux/drivers/block/ide-disk.c
--- ../linux-2.3.14/linux/drivers/block/ide-disk.c Tue Jul 13 15:12:02 1999
+++ ./linux/drivers/block/ide-disk.c Sun Aug 22 23:59:32 1999
@@ -94,42 +94,41 @@
*
* Returns: 1 if lba_capacity looks sensible
* 0 otherwise
+ *
+ * It is called only once for each drive.
+ * (%% It modifies id->lba_capacity, bad!)
*/
static int lba_capacity_is_ok (struct hd_driveid *id)
{
- unsigned long lba_sects = id->lba_capacity;
- unsigned long chs_sects = id->cyls * id->heads * id->sectors;
- unsigned long _10_percent = chs_sects / 10;
+ unsigned long lba_sects, chs_sects, head, tail;

/*
- * very large drives (8GB+) may lie about the number of cylinders
- * This is a split test for drives 8 Gig and Bigger only.
+ * The ATA spec tells large drives to return
+ * C/H/S = 16383/16/63 independent of their size.
+ * Some drives can be jumpered to use 15 heads instead of 16.
*/
- if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) &&
- (id->heads == 16) && (id->sectors == 63)) {
- id->cyls = lba_sects / (16 * 63); /* correct cyls */
- return 1; /* lba_capacity is our only option */
- }
- /*
- * ... and at least one TLA VBC has POS instead of brain and can't
- * tell 16 from 15.
- */
- if ((id->lba_capacity >= 15481935) && (id->cyls == 0x3fff) &&
- (id->heads == 15) && (id->sectors == 63)) {
- id->cyls = lba_sects / (15 * 63); /* correct cyls */
- return 1; /* lba_capacity is our only option */
- }
- /* perform a rough sanity check on lba_sects: within 10% is "okay" */
- if ((lba_sects - chs_sects) < _10_percent) {
- return 1; /* lba_capacity is good */
- }
+ if (id->cyls == 16383 && id->sectors == 63 &&
+ (id->heads == 15 || id->heads == 16) &&
+ id->lba_capacity >= 16383*63*id->heads)
+ return 1;
+
+ lba_sects = id->lba_capacity;
+ chs_sects = id->cyls * id->heads * id->sectors;
+
+ /* perform a rough sanity check on lba_sects: within 10% is OK */
+ if ((lba_sects - chs_sects) < chs_sects/10)
+ return 1;
+
/* some drives have the word order reversed */
- lba_sects = (lba_sects << 16) | (lba_sects >> 16);
- if ((lba_sects - chs_sects) < _10_percent) {
- id->lba_capacity = lba_sects; /* fix it */
+ head = ((lba_sects >> 16) & 0xffff);
+ tail = (lba_sects & 0xffff);
+ lba_sects = (head | (tail << 16));
+ if ((lba_sects - chs_sects) < chs_sects/10) {
+ id->lba_capacity = lba_sects; /* %% fix it */
return 1; /* lba_capacity is (now) good */
}
- return 0; /* lba_capacity value is bad */
+
+ return 0; /* lba_capacity value may be bad */
}

/*
@@ -450,24 +449,27 @@
}

/*
- * current_capacity() returns the capacity (in sectors) of a drive
- * according to its current geometry/LBA settings.
+ * Compute drive->capacity, the full capacity of the drive
*/
-static unsigned long idedisk_capacity (ide_drive_t *drive)
+static void init_idedisk_capacity (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
unsigned long capacity = drive->cyl * drive->head * drive->sect;

drive->select.b.lba = 0;
+
/* Determine capacity, and use LBA if the drive properly supports it */
if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
- if (id->lba_capacity >= capacity) {
- drive->cyl = id->lba_capacity / (drive->head * drive->sect);
- capacity = id->lba_capacity;
- drive->select.b.lba = 1;
- }
+ drive->cyl = id->lba_capacity / (drive->head * drive->sect);
+ capacity = id->lba_capacity;
+ drive->select.b.lba = 1;
}
- return (capacity - drive->sect0);
+ drive->capacity = capacity;
+}
+
+static unsigned long idedisk_capacity (ide_drive_t *drive)
+{
+ return (drive->capacity - drive->sect0);
}

static void idedisk_special (ide_drive_t *drive)
@@ -628,7 +630,7 @@
int major = HWIF(drive)->major;
int minor = drive->select.b.unit << PARTN_BITS;

- ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 65535, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL);
ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL);
@@ -679,7 +681,7 @@
static void idedisk_setup (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
- unsigned long capacity, check;
+ unsigned long capacity;

idedisk_add_settings(drive);

@@ -705,66 +707,37 @@
drive->head = drive->bios_head = id->heads;
drive->sect = drive->bios_sect = id->sectors;
}
+
/* Handle logical geometry translation by the drive */
if ((id->field_valid & 1) && id->cur_cyls &&
id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
- /*
- * Extract the physical drive geometry for our use.
- * Note that we purposely do *not* update the bios info.
- * This way, programs that use it (like fdisk) will
- * still have the same logical view as the BIOS does,
- * which keeps the partition table from being screwed.
- *
- * An exception to this is the cylinder count,
- * which we reexamine later on to correct for 1024 limitations.
- */
drive->cyl = id->cur_cyls;
drive->head = id->cur_heads;
drive->sect = id->cur_sectors;
-
- /* check for word-swapped "capacity" field in id information */
- capacity = drive->cyl * drive->head * drive->sect;
- check = (id->cur_capacity0 << 16) | id->cur_capacity1;
- if (check == capacity) { /* was it swapped? */
- /* yes, bring it into little-endian order: */
- id->cur_capacity0 = (capacity >> 0) & 0xffff;
- id->cur_capacity1 = (capacity >> 16) & 0xffff;
- }
}
+
/* Use physical geometry if what we have still makes no sense */
if ((!drive->head || drive->head > 16) &&
id->heads && id->heads <= 16) {
if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) {
id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors)));
}
- drive->cyl = id->cur_cyls = id->cyls;
- drive->head = id->cur_heads = id->heads;
- drive->sect = id->cur_sectors = id->sectors;
+ drive->cyl = id->cyls;
+ drive->head = id->heads;
+ drive->sect = id->sectors;
}

/* calculate drive capacity, and select LBA if possible */
- capacity = idedisk_capacity (drive);
+ init_idedisk_capacity (drive);

/*
* if possible, give fdisk access to more of the drive,
* by correcting bios_cyls:
*/
+ capacity = idedisk_capacity (drive);
if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
- (!drive->forced_geom) && drive->bios_sect && drive->bios_head) {
+ (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
-#ifdef DEBUG
- printk("Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n",
- drive->id->cur_cyls,
- drive->id->cur_heads,
- drive->id->cur_sectors,
- drive->bios_cyl,
- drive->bios_head,
- drive->bios_sect);
-#endif
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
- }

#if 0 /* done instead for entire identify block in arch/ide.h stuff */
/* fix byte-ordering of buffer size field */
@@ -790,19 +763,6 @@
}
}
printk("\n");
-
- if (drive->select.b.lba) {
- if (*(int *)&id->cur_capacity0 < id->lba_capacity) {
-#ifdef DEBUG
- printk(" CurSects=%d, LBASects=%d, ",
- *(int *)&id->cur_capacity0, id->lba_capacity);
-#endif
- *(int *)&id->cur_capacity0 = id->lba_capacity;
-#ifdef DEBUG
- printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0);
-#endif
- }
- }

drive->mult_count = 0;
if (id->max_multsect) {
diff -u --recursive --new-file ../linux-2.3.14/linux/drivers/block/ide-floppy.c ./linux/drivers/block/ide-floppy.c
--- ../linux-2.3.14/linux/drivers/block/ide-floppy.c Sun Aug 22 00:09:06 1999
+++ ./linux/drivers/block/ide-floppy.c Sun Aug 22 21:49:23 1999
@@ -1498,7 +1498,7 @@
int major = HWIF(drive)->major;
int minor = drive->select.b.unit << PARTN_BITS;

- ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
diff -u --recursive --new-file ../linux-2.3.14/linux/drivers/block/ide-probe.c ./linux/drivers/block/ide-probe.c
--- ../linux-2.3.14/linux/drivers/block/ide-probe.c Sun Aug 22 00:09:06 1999
+++ ./linux/drivers/block/ide-probe.c Sun Aug 22 23:13:29 1999
@@ -372,6 +372,8 @@
* The only "perfect" way to handle this would be to modify the setup.[cS] code
* to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
* for us during initialization. I have the necessary docs -- any takers? -ml
+ * I did this, but it doesnt work - there is no reasonable way to find the
+ * correspondence between the BIOS numbering of the disks and the Linux numbering. -aeb
*/
static void probe_cmos_for_drives (ide_hwif_t *hwif)
{
diff -u --recursive --new-file ../linux-2.3.14/linux/drivers/block/ide.c ./linux/drivers/block/ide.c
--- ../linux-2.3.14/linux/drivers/block/ide.c Sun Aug 22 00:09:41 1999
+++ ./linux/drivers/block/ide.c Mon Aug 23 00:51:59 1999
@@ -2318,10 +2318,11 @@
case HDIO_GETGEO:
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
+ unsigned short bios_cyl = drive->bios_cyl; /* truncate */
if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
- if (put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
+ if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
(unsigned long *) &loc->start)) return -EFAULT;
return 0;
@@ -2340,7 +2341,8 @@
return -EINVAL;
if (drive->id == NULL)
return -ENOMSG;
- if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
+ if (copy_to_user((char *)arg, (char *)drive->id,
+ (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
return -EFAULT;
return 0;

@@ -3048,37 +3050,24 @@
if (!drive)
return 0;

- if (drive->forced_geom) {
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
- return 0;
+ if (xparm == -1) {
+ drive->remap_0_to_1 = 1;
+ if (drive->bios_head > 16) {
+ printk("[remap 0->1]");
+ return 0;
+ }
}

- if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) {
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
+ if (drive->forced_geom)
+ return 0;
+
+ if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
return 0; /* we already have a translation */
- }

printk("%s ", msg);

- if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) {
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
+ if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63))
return 0; /* small disk: no translation needed */
- }

if (drive->id) {
drive->cyl = drive->id->cyls;
@@ -3117,12 +3106,6 @@

drive->part[0].nr_sects = current_capacity(drive);
printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
return 1;
}

diff -u --recursive --new-file ../linux-2.3.14/linux/drivers/scsi/ide-scsi.c ./linux/drivers/scsi/ide-scsi.c
--- ../linux-2.3.14/linux/drivers/scsi/ide-scsi.c Tue Jul 13 15:12:09 1999
+++ ./linux/drivers/scsi/ide-scsi.c Sun Aug 22 21:49:36 1999
@@ -487,7 +487,7 @@
/*
* drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function
*/
- ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
ide_add_setting(drive, "transform", SETTING_RW, -1, -1, TYPE_INT, 0, 3, 1, 1, &scsi->transform, NULL);
diff -u --recursive --new-file ../linux-2.3.14/linux/include/linux/ide.h ./linux/include/linux/ide.h
--- ../linux-2.3.14/linux/include/linux/ide.h Sun Aug 22 00:09:35 1999
+++ ./linux/include/linux/ide.h Mon Aug 23 15:01:45 1999
@@ -282,8 +282,9 @@
byte sect; /* "real" sectors per track */
byte bios_head; /* BIOS/fdisk/LILO number of heads */
byte bios_sect; /* BIOS/fdisk/LILO sectors per track */
- unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
- unsigned short cyl; /* "real" number of cyls */
+ unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
+ unsigned int cyl; /* "real" number of cyls */
+ unsigned long capacity; /* total number of sectors */
unsigned int drive_data; /* for use by tuneproc/selectproc as needed */
void *hwif; /* actually (ide_hwif_t *) */
wait_queue_head_t wqueue; /* used to wait for drive in open() */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/