--- /dev/null
+/* MFM & IDE Hard Disk Device Driver for MMURTL.\r
+ This driver does not depend on the data stored in CMOS RAM for \r
+ drive geometry. Three routines determine number of sectors per track, \r
+ number of cylinders, and number of heads by actually trying to seek \r
+ and/or read them. This eliminates dependence on some system's proprietary\r
+ CMOS locations.\r
+*/\r
+\r
+#define U32 unsigned long\r
+#define S32 long\r
+#define U16 unsigned int\r
+#define S16 int\r
+#define U8 unsigned char\r
+#define S8 char\r
+\r
+/* MMURTL OS PROTOTYPES */\r
+\r
+extern far AllocExch(U32 *pExchRet);\r
+extern far U32 InitDevDr(U32 dDevNum,\r
+ S8 *pDCBs,\r
+ U32 nDevices,\r
+ U32 dfReplace);\r
+\r
+extern far U32 UnMaskIRQ(U32 IRQNum);\r
+extern far U32 MaskIRQ(U32 IRQNum);\r
+extern far U32 SetIRQVector(U32 IRQNum, S8 *pIRQ);\r
+extern far U32 EndOfIRQ(U32 IRQNum);\r
+extern far U32 SendMsg(U32 Exch, U32 msg1, U32 msg2);\r
+extern far U32 ISendMsg(U32 Exch, U32 msg1, U32 msg2);\r
+extern far U32 WaitMsg(U32 Exch, U32 *pMsgRet);\r
+extern far U32 CheckMsg(U32 Exch, U32 *pMsgRet);\r
+extern far U32 Alarm(U32 Exch, U32 count);\r
+extern far U32 KillAlarm(U32 Exch);\r
+extern far U32 Sleep(U32 count);\r
+extern far void MicroDelay(U32 us15count);\r
+extern far void OutByte(U8 Byte, U16 wPort);\r
+extern far void OutWord(U16 Word, U16 wPort);\r
+extern far U8 InByte(U16 wPort);\r
+extern far U16 InWord(U16 wPort);\r
+extern far U8 ReadCMOS(U16 Address);\r
+extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
+extern far InWords(U32 dPort, U8 *pDataIn, U32 dBytes);\r
+extern far OutWords(U32 dPort, U8 *pDataOut, U32 dBytes);\r
+\r
+/* Near External for troubleshooting */\r
+\r
+extern long xprintf(char *fmt, ...);\r
+\r
+\r
+/* LOCAL PROTOTYPES */\r
+\r
+U32 hdisk_setup(void);\r
+static void interrupt hdisk_isr(void); /* The HD interrupt function */\r
+static U32 hd_format_track(U32 dLBA, U32 dnBlocks);\r
+static void hd_reset(void);\r
+static U32 send_command(U8 parm);\r
+static U32 hd_wait (void);\r
+static U32 check_busy(void);\r
+static U32 hd_seek(U32 dLBA);\r
+static U32 hd_recal(U8 drive);\r
+static U32 hd_write(U32 dLBA, U32 dnBlocks, U8 *pDataOut);\r
+static U32 hd_read(U32 dLBA, U32 dnBlocks, U8 *pDataIn);\r
+static U32 hd_status(U8 LastCmd);\r
+static U32 setupseek(U32 dLBA, U32 nBlks);\r
+static U32 hd_init(U8 drive);\r
+static U32 ReadSector(U32 Cylinder, U32 HdSect, U8 *pDataRet);\r
+\r
+/* The following 3 calls are required in every MMURTL device driver */\r
+\r
+static U32 hddev_op(U32 dDevice,\r
+ U32 dOpNum,\r
+ U32 dLBA,\r
+ U32 dnBlocks,\r
+ U8 *pData);\r
+\r
+static U32 hddev_stat(U32 dDevice,\r
+ S8 * pStatRet,\r
+ U32 dStatusMax,\r
+ U32 *pdSatusRet);\r
+\r
+static U32 hddev_init(U32 dDevNum,\r
+ S8 *pInitData,\r
+ U32 sdInitData);\r
+\r
+/* LOCAL DEFINITIONS */\r
+\r
+#define ok 0\r
+\r
+/* Error Codes to return */\r
+\r
+#define ErcNoMsg 20\r
+#define ErcNotInstalled 504\r
+\r
+#define ErcBadBlock 651\r
+#define ErcAddrMark 652\r
+#define ErcBadECC 653\r
+#define ErcSectNotFound 654\r
+#define ErcNoDrive0 655\r
+#define ErcNotSupported 656\r
+#define ErcBadHDC 658\r
+#define ErcBadSeek 659\r
+#define ErcHDCTimeOut 660\r
+#define ErcOverRun 661\r
+#define ErcBadLBA 662\r
+#define ErcInvalidDrive 663\r
+#define ErcBadOp 664\r
+#define ErcBadRecal 665\r
+#define ErcSendHDC 666\r
+#define ErcNotReady 667\r
+#define ErcBadCmd 668\r
+#define ErcNeedsInit 669\r
+#define ErcTooManyBlks 670 /* The controller can only do 128 max */\r
+#define ErcZeroBlks 671 /* 0 Blocks not allowed for this cmd */\r
+#define ErcWriteFault 672 /* WriteFault bit set... bummer */\r
+\r
+#define ErcMissHDDInt 675\r
+\r
+#define ErcHDDMsgBogus 676\r
+#define ErcHDDIntMsg 677\r
+#define ErcHDDAlarmMsg 678\r
+\r
+/* Commands accepted by this HD driver */\r
+\r
+#define CmdNull 0\r
+#define CmdRead 1\r
+#define CmdWrite 2\r
+#define CmdVerify 3\r
+#define CmdFmtBlk 4\r
+#define CmdFmtTrk 5\r
+#define CmdSeekTrk 6\r
+#define CmdSetMedia 7 /* Not used unless mountable */\r
+#define CmdResetHdw 8 /* Used to reset controller hardware */\r
+\r
+/* CmdReadSect is the only device specific call in the IDE/MFM hard\r
+ disk device driver. This allows you to read ONE sector\r
+ specified by Cylinder, head and Sector number.\r
+ Cylinder is HiWord of dLBA in DeviceOp call,\r
+ Head is LoWord of dLBA in DeviceOp call, and\r
+ Sector number is LowWord in dnBlocks.\r
+*/\r
+\r
+#define CmdReadSect 256 /* only device specific call in HDD */\r
+\r
+/* HDC port definitions */\r
+\r
+#define HD_PORT 0x1f0\r
+\r
+/* When writing to the port+X (where X =):\r
+ 0 - write data (1F0h - 16 bit)\r
+ 1 - pre-comp (1F1h)\r
+ 2 - sector count (1F2h)\r
+ 3 - sector number (1F3h)\r
+ 4 - low cyl (1F4h)\r
+ 5 - high cyl (1F5h)\r
+ 6 - size/drive/head (1F6h)\r
+ 7 - command register (1F7h)\r
+\r
+When reading from the port+X (where X =):\r
+ 0 - read data (1F0h - 16 bit)\r
+ 1 - error register (1F1h)\r
+ 2 - sector count (1F2h)\r
+ 3 - sector number (1F3h)\r
+ 4 - low cyl (1F4h)\r
+ 5 - high cyl (1F5h)\r
+ 6 - size/drive/head (1F6h)\r
+ 7 - status register (1F7h)\r
+*/\r
+\r
+#define HD_REG_PORT 0x3f6\r
+\r
+/* This is a byte wide write only control port\r
+ that allows reset and defines some special\r
+ characteristics of the hard drives.\r
+ Bit Desc\r
+ 0 Not used\r
+ 1 Not used\r
+ 2 Reset Bit - Set, wait 50us, then Reset\r
+ 3 Mucho Heads Flag. Set = More than 8 heads\r
+ 4 Not used\r
+ 5 Not used\r
+ 6 Disable retries\r
+ 7 Disable retries (same as six, either one set)\r
+*/\r
+\r
+/* HDC Status Register Bit Masks (1F7h) */\r
+\r
+#define BUSY 0x80 /* busy.. can't talk now! */\r
+#define READY 0x40 /* Drive Ready */\r
+#define WRITE_FAULT 0x20 /* Bad news */\r
+#define SEEKOK 0x10 /* Seek Complete */\r
+#define DATA_REQ 0x08 /* Sector buffer needs servicing */\r
+#define CORRECTED 0x04 /* ECC corrected data was read */\r
+#define REV_INDEX 0x02 /* Set once each disk revolution */\r
+#define ERROR 0x01 /* data address mark not found */\r
+\r
+/* HDC Error Register Bit Masks (1F1h) */\r
+\r
+#define BAD_SECTOR 0x80 /* bad block */\r
+#define BAD_ECC 0x40 /* bad data ecc */\r
+#define BAD_IDMARK 0x10 /* id not found */\r
+#define BAD_CMD 0x04 /* aborted command */\r
+#define BAD_SEEK 0x02 /* trk 0 not found on recalibrate, or bad seek */\r
+#define BAD_ADDRESS 0x01 /* data address mark not found */\r
+\r
+\r
+/* HDC internal command bytes (HDC_Cmd[7]) */\r
+\r
+#define HDC_RECAL 0x10 /* 0001 0000 */\r
+#define HDC_READ 0x20 /* 0010 0000 */\r
+#define HDC_READ_LONG 0x22 /* 0010 0010 */\r
+#define HDC_WRITE 0x30 /* 0011 0000 */\r
+#define HDC_WRITE_LONG 0x32 /* 0011 0010 */\r
+#define HDC_VERIFY 0x40 /* 0100 0000 */\r
+#define HDC_FORMAT 0x50 /* 0101 0000 */\r
+#define HDC_SEEK 0x70 /* 0111 0000 */\r
+#define HDC_DIAG 0x90 /* 1001 0000 */\r
+#define HDC_SET_PARAMS 0x91 /* 1001 0001 */\r
+\r
+/* L O C A L D A T A */\r
+\r
+static U8 hd_Cmd[8]; /* For all 8 command bytes */\r
+\r
+static U8 fDataReq; /* Flag to indicate is fDataRequest is active */\r
+static U8 statbyte; /* From HDC status register last time it was read */\r
+\r
+static U8 hd_control; /* Current control byte value */\r
+static U8 hd_command; /* Current Command */\r
+static U8 hd_drive; /* Current Physical Drive, 0 or 1 */\r
+static U8 hd_head; /* Calculated from LBA - which head */\r
+static U8 hd_nsectors; /* Calculated from LBA - n sectors to read/write */\r
+static U8 hd_sector; /* Calculated from LBA - Starting sector */\r
+\r
+/* Current type drive 0 & 1 found in CMOS or Set by caller. */\r
+/* Current number of heads, cylinders, and sectors set by caller */\r
+\r
+static U8 hd0_type;\r
+static U8 hd0_heads;\r
+static U8 hd0_secpertrk;\r
+static U16 hd0_cyls;\r
+\r
+static U8 hd1_type;\r
+static U8 hd1_heads;\r
+static U8 hd1_secpertrk;\r
+static U16 hd1_cyls;\r
+\r
+#define sStatus 64\r
+\r
+static struct statstruct\r
+ {\r
+ U32 erc;\r
+ U32 blocks_done;\r
+ U32 BlocksMax;\r
+ U8 fNewMedia;\r
+ U8 type_now; /* current fdisk_table for drive selected */\r
+ U8 resvd0[2]; /* padding for DWord align */\r
+ U32 nCyl; /* total physical cylinders */\r
+ U32 nHead; /* total heads on device */\r
+ U32 nSectors; /* Sectors per track */\r
+ U32 nBPS; /* Number of bytes per sect. 32 bytes out to here.*/\r
+\r
+ U32 LastRecalErc0;\r
+ U32 LastSeekErc0;\r
+ U8 LastStatByte0;\r
+ U8 LastErcByte0;\r
+ U8 fIntOnReset; /* Interrupt was received on HDC_RESET */\r
+ U8 filler0;\r
+\r
+ U32 LastRecalErc1;\r
+ U32 LastSeekErc1;\r
+ U8 LastStatByte1;\r
+ U8 LastErcByte1;\r
+ U8 ResetStatByte; /* Status Byte immediately after RESET */\r
+ U8 filler1;\r
+\r
+ U32 resvd1[2]; /* out to 64 bytes */\r
+ };\r
+\r
+static struct statstruct hdstatus;\r
+static struct statstruct HDStatTmp;\r
+\r
+static struct dcbtype \r
+{\r
+ S8 Name[12];\r
+ S8 sbName;\r
+ S8 type;\r
+ S16 nBPB;\r
+ U32 last_erc;\r
+ U32 nBlocks;\r
+ S8 *pDevOp;\r
+ S8 *pDevInit;\r
+ S8 *pDevSt;\r
+ U8 fDevReent;\r
+ U8 fSingleUser;\r
+ S16 wJob;\r
+ U32 OS1;\r
+ U32 OS2;\r
+ U32 OS3;\r
+ U32 OS4;\r
+ U32 OS5;\r
+ U32 OS6;\r
+ };\r
+\r
+static struct dcbtype hdcb[2]; /* two HD device control blocks */\r
+\r
+/* Exch and msgs space for HD ISR */\r
+\r
+static U32 hd_exch;\r
+\r
+static U32 hd_msg;\r
+static U32 hd_msg2;\r
+\r
+static long HDDInt;\r
+\r
+/*======================================================*/\r
+/*=================== START OF CODE ====================*/\r
+/*======================================================*/\r
+\r
+/*********************************************************\r
+ This is called ONCE to initialize the HD Driver.\r
+*********************************************************/\r
+\r
+U32 hdisk_setup(void)\r
+{\r
+U32 erc;\r
+\r
+ /* first we set up the 2 DCBs in anticipation of calling InitDevDr */\r
+\r
+ hdcb[0].Name[0] = 'H';\r
+ hdcb[0].Name[1] = 'D';\r
+ hdcb[0].Name[2] = '0';\r
+ hdcb[0].sbName = 3;\r
+ hdcb[0].type = 1; /* Random */\r
+ hdcb[0].nBPB = 512;\r
+ hdcb[0].nBlocks = 524288; /* largest disk handled - 2Gb disks*/\r
+ hdcb[0].pDevOp = &hddev_op;\r
+ hdcb[0].pDevInit = &hddev_init;\r
+ hdcb[0].pDevSt = &hddev_stat;\r
+\r
+ hdcb[1].Name[0] = 'H';\r
+ hdcb[1].Name[1] = 'D';\r
+ hdcb[1].Name[2] = '1';\r
+ hdcb[1].sbName = 3;\r
+ hdcb[1].type = 1; /* Random */\r
+ hdcb[1].nBPB = 512;\r
+ hdcb[1].nBlocks = 524288; /* largest device handled - 2Gb disks*/\r
+ hdcb[1].pDevOp = &hddev_op;\r
+ hdcb[1].pDevInit = &hddev_init;\r
+ hdcb[1].pDevSt = &hddev_stat;\r
+\r
+/* These are defaulted to non zero values to\r
+ ensure we don't get a divide by zero during initial calculations\r
+ on the first read.\r
+*/\r
+\r
+ hd0_type = ReadCMOS(0x19); /* read this but don't use it */\r
+ hd0_heads = 16; /* Max */\r
+ hd0_secpertrk = 17; /* most common */\r
+ hd0_cyls = 1024; /* Max */\r
+\r
+ hd1_type = ReadCMOS(0x1A);\r
+ hd1_heads = 16;\r
+ hd1_secpertrk = 17;\r
+ hd1_cyls = 1024;\r
+\r
+ erc = AllocExch(&hd_exch); /* Exhange for HD Task to use */\r
+\r
+ SetIRQVector(14, &hdisk_isr);\r
+ UnMaskIRQ(14);\r
+\r
+/* Documentation lists the fixed disk types at CMOS 11h and 12h,\r
+ and also shows them at 19h and 1Ah. We don't actually read them\r
+ because they are not dependable. They vary from BIOS to BIOS.\r
+ We have to make this sucker work the hard way.\r
+*/\r
+\r
+/* Reset the HDC - hd_reset resets the controller (which controlls\r
+ both drives). We have to do it once, then try both physical drives.\r
+ If the second drive is not there, some controllers will lock-up\r
+ (the el-cheapos). In this case we have to reset it again so it\r
+ will work. It seems like a lot of work, but to make it function\r
+ with the widest range of IDE and MFM controllers this is the\r
+ only way I have found that works.\r
+*/\r
+\r
+ hd_reset(); /* no error is returned */\r
+\r
+/* Now we attempt to select and recal both drives.\r
+ The driver MUST be able to recal the first physical drive\r
+ or the Driver won't install.\r
+*/\r
+\r
+ erc = hd_recal(0); /* try to recal */\r
+ if (erc)\r
+ { /* try one more time! */\r
+ hd_reset();\r
+ erc = hd_recal(0); /* try to recal */\r
+ if (erc)\r
+ {\r
+ hdcb[0].last_erc = erc;\r
+ hd0_type = 0; /* Must not be a valid drive */\r
+ return(ErcNoDrive0);\r
+ }\r
+ }\r
+\r
+ /* if we got here, drive 0 looks OK and the controller is\r
+ functioning. Now we try drive 1 if type > 0.\r
+ */\r
+\r
+ if (hd1_type)\r
+ {\r
+ erc = hd_recal(1); /* try to recal if CMOS says it's there */\r
+ if (erc)\r
+ {\r
+ hdcb[1].last_erc = erc;\r
+ hd1_type = 0; /* Guess it's not a valid drive */\r
+\r
+ if (!erc)\r
+\r
+ /* We must redo drive 0 cause some cheap controllers lockup\r
+ on us if drive 1 is not there. They SHOULD simply return\r
+ a Bad Command bit set in the Error register, but they don't. */\r
+\r
+ hd_reset();\r
+ erc = hd_recal(0); /* recal drive 0 */\r
+ hdcb[0].last_erc = erc;\r
+ }\r
+ }\r
+\r
+ return(erc = InitDevDr(12, &hdcb, 2, 1));\r
+\r
+}\r
+\r
+/************************************************************\r
+ Reset the HD controller. This should only be called by\r
+ DeviceInit or hdisk_setup. This resets the controller\r
+ and reloads parameters for both drives (if present) and\r
+ attempts to recal them.\r
+*************************************************************/\r
+\r
+static void hd_reset(void)\r
+{\r
+U32 i;\r
+ UnMaskIRQ(14); /* enable the IRQ */\r
+ OutByte(4, HD_REG_PORT); /* reset the controller */\r
+ MicroDelay(4); /* Delay 60us */\r
+\r
+ /* bit 3 of HD_REG must be 1 for access to heads 8-15 */\r
+ /* Clear "MUCHO" heads bit, and clear the reset bit */\r
+\r
+ OutByte(hd_control & 0x0f, HD_REG_PORT);\r
+\r
+ Sleep(20); /* 200ms - seems some controllers are SLOW!! */\r
+ i = CheckMsg(hd_exch, &hd_msg); /* Eat Int if one came back */\r
+\r
+ hdstatus.ResetStatByte = statbyte; /* The ISR gets statbyte */\r
+\r
+ if (i) hdstatus.fIntOnReset = 1;\r
+ else hdstatus.fIntOnReset = 0;\r
+\r
+}\r
+\r
+/*************************************************************\r
+ The ISR is VERY simple. It just waits for an interrupt, gets\r
+ the single status byte from the controller (which clears the\r
+ interrupt condition) then sends an empty message to the\r
+ exchange where the HD Driver task will be waiting.\r
+ This tells the HD task currently running that it's got\r
+ some status to act on!\r
+****************************************************************/\r
+static void interrupt hdisk_isr(void)\r
+{\r
+ statbyte = InByte(HD_PORT+7);\r
+ HDDInt = 1;\r
+ ISendMsg(hd_exch, 0xfffffff0, 0xfffffff0);\r
+ EndOfIRQ(14);\r
+}\r
+\r
+/*************************************************************\r
+ This checks the HDC controller to see if it's busy so we can\r
+ send it commands or read the rest of the registers.\r
+ We will wait up to 3 seconds then error out.\r
+ The caller should call check_busy and check the error.\r
+ If it's 0 then the controller became ready in less than\r
+ 3 seconds. ErcNotReady will be returned otherwise.\r
+ It leaves the status byte in the global statbyte.\r
+****************************************************************/\r
+\r
+static U32 check_busy(void)\r
+{\r
+S16 count;\r
+\r
+ count = 0;\r
+ while (count++ < 60) \r
+ {\r
+ statbyte = InByte(HD_PORT+7);\r
+ if ((statbyte & BUSY) == 0) return(ok);\r
+ Sleep(5); /* 50ms shots */\r
+ }\r
+ return(ErcNotReady); /* controller out to lunch! */\r
+}\r
+\r
+/*************************************************************\r
+ This sends the SetParams command to the controller to set\r
+ up the drive geometry (nHeads, nSectors, etc.).\r
+****************************************************************/\r
+\r
+static U32 hd_init(U8 drive)\r
+{\r
+U32 erc;\r
+ /* set max heads, sectors and cylinders */\r
+ if (drive == 0) \r
+ { /* Drive 0 */\r
+ hd_Cmd[2] = hd0_secpertrk; /* sector count */\r
+ hd_Cmd[6] = (drive << 4) | ((hd0_heads-1) & 0x0f) | 0xa0; /* hds & drv */\r
+ }\r
+ else\r
+ { /* Drive 1 */\r
+ hd_Cmd[2] = hd1_secpertrk; /* sector count */\r
+ hd_Cmd[6] = (drive << 4) | ((hd1_heads-1) & 0x0f) | 0xa0; /* hds & drv */\r
+ }\r
+ hd_Cmd[1] = 0;\r
+ hd_Cmd[3] = 0;\r
+ hd_Cmd[4] = 0; /* cyl = 0 for init */\r
+ hd_Cmd[5] = 0; /* cyl = 0 for init */\r
+\r
+ erc = send_command(HDC_SET_PARAMS); /* Send the command */\r
+ erc = hd_wait(); /* wait for interrupt */\r
+ if (!erc)\r
+ erc = hd_status(HDC_SET_PARAMS);\r
+ return(erc);\r
+}\r
+\r
+/******************************************\r
+Wait for the hardware interrupt to occur.\r
+Time-out and return if no interrupt.\r
+********************************************/\r
+\r
+static U32 hd_wait(void)\r
+{\r
+U32 erc;\r
+\r
+ /* Set alarm for 3 seconds */\r
+\r
+ HDDInt = 0;\r
+ KillAlarm(hd_exch); /* kill any pending alarm */\r
+\r
+ erc = Alarm(hd_exch, 300); /* Set it up again */\r
+ if (erc)\r
+ return(erc); /* bad problem */\r
+\r
+ erc = WaitMsg(hd_exch, &hd_msg);\r
+\r
+ KillAlarm(hd_exch);\r
+\r
+ if (hd_msg != 0xfffffff0)\r
+ { /* HD interrupt sends fffffff0 */\r
+ if (HDDInt)\r
+ return(ErcMissHDDInt);\r
+ else\r
+ return(ErcHDCTimeOut); /* Alarm sends 0xffffffff */\r
+ }\r
+ else\r
+ {\r
+ KillAlarm(hd_exch);\r
+ return(ok);\r
+ }\r
+}\r
+\r
+/********************************************\r
+ Recalibrate the drive.\r
+*********************************************/\r
+\r
+static U32 hd_recal(U8 drive)\r
+{\r
+U32 erc;\r
+\r
+ hd_Cmd[6] = (drive << 4) | (hd_head & 0x0f) | 0xa0;\r
+ erc = send_command(HDC_RECAL);\r
+ if (!erc)\r
+ erc = hd_wait(); /* wait for interrupt */\r
+ if (!erc)\r
+ erc = hd_status(HDC_RECAL);\r
+ if (drive)\r
+ hdstatus.LastRecalErc1 = erc;\r
+ else\r
+ hdstatus.LastRecalErc0 = erc;\r
+ return(erc);\r
+}\r
+\r
+\r
+/********************************************\r
+ Send the command to the controller.\r
+ Clear the Echange of any left over\r
+ alarm or int messages before we\r
+ send a command.\r
+*********************************************/\r
+\r
+static U32 send_command(U8 Cmd)\r
+{\r
+U32 erc, msg[2];\r
+\r
+ while (CheckMsg(hd_exch, &msg) == 0); /* Empty it */\r
+\r
+ /* bit 3 of HD_REG must be 1 for access to heads 8-15 */\r
+ if (hd_head > 7)\r
+ {\r
+ hd_control |= 0x08;\r
+ OutByte(hd_control, HD_REG_PORT); /* set bit for head > 7 */\r
+ hd_control &= 0xf7;\r
+ }\r
+ erc = check_busy();\r
+ if (!erc) OutByte(hd_Cmd[1], HD_PORT+1);\r
+ if (!erc) erc = check_busy();\r
+ if (!erc) OutByte(hd_Cmd[2], HD_PORT+2);\r
+ if (!erc) erc = check_busy();\r
+ if (!erc) OutByte(hd_Cmd[3], HD_PORT+3);\r
+ if (!erc) erc = check_busy();\r
+ if (!erc) OutByte(hd_Cmd[4], HD_PORT+4);\r
+ if (!erc) erc = check_busy();\r
+ if (!erc) OutByte(hd_Cmd[5], HD_PORT+5);\r
+ if (!erc) erc = check_busy();\r
+ if (!erc) OutByte(hd_Cmd[6], HD_PORT+6);\r
+ if (!erc) erc = check_busy();\r
+ if (!erc) OutByte(Cmd, HD_PORT+7);\r
+ return(erc);\r
+}\r
+\r
+/*************************************************************\r
+ This sets up the cylinder, head and sector variables for all\r
+ commands that require them (read, write, verify, format, seek).\r
+ nBlks ca NOT be greater than the hardware can handle. For\r
+ IDE/MFM controllers this is 128 sectors.\r
+ The caculated values are placed in the proper command byte\r
+ in anticipation of the command being sent.\r
+*************************************************************/\r
+\r
+static U32 setupseek(U32 dLBA, U32 nBlks)\r
+{\r
+ U32 j;\r
+ U16 cyl;\r
+\r
+ if (nBlks > 256) return ErcTooManyBlks;\r
+ if (nBlks == 0) return ErcZeroBlks;\r
+\r
+ hd_nsectors = nBlks;\r
+ if (hd_nsectors == 256) hd_nsectors = 0; /* 0==256 for controller */\r
+\r
+ if (hd_drive == 0) \r
+ { /* drive 0 */\r
+\r
+ cyl = dLBA / (hd0_heads * hd0_secpertrk);\r
+ j = dLBA % (hd0_heads * hd0_secpertrk); /* remainder */\r
+\r
+ /* we now know what cylinder, calculate head and sector */\r
+\r
+ hd_head = j / hd0_secpertrk;\r
+ hd_sector = j % hd0_secpertrk + 1; /* sector number start at 1 !!! */\r
+\r
+ }\r
+ else\r
+ { /* drive 1 */\r
+\r
+ cyl = dLBA / (hd1_heads * hd1_secpertrk);\r
+ j = dLBA % (hd1_heads * hd1_secpertrk); /* remainder */\r
+\r
+ /* We now know what cylinder. Calculate head and sector */\r
+\r
+ hd_head = j / hd1_secpertrk;\r
+ hd_sector = j % hd1_secpertrk + 1; /* sector number start at 1 !!! */\r
+ }\r
+\r
+ hd_Cmd[2] = nBlks; /* How many sectors */\r
+ hd_Cmd[3] = hd_sector; /* Which sector to start on */\r
+ hd_Cmd[4] = cyl & 0xff; /* cylinder lobyte */\r
+ hd_Cmd[5] = (cyl >> 8) & 0xff; /* cylinder hibyte */\r
+ hd_Cmd[6] = (hd_drive << 4) | (hd_head & 0x0f) | 0xa0;\r
+\r
+ return ok;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ Move the head to the selected track (cylinder).\r
+*********************************************************/\r
+\r
+static U32 hd_seek(U32 dLBA)\r
+{\r
+U32 erc;\r
+\r
+ erc = setupseek(dLBA, 1); /* sets up for implied seek */\r
+ if (!erc) erc = send_command(HDC_SEEK); /* Not implied anymore... */\r
+ if (!erc) erc = hd_wait(); /* wait for interrupt */\r
+ if (!erc) erc = hd_status(HDC_SEEK);\r
+ hdstatus.LastSeekErc0 = erc;\r
+ return(erc);\r
+}\r
+\r
+\r
+/*******************************************************\r
+ Called to read status and errors from the controller\r
+ after an interrupt generated by a command we sent.\r
+ The error checking is based on the command that we sent.\r
+ This is done because certain bits in the status and error\r
+ registers are actually not errors, but simply indicate\r
+ status or indicate an action we must take next.\r
+ ZERO returned indicates no errors for the command status\r
+ we are checking.\r
+*********************************************************/\r
+\r
+static U32 hd_status(U8 LastCmd)\r
+{\r
+U32 erc;\r
+U8 statbyte, errbyte;\r
+\r
+ /* We shouldn't see the controller busy. After all,\r
+ he interrupted us with status.\r
+ */\r
+\r
+ erc = check_busy(); /* puts status byte into global StatByte */\r
+ if (!erc)\r
+ statbyte = InByte(HD_PORT+7);\r
+ else return(erc);\r
+\r
+ if (hd_drive)\r
+ hdstatus.LastStatByte1 = statbyte;\r
+ else\r
+ hdstatus.LastStatByte0 = statbyte;\r
+\r
+ if ((statbyte & ERROR) == 0)\r
+ { /* Error bit not set in status reg */\r
+ erc = ok; /* default */\r
+\r
+ switch (LastCmd)\r
+ {\r
+ case HDC_READ:\r
+ case HDC_READ_LONG:\r
+ case HDC_WRITE:\r
+ case HDC_WRITE_LONG:\r
+ case HDC_SEEK:\r
+ case HDC_RECAL:\r
+ if (statbyte & WRITE_FAULT) erc = ErcWriteFault;\r
+ else\r
+ if ((statbyte & SEEKOK) == 0) erc = ErcBadSeek;\r
+ break;\r
+ case HDC_SET_PARAMS:\r
+ case HDC_VERIFY:\r
+ case HDC_FORMAT:\r
+ case HDC_DIAG:\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ return(erc);\r
+ }\r
+ else\r
+ {\r
+ erc = check_busy();\r
+ if (!erc)\r
+ errbyte = InByte(HD_PORT+1);\r
+ else return(erc);\r
+\r
+ if (hd_drive)\r
+ hdstatus.LastErcByte1 = errbyte;\r
+ else\r
+ hdstatus.LastErcByte0 = errbyte;\r
+\r
+ if (errbyte & BAD_ADDRESS) erc = ErcAddrMark;\r
+ else if (errbyte & BAD_SEEK) erc = ErcBadSeek;\r
+ else if (errbyte & BAD_CMD) erc = ErcBadCmd;\r
+ else if (errbyte & BAD_IDMARK) erc = ErcSectNotFound;\r
+ else if (errbyte & BAD_ECC) erc = ErcBadECC;\r
+ else if (errbyte & BAD_SECTOR) erc = ErcBadBlock;\r
+ else erc = ErcBadHDC; /* no error bits found but should have been! */\r
+ }\r
+ return erc;\r
+}\r
+\r
+/*************************************************************\r
+ This is called for the DeviceOp code Read.\r
+ This reads 1 or more whole sectors from the calculated values\r
+ in hd_head, hd_sector, and hd_cyl\r
+*************************************************************/\r
+\r
+static U32 hd_read(U32 dLBA, U32 dnBlocks, U8 *pDataRet)\r
+{\r
+U32 erc, nleft, nBPS;\r
+\r
+ nBPS = hdcb[hd_drive].nBPB; /* From nBytesPerBlock in DCB */\r
+ nleft = dnBlocks;\r
+ erc = setupseek(dLBA, dnBlocks); /* sets up for implied seek */\r
+ if (!erc) erc = send_command(HDC_READ);\r
+\r
+ while ((nleft) && (!erc)) \r
+ {\r
+ erc = hd_wait(); /* wait for interrupt */\r
+ if (!erc)\r
+ erc = hd_status(HDC_READ);\r
+ if (!erc) /* && (statbyte & DATA_REQ)) */ \r
+ {\r
+ InWords(HD_PORT, pDataRet, nBPS);\r
+ pDataRet+=nBPS;\r
+ --nleft;\r
+ }\r
+ }\r
+ return(erc);\r
+}\r
+\r
+/*************************************************************\r
+ This is called for the DeviceOp code Write.\r
+ This writes 1 or more whole sectors from the calculated values\r
+ in hd_head, hd_sector, and hd_cyl\r
+*************************************************************/\r
+\r
+static U32 hd_write(U32 dLBA, U32 dnBlocks, U8 *pDataOut)\r
+{\r
+U32 erc, nSoFar, nBPS;\r
+\r
+ nBPS = hdcb[hd_drive].nBPB; /* From n BytesPerBlock in DCB */\r
+ nSoFar = 0;\r
+ erc = setupseek(dLBA, dnBlocks); /* sets up for implied seek */\r
+ erc = send_command(HDC_WRITE);\r
+ erc = check_busy(); /* No INT occurs for first sector of write */\r
+\r
+ if ((!erc) && (statbyte & DATA_REQ)) \r
+ {\r
+ OutWords(HD_PORT, pDataOut, nBPS);\r
+ pDataOut+=nBPS;\r
+ nSoFar++;\r
+ }\r
+\r
+ while ((nSoFar < dnBlocks ) && (erc==ok))\r
+ {\r
+ erc = hd_wait(); /* wait for interrupt */\r
+ if (erc==ok) erc = hd_status(HDC_WRITE);\r
+ if ((erc==ok) && (statbyte & DATA_REQ)) \r
+ {\r
+ OutWords(HD_PORT, pDataOut, nBPS);\r
+ pDataOut+=nBPS;\r
+ nSoFar++;\r
+ }\r
+ }\r
+ if (!erc) erc = hd_wait(); /* wait for final interrupt */\r
+ if (!erc) erc = hd_status(HDC_WRITE);\r
+\r
+ return(erc);\r
+}\r
+\r
+\r
+/*************************************************************\r
+ This formats the track beginning at the block address given\r
+ in dLBA. dLBA must always be a multiple of the number of\r
+ sectors per track minus 1 for the disk type.\r
+*************************************************************/\r
+\r
+static U32 hd_format_track(U32 dLBA, U32 dnBlocks)\r
+{\r
+U32 erc;\r
+ erc = setupseek(dLBA, dnBlocks); /* sets up for implied seek */\r
+ erc = send_command(HDC_FORMAT);\r
+ erc = hd_wait(); /* wait for interrupt */\r
+ if (erc==ok)\r
+ erc = hd_status(HDC_FORMAT);\r
+ return(erc);\r
+}\r
+\r
+/******************************************************************\r
+ ReadSector is the only device specific call in the IDE/MFM hard\r
+ disk device driver. This allows you to read ONE sector\r
+ specified by Cylinder, head and Sector number.\r
+ Cylinder is LoWord of dLBA in DeviceOp call,\r
+ Head is LoWord of dnBlocks in DeviceOp call, and\r
+ Sector number is HiWord in dnBlocks.\r
+*******************************************************************/\r
+\r
+static U32 ReadSector(U32 Cylinder, U32 HdSect, U8 *pDataRet)\r
+{\r
+U32 erc;\r
+U16 cyl;\r
+\r
+ cyl = Cylinder;\r
+ hd_head = HdSect & 0xffff;\r
+ hd_sector = (HdSect >> 16) & 0xffff;\r
+\r
+/* For testing\r
+ xprintf("\r\nCYL %d, HD %d, SEC %d\r\n", cyl, hd_head, hd_sector);\r
+*/\r
+\r
+ hd_Cmd[2] = 1; /* How many sectors */\r
+ hd_Cmd[3] = hd_sector; /* Which sector to start on */\r
+ hd_Cmd[4] = cyl & 0xff; /* cylinder lobyte */\r
+ hd_Cmd[5] = (cyl >> 8) & 0xff; /* cylinder hibyte */\r
+ hd_Cmd[6] = (hd_drive << 4) | (hd_head & 0x0f) | 0xa0;\r
+\r
+ erc = send_command(HDC_READ);\r
+ erc = hd_wait(); /* wait for interrupt */\r
+ if (!erc) erc = hd_status(HDC_READ);\r
+ if (!erc)\r
+ InWords(HD_PORT, pDataRet, 512);\r
+ return(erc);\r
+}\r
+\r
+/***************************************************************************\r
+Now begins the PUBLIC routines that are used for all DEVICE DRIVERS\r
+*/\r
+\r
+/******************************************\r
+Called for all device operations. This\r
+assigns physical device from logical number\r
+that outside callers use. For Hard disk,\r
+12=0 and 13=1. This will check to make sure a\r
+drive type is assigned and check to see if \r
+they are going to exceed max logical blocks.\r
+*******************************************/\r
+\r
+static U32 hddev_op(U32 dDevice,\r
+ U32 dOpNum,\r
+ U32 dLBA,\r
+ U32 dnBlocks,\r
+ U8 *pData)\r
+{\r
+U32 erc;\r
+\r
+ hdstatus.blocks_done = 0; /* Reset values in Status record */\r
+\r
+ erc = 0;\r
+\r
+ /* Set drive internal drive number */\r
+\r
+ if (dDevice == 12)\r
+ hd_drive = 0;\r
+ else\r
+ hd_drive = 1;\r
+\r
+ /* Check to see if we have a leftover interrupt message from last\r
+ command. If so then we eat it (and do nothing) */\r
+\r
+ CheckMsg(hd_exch, &hd_msg); /* Ignore error */\r
+\r
+ if (hd_drive==0)\r
+ {\r
+ if (hd0_type==0)\r
+ erc = ErcInvalidDrive;\r
+ }\r
+ else\r
+ {\r
+ if (hd1_type==0)\r
+ erc = ErcInvalidDrive;\r
+ }\r
+\r
+ /* make sure they don't exceed max blocks */\r
+\r
+ if (!erc)\r
+ if (dLBA > hdcb[hd_drive].nBlocks) erc = ErcBadLBA;\r
+\r
+ if (!erc)\r
+ {\r
+\r
+ switch(dOpNum)\r
+ {\r
+ case(CmdNull):\r
+ erc = ok; /* Null Command */\r
+ break;\r
+ case(CmdRead): /* Read */\r
+ erc = hd_read(dLBA, dnBlocks, pData);\r
+ break;\r
+ case(CmdWrite): /* Write */\r
+ erc = hd_write(dLBA, dnBlocks, pData);\r
+ break;\r
+ case(CmdVerify): /* Verify */\r
+ erc = ErcNotSupported;\r
+\r
+ /* hd_verify is not supported in this version of the driver */\r
+ /* erc = hd_verify(dLBA, dnBlocks, pData); */\r
+ break;\r
+ case(CmdSeekTrk): /* Seek Track */\r
+ erc = hd_seek(dLBA);\r
+ break;\r
+ case(CmdFmtTrk): /* Format Track */\r
+ erc = hd_format_track(dLBA, dnBlocks);\r
+ break;\r
+ case(CmdResetHdw): /* Reset Ctrlr */\r
+ hd_reset();\r
+ erc = 0;\r
+ break;\r
+ case(CmdReadSect): /* Read Sector(s) */\r
+ erc = ReadSector(dLBA, dnBlocks, pData);\r
+ break;\r
+ default:\r
+ erc = ErcBadOp;\r
+ break;\r
+ }\r
+ }\r
+ hdcb[hd_drive].last_erc = erc; /* update DCB erc */\r
+ return(erc);\r
+}\r
+\r
+/******************************************\r
+Called for indepth status report on ctrlr\r
+and drive specified. Returns 64 byte block\r
+of data including current drive geometery.\r
+This is called by the PUBLIC call DeviceStat!\r
+*******************************************/\r
+\r
+static U32 hddev_stat(U32 dDevice,\r
+ S8 * pStatRet,\r
+ U32 dStatusMax,\r
+ U32 *pdStatusRet)\r
+{\r
+U32 i;\r
+\r
+ /* Set status for proper device */\r
+\r
+ if (dDevice == 12)\r
+ {\r
+ hdstatus.erc = hdcb[0].last_erc;\r
+ hdstatus.type_now = hd0_type;\r
+ hdstatus.nCyl = hd0_cyls;\r
+ hdstatus.nHead = hd0_heads;\r
+ hdstatus.nSectors = hd0_secpertrk;\r
+ hdstatus.nBPS = hdcb[0].nBPB;\r
+ }\r
+ else \r
+ {\r
+ hdstatus.erc = hdcb[1].last_erc;\r
+ hdstatus.type_now = hd1_type;\r
+ hdstatus.nCyl = hd1_cyls;\r
+ hdstatus.nHead = hd1_heads;\r
+ hdstatus.nSectors = hd1_secpertrk;\r
+ hdstatus.nBPS = hdcb[1].nBPB;\r
+ }\r
+\r
+ /* Calculate size of status to return. Return no more than asked for! */\r
+\r
+ if (dStatusMax <= sStatus) i = dStatusMax;\r
+ else i = sStatus;\r
+\r
+ CopyData(&hdstatus, pStatRet, i); /* copy to their status block */\r
+\r
+ *pdStatusRet = i; /* tell em how much it was */\r
+\r
+ return ok;\r
+}\r
+\r
+/******************************************\r
+Called to reset the hard disk controller\r
+and set drive parameters. The Initdata\r
+is a copy of the 64 byte status block\r
+that is read from status. The caller\r
+normally reads the block (DeviceStat),\r
+makes changes to certain fields and\r
+calls DeviceInit pointing to the block\r
+for the changes to take effect.\r
+This should ONLY be called once for each HD\r
+to set it's parameters before it is used\r
+the first time after the driver is loaded,\r
+or after a fatal error is received that\r
+indicates the controller may need to be\r
+reset (multiple timeouts etc.).\r
+The DCB values are updated if this is\r
+successful.\r
+This is called by the PUBLIC call DeviceInit.\r
+*******************************************/\r
+\r
+static U32 hddev_init(U32 dDevice,\r
+ S8 *pInitData,\r
+ U32 sdInitData)\r
+\r
+{\r
+U32 erc, i;\r
+\r
+ erc = 0;\r
+\r
+ /* Read the init status block in */\r
+\r
+ if (sdInitData > sStatus) i = sStatus; /* no more than 64 bytes! */\r
+ else i = sdInitData;\r
+\r
+ CopyData(pInitData, &HDStatTmp, i); /* copy in their init data */\r
+\r
+ /* Set internal drive number */\r
+ if (dDevice == 12) hd_drive=0;\r
+ else hd_drive = 1;\r
+\r
+ if (hd_drive==0)\r
+ {\r
+ hd0_type = HDStatTmp.type_now;\r
+ if (hd0_type) \r
+ {\r
+ hd0_cyls = HDStatTmp.nCyl;\r
+ hd0_heads = HDStatTmp.nHead;\r
+ hd0_secpertrk = HDStatTmp.nSectors;\r
+ }\r
+ else erc = ErcInvalidDrive;\r
+ }\r
+ else \r
+ {\r
+ hd1_type = HDStatTmp.type_now;\r
+ if (hd1_type) \r
+ {\r
+ hd1_cyls = HDStatTmp.nCyl;\r
+ hd1_heads = HDStatTmp.nHead;\r
+ hd1_secpertrk = HDStatTmp.nSectors;\r
+ }\r
+ else erc = ErcInvalidDrive;\r
+ }\r
+\r
+/* If no error, initialize it and recal */\r
+\r
+ if (!erc) erc = hd_init(hd_drive);\r
+ if (!erc) erc = hd_recal(hd_drive);\r
+\r
+/* If no error, update corresponding DCB values */\r
+\r
+ if (!erc) \r
+ {\r
+ hdcb[hd_drive].nBPB = HDStatTmp.nBPS;\r
+ hdcb[hd_drive].last_erc = 0;\r
+ hdcb[hd_drive].nBlocks =\r
+ HDStatTmp.nCyl * HDStatTmp.nSectors * HDStatTmp.nHead;\r
+ }\r
+\r
+ hdcb[hd_drive].last_erc = erc; /* update DCB erc */\r
+\r
+ return(erc);\r
+}\r
+\r
+/*=========== THE END =========================================*/\r