/*------------------------------------------------------+ | | | Name: | | ide_conf | | | | Usage: | | ide_conf [drive number] | | | | Description: | | Displays IDE drive configuration under MSDOS. | | Optional drive parameter selects drive for query. | | Legal values are 0..3 where 0 and 1 are drives 0 | | and 1 attached to a primary controller while 2 and | | 3 are drives 0 and 1 attached to a controller at | | the alternate controller address (0x170 - has not | | been tested). If omitted, drive 0 is checked. | | | | Author: | | Frank P. MacLachlan | | | | Date: | | 18-Oct-92 | | | | Compilation using MSC 6.0: | | cl ide_conf.c | | | +------------------------------------------------------*/ #include #include #include "wddefs.h" #define TPS 18 /* ticks/second (really 18.2) */ typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; /* ** The following structure corresponds to the ROM BIOS ** disk parameter table: */ #pragma pack(1) typedef struct parms { uint ncyls; uchar nheads; uint res0; uint wpc; uchar ebl; uchar ctlb; uchar res1[3]; uint lzc; uchar nsecs; uchar res2; } BIOS_PARMS; #pragma pack() /* ** The following structure corresponds to the values as returned ** by the ESDI/IDE identify drive command. */ typedef struct { ushort gcfg; /* general configuration */ ushort fcyl; /* number of fixed cyls */ ushort rcyl; /* number of removable cyls */ ushort hds; /* number of heads */ ushort bpt; /* bytes/track */ ushort bps; /* bytes/sector */ ushort spt; /* sectors/track */ ushort isg; /* inter-sector gap */ ushort plo; /* PLO sync field */ ushort vsw; /* vendor status word */ } ID_PARMS; /* ** Used to manipulate identify drive data. */ union { ID_PARMS id_parms; unsigned short id_data[256]; } id_buf; /* pointer to low word of system time value at 0040:006c */ ushort far *TimePtr = (ushort far *)0x0040006cL; int drive = 0; /* drive number */ int wd_base = WDP_BASE0; /* base I/O port addr of HDC */ char *my_name; void reboot(void); void chk_drv(void); void dsp_bios_cfg(void); void dsp_hw_cfg(void); void reset_hdc(void); void delay(int); void sel_drv(uint, uint); void bsy_chk(void); void usage(void); void io_delay(void); void test_hdc(uint, uint, uint); /*----------------------------------------------- | | Main program | +----------------------------------------------*/ main(argc, argv) int argc; char *argv[]; { my_name = argv[0]; if (argc > 1) { if ( sscanf(argv[1], "%d", &drive) < 1 || drive < 0 || drive > 3) usage(); if (drive > 1) { drive &= 1; wd_base = WDP_BASE1; } } chk_drv(); /* make sure controller, drive present */ dsp_bios_cfg(); /* display BIOS drive config */ dsp_hw_cfg(); /* display H/W drive config */ reset_hdc(); /* reset the controller */ dsp_hw_cfg(); /* display H/W drive config */ reboot(); /* ask user to reboot system */ } /*----------------------------------------------- | | Ask user to reboot system. | +----------------------------------------------*/ void reboot() { fprintf(stderr, "Please reboot system!\n"); while (1) ; } /*----------------------------------------------- | | Check if controller, drive present. | +----------------------------------------------*/ void chk_drv() { #define MASK (WDM_BSY|WDM_RDY|WDM_WTF|WDM_SKC) #define EXP (WDM_RDY|WDM_SKC) test_hdc(wd_base+WDP_CL, 0x55, 0xff); test_hdc(wd_base+WDP_CL, 0xaa, 0xff); test_hdc(wd_base+WDP_CH, 0x55, 0x01); test_hdc(wd_base+WDP_CH, 0xaa, 0x01); sel_drv(drive, 0); if ((inp(wd_base+WDP_CSR) & MASK) != EXP) { fprintf(stderr, "Drive missing or status hosed\n"); exit(2); } } /*----------------------------------------------- | | Display BIOS configuration info for this drive. | +----------------------------------------------*/ void dsp_bios_cfg() { /* * Array of pointers to HD parameter table entries indexed * by drive number */ static BIOS_PARMS far * far *dskptr[2] = { (BIOS_PARMS far * far *)(4*0x41L), (BIOS_PARMS far * far *)(4*0x46L) }; BIOS_PARMS far *bpp = *dskptr[drive]; printf("BIOS reports drive has %u cyls, %u hds, %u secs/trk\n\n", bpp->ncyls, bpp->nheads, bpp->nsecs); } /*----------------------------------------------- | | Ask the drive to identify itself. | +----------------------------------------------*/ void dsp_hw_cfg() { int n; /* select drive */ sel_drv(drive, 0); /* Issue Get Drive Parameters cmd */ outp(wd_base+WDP_CSR, WDC_GDP); /* Wait for Busy status to be asserted */ for (n = 1000; n > 0 && (inp(wd_base+WDP_CSR) & WDM_BSY) == 0; n--) ; /* Now wait for Busy status to be negated */ bsy_chk(); /* Print error msg and bail out if error */ if ((n = inp(wd_base+WDP_CSR)) & WDM_HER) { fprintf(stderr, "Identify drive cmd failed: csr=0x%02x, err=0x%02x\n", n, inp(wd_base+WDP_ERR) ); reboot(); } /* Wait for Data request to be asserted */ while ((inp(wd_base+WDP_CSR) & WDM_DRQ) == 0) ; /* Input parameter info from controller */ for (n = 0; n < sizeof(id_buf.id_data)/sizeof(id_buf.id_data[0]); n++) id_buf.id_data[n] = inpw(wd_base+WDP_DAT); /* Print parameter info */ printf("Controller reports drive has %u cyls, %u hds, %u secs/trk\n\n", id_buf.id_parms.fcyl, id_buf.id_parms.hds, id_buf.id_parms.spt); } /*----------------------------------------------- | | Reset hard disk controller (or drive if IDE). | +----------------------------------------------*/ void reset_hdc() { printf("Resetting controller/drive\n\n"); outp(wd_base+WDP_DCR, WDM_RSTGO); delay(2); outp(wd_base+WDP_DCR, WDM_RSTNO|WDM_HS3); delay(1*TPS); } /*----------------------------------------------- | | Delay n system timer ticks. 18.2 ticks = 1 sec. | +----------------------------------------------*/ void delay(n) int n; { int cur_lsb = *TimePtr & 1; while (n-- > 0) { while ((*TimePtr & 1) == cur_lsb) ; cur_lsb = 1 - cur_lsb; } } /*----------------------------------------------- | | Select drive. | +----------------------------------------------*/ void sel_drv(drv, head) uint drv, head; { outp(wd_base+WDP_SDH, WDM_ECC|WDM_512|(drv<<4)|head); bsy_chk(); } /*----------------------------------------------- | | Wait for Busy status to be reset. | +----------------------------------------------*/ void bsy_chk() { while (inp(wd_base+WDP_CSR) & WDM_BSY) ; } /*----------------------------------------------- | | Display usage message, abort. | +----------------------------------------------*/ void usage() { static char msg[] = "Usage: %s [drive]\n" "Drive may be 0..3 (2,3 => drives on alternate controller)\n" "Default is 0\n"; fprintf(stderr, msg, my_name); exit(2); } /*----------------------------------------------- | | Delay a bit between back to back I/O operations. | The delay results from the call/return overhead. | +----------------------------------------------*/ void io_delay() { } /*----------------------------------------------- | | Check if hard disk controller is present and | probably register compatible with the standard | AT controller. Abort if not. | +----------------------------------------------*/ void test_hdc(reg, pat, msk) uint reg; uint pat; uint msk; { outp(reg, pat); io_delay(); io_delay(); io_delay(); io_delay(); io_delay(); io_delay(); if ((inp(reg)&msk) != (pat&msk)) { fprintf(stderr, "Non-compatible or missing Hard Disk Controller!\n"); exit(2); } }