]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1994-12-05 19:44:06
authorRichard Burgess <>
Mon, 5 Dec 1994 19:44:06 +0000 (19:44 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:47 +0000 (14:03 +0000)
ossource/hardide.c [new file with mode: 0644]

diff --git a/ossource/hardide.c b/ossource/hardide.c
new file mode 100644 (file)
index 0000000..3a64c17
--- /dev/null
@@ -0,0 +1,1127 @@
+/* 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