]> pd.if.org Git - mmurtl/blob - ossource/hardide.c
autocommit for file dated 2003-12-29 17:36:54
[mmurtl] / ossource / hardide.c
1 /* MFM & IDE Hard Disk Device Driver for MMURTL.\r
2    This driver does not depend on the data stored in CMOS RAM for \r
3    drive geometry.  Three routines determine number of sectors per track, \r
4    number of cylinders, and number of heads by actually trying to seek \r
5    and/or read them. This eliminates dependence on some system's proprietary\r
6    CMOS locations.\r
7 */\r
8 \r
9 #define U32 unsigned long\r
10 #define S32 long\r
11 #define U16 unsigned int\r
12 #define S16 int\r
13 #define U8 unsigned char\r
14 #define S8 char\r
15 \r
16 /* MMURTL OS PROTOTYPES */\r
17 \r
18 extern far AllocExch(U32  *pExchRet);\r
19 extern far U32  InitDevDr(U32  dDevNum,\r
20                                           S8  *pDCBs,\r
21                                                   U32  nDevices,\r
22                                                   U32  dfReplace);\r
23 \r
24 extern far U32  UnMaskIRQ(U32  IRQNum);\r
25 extern far U32  MaskIRQ(U32  IRQNum);\r
26 extern far U32  SetIRQVector(U32  IRQNum, S8  *pIRQ);\r
27 extern far U32  EndOfIRQ(U32  IRQNum);\r
28 extern far U32  SendMsg(U32  Exch, U32  msg1, U32  msg2);\r
29 extern far U32  ISendMsg(U32  Exch, U32  msg1, U32  msg2);\r
30 extern far U32  WaitMsg(U32  Exch, U32  *pMsgRet);\r
31 extern far U32  CheckMsg(U32  Exch, U32  *pMsgRet);\r
32 extern far U32  Alarm(U32  Exch, U32  count);\r
33 extern far U32  KillAlarm(U32  Exch);\r
34 extern far U32  Sleep(U32  count);\r
35 extern far void MicroDelay(U32  us15count);\r
36 extern far void OutByte(U8 Byte, U16 wPort);\r
37 extern far void OutWord(U16 Word, U16 wPort);\r
38 extern far U8 InByte(U16 wPort);\r
39 extern far U16 InWord(U16 wPort);\r
40 extern far U8 ReadCMOS(U16 Address);\r
41 extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
42 extern far InWords(U32 dPort, U8 *pDataIn, U32 dBytes);\r
43 extern far OutWords(U32 dPort, U8 *pDataOut, U32 dBytes);\r
44 \r
45 /* Near External for troubleshooting */\r
46 \r
47 extern long xprintf(char *fmt, ...);\r
48 \r
49 \r
50 /* LOCAL PROTOTYPES */\r
51 \r
52 U32  hdisk_setup(void);\r
53 static void interrupt hdisk_isr(void);  /* The HD interrupt function */\r
54 static U32  hd_format_track(U32 dLBA, U32 dnBlocks);\r
55 static void hd_reset(void);\r
56 static U32  send_command(U8  parm);\r
57 static U32  hd_wait (void);\r
58 static U32  check_busy(void);\r
59 static U32  hd_seek(U32 dLBA);\r
60 static U32  hd_recal(U8 drive);\r
61 static U32  hd_write(U32 dLBA, U32 dnBlocks, U8 *pDataOut);\r
62 static U32  hd_read(U32 dLBA, U32 dnBlocks, U8 *pDataIn);\r
63 static U32  hd_status(U8 LastCmd);\r
64 static U32  setupseek(U32 dLBA, U32 nBlks);\r
65 static U32  hd_init(U8 drive);\r
66 static U32 ReadSector(U32 Cylinder, U32 HdSect, U8 *pDataRet);\r
67 \r
68 /* The following 3 calls are required in every MMURTL device driver */\r
69 \r
70 static U32  hddev_op(U32  dDevice,\r
71                     U32  dOpNum,\r
72                     U32  dLBA,\r
73                     U32  dnBlocks,\r
74                     U8  *pData);\r
75 \r
76 static U32  hddev_stat(U32  dDevice,\r
77                           S8 * pStatRet,\r
78                           U32  dStatusMax,\r
79                           U32  *pdSatusRet);\r
80 \r
81 static U32  hddev_init(U32  dDevNum,\r
82                           S8  *pInitData,\r
83                           U32   sdInitData);\r
84 \r
85 /* LOCAL DEFINITIONS */\r
86 \r
87 #define ok 0\r
88 \r
89 /* Error Codes to return */\r
90 \r
91 #define ErcNoMsg                20\r
92 #define ErcNotInstalled 504\r
93 \r
94 #define ErcBadBlock             651\r
95 #define ErcAddrMark             652\r
96 #define ErcBadECC               653\r
97 #define ErcSectNotFound 654\r
98 #define ErcNoDrive0             655\r
99 #define ErcNotSupported 656\r
100 #define ErcBadHDC               658\r
101 #define ErcBadSeek              659\r
102 #define ErcHDCTimeOut   660\r
103 #define ErcOverRun              661\r
104 #define ErcBadLBA               662\r
105 #define ErcInvalidDrive 663\r
106 #define ErcBadOp                664\r
107 #define ErcBadRecal             665\r
108 #define ErcSendHDC              666\r
109 #define ErcNotReady             667\r
110 #define ErcBadCmd               668\r
111 #define ErcNeedsInit    669\r
112 #define ErcTooManyBlks  670             /* The controller can only do 128 max */\r
113 #define ErcZeroBlks             671             /* 0 Blocks not allowed for this cmd */\r
114 #define ErcWriteFault   672             /* WriteFault bit set... bummer */\r
115 \r
116 #define ErcMissHDDInt   675\r
117 \r
118 #define ErcHDDMsgBogus  676\r
119 #define ErcHDDIntMsg    677\r
120 #define ErcHDDAlarmMsg  678\r
121 \r
122 /* Commands accepted by this HD driver */\r
123 \r
124 #define CmdNull     0\r
125 #define CmdRead     1\r
126 #define CmdWrite    2\r
127 #define CmdVerify   3\r
128 #define CmdFmtBlk   4\r
129 #define CmdFmtTrk   5\r
130 #define CmdSeekTrk  6\r
131 #define CmdSetMedia 7   /* Not used unless mountable */\r
132 #define CmdResetHdw 8   /* Used to reset controller hardware */\r
133 \r
134 /* CmdReadSect is the only device specific call in the IDE/MFM hard\r
135    disk device driver.  This allows you to read ONE sector\r
136    specified by Cylinder, head and Sector number.\r
137    Cylinder is HiWord of dLBA in DeviceOp call,\r
138    Head is LoWord of dLBA in DeviceOp call, and\r
139    Sector number is LowWord in dnBlocks.\r
140 */\r
141 \r
142 #define CmdReadSect 256 /* only device specific call in HDD */\r
143 \r
144 /* HDC port definitions */\r
145 \r
146 #define HD_PORT 0x1f0\r
147 \r
148 /* When writing to the port+X (where X =):\r
149          0 - write data       (1F0h - 16 bit)\r
150          1 - pre-comp         (1F1h)\r
151          2 - sector count     (1F2h)\r
152          3 - sector number    (1F3h)\r
153          4 - low cyl          (1F4h)\r
154          5 - high cyl         (1F5h)\r
155          6 - size/drive/head  (1F6h)\r
156          7 - command register (1F7h)\r
157 \r
158 When reading from the port+X (where X =):\r
159          0 - read data       (1F0h - 16 bit)\r
160          1 - error register  (1F1h)\r
161          2 - sector count    (1F2h)\r
162          3 - sector number   (1F3h)\r
163          4 - low cyl         (1F4h)\r
164          5 - high cyl        (1F5h)\r
165          6 - size/drive/head (1F6h)\r
166          7 - status register (1F7h)\r
167 */\r
168 \r
169 #define HD_REG_PORT 0x3f6\r
170 \r
171 /* This is a byte wide write only control port\r
172    that allows reset and defines some special\r
173    characteristics of the hard drives.\r
174         Bit             Desc\r
175         0               Not used\r
176         1               Not used\r
177         2               Reset Bit - Set, wait 50us, then Reset\r
178         3               Mucho Heads Flag. Set = More than 8 heads\r
179         4               Not used\r
180         5               Not used\r
181         6               Disable retries\r
182         7               Disable retries (same as six, either one set)\r
183 */\r
184 \r
185 /* HDC Status Register Bit Masks (1F7h) */\r
186 \r
187 #define BUSY        0x80  /* busy.. can't talk now! */\r
188 #define READY       0x40  /* Drive Ready  */\r
189 #define WRITE_FAULT 0x20  /* Bad news */\r
190 #define SEEKOK      0x10  /* Seek Complete */\r
191 #define DATA_REQ    0x08  /* Sector buffer needs servicing */\r
192 #define CORRECTED   0x04  /* ECC corrected data was read */\r
193 #define REV_INDEX   0x02  /* Set once each disk revolution */\r
194 #define ERROR       0x01  /* data address mark not found */\r
195 \r
196 /* HDC Error Register Bit Masks (1F1h) */\r
197 \r
198 #define BAD_SECTOR  0x80  /* bad block */\r
199 #define BAD_ECC     0x40  /* bad data ecc */\r
200 #define BAD_IDMARK  0x10  /* id not found */\r
201 #define BAD_CMD     0x04  /* aborted command */\r
202 #define BAD_SEEK    0x02  /* trk 0 not found on recalibrate, or bad seek */\r
203 #define BAD_ADDRESS 0x01  /* data address mark not found */\r
204 \r
205 \r
206 /* HDC internal command bytes (HDC_Cmd[7]) */\r
207 \r
208 #define HDC_RECAL      0x10       /* 0001 0000 */\r
209 #define HDC_READ       0x20   /* 0010 0000 */\r
210 #define HDC_READ_LONG  0x22   /* 0010 0010 */\r
211 #define HDC_WRITE      0x30   /* 0011 0000 */\r
212 #define HDC_WRITE_LONG 0x32   /* 0011 0010 */\r
213 #define HDC_VERIFY     0x40   /* 0100 0000 */\r
214 #define HDC_FORMAT     0x50   /* 0101 0000 */\r
215 #define HDC_SEEK       0x70   /* 0111 0000 */\r
216 #define HDC_DIAG       0x90   /* 1001 0000 */\r
217 #define HDC_SET_PARAMS 0x91   /* 1001 0001 */\r
218 \r
219 /* L O C A L   D A T A  */\r
220 \r
221 static U8  hd_Cmd[8];           /* For all 8 command bytes */\r
222 \r
223 static U8  fDataReq;            /* Flag to indicate is fDataRequest is active */\r
224 static U8  statbyte;            /* From HDC status register last time it was read */\r
225 \r
226 static U8  hd_control;          /* Current control byte value */\r
227 static U8  hd_command;          /* Current Command */\r
228 static U8  hd_drive;            /* Current Physical Drive, 0 or 1 */\r
229 static U8  hd_head;             /* Calculated from LBA - which head */\r
230 static U8  hd_nsectors; /* Calculated from LBA - n sectors to read/write */\r
231 static U8  hd_sector;           /* Calculated from LBA - Starting sector */\r
232 \r
233 /* Current type drive 0 & 1 found in CMOS or Set by caller. */\r
234 /* Current number of heads, cylinders, and sectors set by caller */\r
235 \r
236 static U8  hd0_type;\r
237 static U8  hd0_heads;\r
238 static U8  hd0_secpertrk;\r
239 static U16 hd0_cyls;\r
240 \r
241 static U8  hd1_type;\r
242 static U8  hd1_heads;\r
243 static U8  hd1_secpertrk;\r
244 static U16 hd1_cyls;\r
245 \r
246 #define sStatus 64\r
247 \r
248 static struct statstruct\r
249  {\r
250   U32 erc;\r
251   U32 blocks_done;\r
252   U32 BlocksMax;\r
253   U8 fNewMedia;\r
254   U8 type_now;          /* current fdisk_table for drive selected */\r
255   U8 resvd0[2];         /* padding for DWord align  */\r
256   U32 nCyl;                     /* total physical cylinders */\r
257   U32 nHead;            /* total heads on device    */\r
258   U32 nSectors;         /* Sectors per track        */\r
259   U32 nBPS;                     /* Number of bytes per sect.  32 bytes out to here.*/\r
260 \r
261   U32 LastRecalErc0;\r
262   U32 LastSeekErc0;\r
263   U8  LastStatByte0;\r
264   U8  LastErcByte0;\r
265   U8  fIntOnReset;      /* Interrupt was received on HDC_RESET */\r
266   U8  filler0;\r
267 \r
268   U32 LastRecalErc1;\r
269   U32 LastSeekErc1;\r
270   U8  LastStatByte1;\r
271   U8  LastErcByte1;\r
272   U8  ResetStatByte;    /* Status Byte immediately after RESET */\r
273   U8  filler1;\r
274 \r
275   U32 resvd1[2];        /* out to 64 bytes */\r
276   };\r
277 \r
278 static struct statstruct hdstatus;\r
279 static struct statstruct HDStatTmp;\r
280 \r
281 static struct dcbtype \r
282 {\r
283         S8   Name[12];\r
284         S8   sbName;\r
285         S8   type;\r
286         S16  nBPB;\r
287         U32  last_erc;\r
288         U32  nBlocks;\r
289         S8  *pDevOp;\r
290         S8  *pDevInit;\r
291         S8  *pDevSt;\r
292         U8   fDevReent;\r
293         U8   fSingleUser;\r
294         S16  wJob;\r
295         U32  OS1;\r
296         U32  OS2;\r
297         U32  OS3;\r
298         U32  OS4;\r
299         U32  OS5;\r
300         U32  OS6;\r
301         };\r
302 \r
303 static struct dcbtype hdcb[2];          /* two HD device control blocks */\r
304 \r
305 /* Exch and msgs space for HD ISR */\r
306 \r
307 static U32 hd_exch;\r
308 \r
309 static U32 hd_msg;\r
310 static U32 hd_msg2;\r
311 \r
312 static long HDDInt;\r
313 \r
314 /*======================================================*/\r
315 /*=================== START OF CODE ====================*/\r
316 /*======================================================*/\r
317 \r
318 /*********************************************************\r
319     This is called ONCE to initialize the HD Driver.\r
320 *********************************************************/\r
321 \r
322 U32  hdisk_setup(void)\r
323 {\r
324 U32  erc;\r
325 \r
326   /* first we set up the 2 DCBs in anticipation of calling InitDevDr */\r
327 \r
328         hdcb[0].Name[0]  = 'H';\r
329         hdcb[0].Name[1]  = 'D';\r
330         hdcb[0].Name[2]  = '0';\r
331         hdcb[0].sbName   = 3;\r
332         hdcb[0].type     = 1;            /* Random */\r
333         hdcb[0].nBPB     = 512;\r
334         hdcb[0].nBlocks  = 524288;   /* largest disk handled - 2Gb disks*/\r
335         hdcb[0].pDevOp   = &hddev_op;\r
336         hdcb[0].pDevInit = &hddev_init;\r
337         hdcb[0].pDevSt   = &hddev_stat;\r
338 \r
339         hdcb[1].Name[0]  = 'H';\r
340         hdcb[1].Name[1]  = 'D';\r
341         hdcb[1].Name[2]  = '1';\r
342         hdcb[1].sbName   = 3;\r
343         hdcb[1].type     = 1;                   /* Random */\r
344         hdcb[1].nBPB     = 512;\r
345         hdcb[1].nBlocks  = 524288;      /* largest device handled - 2Gb disks*/\r
346         hdcb[1].pDevOp   = &hddev_op;\r
347         hdcb[1].pDevInit = &hddev_init;\r
348         hdcb[1].pDevSt   = &hddev_stat;\r
349 \r
350 /* These are defaulted to non zero values to\r
351    ensure we don't get a divide by zero during initial calculations\r
352    on the first read.\r
353 */\r
354 \r
355         hd0_type =      ReadCMOS(0x19); /* read this but don't use it */\r
356         hd0_heads = 16;                 /* Max */\r
357         hd0_secpertrk = 17;             /* most common */\r
358         hd0_cyls = 1024;                /* Max */\r
359 \r
360         hd1_type =      ReadCMOS(0x1A);\r
361         hd1_heads = 16;\r
362         hd1_secpertrk = 17;\r
363         hd1_cyls = 1024;\r
364 \r
365         erc = AllocExch(&hd_exch);              /* Exhange for HD Task to use */\r
366 \r
367         SetIRQVector(14, &hdisk_isr);\r
368         UnMaskIRQ(14);\r
369 \r
370 /* Documentation lists the fixed disk types at CMOS 11h and 12h,\r
371    and also shows them at 19h and 1Ah.  We don't actually read them\r
372    because they are not dependable. They vary from BIOS to BIOS.\r
373    We have to make this sucker work the hard way.\r
374 */\r
375 \r
376 /* Reset the HDC - hd_reset resets the controller (which controlls\r
377    both drives). We have to do it once, then try both physical drives.\r
378    If the second drive is not there, some controllers will lock-up\r
379    (the el-cheapos).  In this case we have to reset it again so it\r
380    will work.  It seems like a lot of work, but to make it function\r
381    with the widest range of IDE and MFM controllers this is the\r
382    only way I have found that works.\r
383 */\r
384 \r
385         hd_reset();             /* no error is returned */\r
386 \r
387 /* Now we attempt to select and recal both drives.\r
388    The driver MUST be able to recal the first physical drive\r
389    or the Driver won't install.\r
390 */\r
391 \r
392         erc = hd_recal(0);                      /* try to recal */\r
393         if (erc)\r
394         {                                       /* try one more time! */\r
395                 hd_reset();\r
396                 erc = hd_recal(0);                      /* try to recal */\r
397                 if (erc)\r
398                 {\r
399                 hdcb[0].last_erc = erc;\r
400                         hd0_type = 0;                   /* Must not be a valid drive */\r
401                         return(ErcNoDrive0);\r
402                 }\r
403     }\r
404 \r
405         /* if we got here, drive 0 looks OK and the controller is\r
406            functioning.  Now we try drive 1 if type > 0.\r
407         */\r
408 \r
409         if (hd1_type)\r
410         {\r
411                 erc = hd_recal(1);      /* try to recal if CMOS says it's there */\r
412                 if (erc)\r
413                 {\r
414                 hdcb[1].last_erc = erc;\r
415                         hd1_type = 0;                   /* Guess it's not a valid drive */\r
416 \r
417                         if (!erc)\r
418 \r
419                 /* We must redo drive 0 cause some cheap controllers lockup\r
420                 on us if drive 1 is not there.  They SHOULD simply return\r
421                 a Bad Command bit set in the Error register, but they don't. */\r
422 \r
423                         hd_reset();\r
424                         erc = hd_recal(0);                      /* recal drive 0 */\r
425              hdcb[0].last_erc = erc;\r
426                 }\r
427         }\r
428 \r
429         return(erc = InitDevDr(12, &hdcb, 2, 1));\r
430 \r
431 }\r
432 \r
433 /************************************************************\r
434  Reset the HD controller.  This should only be called by\r
435  DeviceInit or hdisk_setup.  This resets the controller\r
436  and reloads parameters for both drives (if present) and\r
437  attempts to recal them.\r
438 *************************************************************/\r
439 \r
440 static void hd_reset(void)\r
441 {\r
442 U32 i;\r
443         UnMaskIRQ(14);                  /*  enable the IRQ */\r
444         OutByte(4, HD_REG_PORT);        /*  reset the controller */\r
445         MicroDelay(4);                          /*  Delay 60us */\r
446 \r
447     /* bit 3 of HD_REG must be 1 for access to heads 8-15 */\r
448     /* Clear "MUCHO" heads bit, and clear the reset bit */\r
449 \r
450     OutByte(hd_control & 0x0f, HD_REG_PORT);\r
451 \r
452         Sleep(20);              /* 200ms - seems some controllers are SLOW!! */\r
453     i = CheckMsg(hd_exch, &hd_msg);     /* Eat Int if one came back  */\r
454 \r
455         hdstatus.ResetStatByte = statbyte;      /* The ISR gets statbyte */\r
456 \r
457         if (i) hdstatus.fIntOnReset = 1;\r
458         else hdstatus.fIntOnReset = 0;\r
459 \r
460 }\r
461 \r
462 /*************************************************************\r
463  The ISR is VERY simple. It just waits for an interrupt, gets\r
464  the single status byte from the controller (which clears the\r
465  interrupt condition) then sends an empty message to the\r
466  exchange where the HD Driver task will be waiting.\r
467  This tells the HD task currently running that it's got\r
468  some status to act on!\r
469 ****************************************************************/\r
470 static void interrupt hdisk_isr(void)\r
471 {\r
472         statbyte = InByte(HD_PORT+7);\r
473     HDDInt = 1;\r
474         ISendMsg(hd_exch, 0xfffffff0, 0xfffffff0);\r
475         EndOfIRQ(14);\r
476 }\r
477 \r
478 /*************************************************************\r
479  This checks the HDC controller to see if it's busy so we can\r
480  send it commands or read the rest of the registers.\r
481  We will wait up to 3 seconds then error out.\r
482  The caller should call check_busy and check the error.\r
483  If it's 0 then the controller became ready in less than\r
484  3 seconds.  ErcNotReady will be returned otherwise.\r
485  It leaves the status byte in the global statbyte.\r
486 ****************************************************************/\r
487 \r
488 static U32  check_busy(void)\r
489 {\r
490 S16 count;\r
491 \r
492   count = 0;\r
493   while (count++ < 60) \r
494   {\r
495         statbyte = InByte(HD_PORT+7);\r
496         if ((statbyte & BUSY) == 0) return(ok);\r
497         Sleep(5);  /* 50ms shots */\r
498   }\r
499   return(ErcNotReady);   /* controller out to lunch! */\r
500 }\r
501 \r
502 /*************************************************************\r
503   This sends the SetParams command to the controller to set\r
504   up the drive geometry (nHeads, nSectors, etc.).\r
505 ****************************************************************/\r
506 \r
507 static U32  hd_init(U8 drive)\r
508 {\r
509 U32 erc;\r
510   /* set max heads, sectors and cylinders */\r
511   if (drive == 0) \r
512   {                                                             /* Drive 0 */\r
513     hd_Cmd[2] = hd0_secpertrk;                                  /* sector count */\r
514     hd_Cmd[6] = (drive << 4) | ((hd0_heads-1) & 0x0f) | 0xa0; /* hds & drv */\r
515   }\r
516   else\r
517   {                                                                             /* Drive 1 */\r
518     hd_Cmd[2] = hd1_secpertrk;                                  /* sector count */\r
519     hd_Cmd[6] = (drive << 4) | ((hd1_heads-1) & 0x0f) | 0xa0; /* hds & drv */\r
520   }\r
521   hd_Cmd[1] = 0;\r
522   hd_Cmd[3] = 0;\r
523   hd_Cmd[4] = 0;                                                                /* cyl = 0 for init */\r
524   hd_Cmd[5] = 0;                                                                /* cyl = 0 for init */\r
525 \r
526   erc = send_command(HDC_SET_PARAMS);           /* Send the command */\r
527   erc = hd_wait();                                                      /* wait for interrupt */\r
528   if (!erc)\r
529       erc = hd_status(HDC_SET_PARAMS);\r
530   return(erc);\r
531 }\r
532 \r
533 /******************************************\r
534 Wait for the hardware interrupt to occur.\r
535 Time-out and return if no interrupt.\r
536 ********************************************/\r
537 \r
538 static U32  hd_wait(void)\r
539 {\r
540 U32  erc;\r
541 \r
542         /* Set alarm for 3 seconds */\r
543 \r
544         HDDInt = 0;\r
545         KillAlarm(hd_exch);                     /* kill any pending alarm */\r
546 \r
547         erc = Alarm(hd_exch, 300);      /* Set it up again */\r
548         if (erc)\r
549                 return(erc);            /* bad problem */\r
550 \r
551         erc = WaitMsg(hd_exch, &hd_msg);\r
552 \r
553         KillAlarm(hd_exch);\r
554 \r
555         if (hd_msg != 0xfffffff0)\r
556         {        /* HD interrupt sends fffffff0 */\r
557             if (HDDInt)\r
558               return(ErcMissHDDInt);\r
559                 else\r
560                         return(ErcHDCTimeOut);          /* Alarm sends 0xffffffff      */\r
561         }\r
562         else\r
563         {\r
564                 KillAlarm(hd_exch);\r
565                 return(ok);\r
566         }\r
567 }\r
568 \r
569 /********************************************\r
570     Recalibrate the drive.\r
571 *********************************************/\r
572 \r
573 static U32  hd_recal(U8 drive)\r
574 {\r
575 U32 erc;\r
576 \r
577   hd_Cmd[6] = (drive << 4) | (hd_head & 0x0f) | 0xa0;\r
578   erc = send_command(HDC_RECAL);\r
579   if (!erc)\r
580     erc = hd_wait();                                            /* wait for interrupt */\r
581   if (!erc)\r
582       erc = hd_status(HDC_RECAL);\r
583   if (drive)\r
584     hdstatus.LastRecalErc1 = erc;\r
585   else\r
586     hdstatus.LastRecalErc0 = erc;\r
587   return(erc);\r
588 }\r
589 \r
590 \r
591 /********************************************\r
592     Send the command to the controller.\r
593     Clear the Echange of any left over\r
594     alarm or int messages before we\r
595     send a command.\r
596 *********************************************/\r
597 \r
598 static U32  send_command(U8  Cmd)\r
599 {\r
600 U32 erc, msg[2];\r
601 \r
602         while (CheckMsg(hd_exch, &msg) == 0);   /* Empty it */\r
603 \r
604     /* bit 3 of HD_REG must be 1 for access to heads 8-15 */\r
605     if (hd_head > 7)\r
606     {\r
607       hd_control |= 0x08;\r
608       OutByte(hd_control, HD_REG_PORT);         /*  set bit for head > 7 */\r
609       hd_control &= 0xf7;\r
610     }\r
611     erc = check_busy();\r
612     if (!erc) OutByte(hd_Cmd[1], HD_PORT+1);\r
613     if (!erc) erc = check_busy();\r
614     if (!erc) OutByte(hd_Cmd[2], HD_PORT+2);\r
615     if (!erc) erc = check_busy();\r
616     if (!erc) OutByte(hd_Cmd[3], HD_PORT+3);\r
617     if (!erc) erc = check_busy();\r
618     if (!erc) OutByte(hd_Cmd[4], HD_PORT+4);\r
619     if (!erc) erc = check_busy();\r
620     if (!erc) OutByte(hd_Cmd[5], HD_PORT+5);\r
621     if (!erc) erc = check_busy();\r
622     if (!erc) OutByte(hd_Cmd[6], HD_PORT+6);\r
623     if (!erc) erc = check_busy();\r
624     if (!erc) OutByte(Cmd, HD_PORT+7);\r
625     return(erc);\r
626 }\r
627 \r
628 /*************************************************************\r
629  This sets up the cylinder, head and sector variables for all\r
630  commands that require them (read, write, verify, format, seek).\r
631  nBlks ca NOT be greater than the hardware can handle. For\r
632  IDE/MFM controllers this is 128 sectors.\r
633  The caculated values are placed in the proper command byte\r
634  in anticipation of the command being sent.\r
635 *************************************************************/\r
636 \r
637 static U32  setupseek(U32 dLBA, U32 nBlks)\r
638 {\r
639  U32  j;\r
640  U16  cyl;\r
641 \r
642   if (nBlks > 256) return ErcTooManyBlks;\r
643   if (nBlks == 0) return ErcZeroBlks;\r
644 \r
645   hd_nsectors = nBlks;\r
646   if (hd_nsectors == 256) hd_nsectors = 0;   /* 0==256 for controller */\r
647 \r
648   if (hd_drive == 0) \r
649   {             /* drive 0 */\r
650 \r
651         cyl = dLBA / (hd0_heads * hd0_secpertrk);\r
652         j = dLBA % (hd0_heads * hd0_secpertrk);         /* remainder */\r
653 \r
654     /* we now know what cylinder, calculate head and sector */\r
655 \r
656     hd_head = j / hd0_secpertrk;\r
657     hd_sector = j % hd0_secpertrk + 1; /* sector number start at 1 !!! */\r
658 \r
659   }\r
660   else\r
661   {                                             /* drive 1 */\r
662 \r
663         cyl = dLBA / (hd1_heads * hd1_secpertrk);\r
664         j = dLBA % (hd1_heads * hd1_secpertrk);         /* remainder */\r
665 \r
666     /* We now know what cylinder. Calculate head and sector */\r
667 \r
668     hd_head = j / hd1_secpertrk;\r
669     hd_sector = j % hd1_secpertrk + 1; /* sector number start at 1 !!! */\r
670   }\r
671 \r
672   hd_Cmd[2] = nBlks;                                    /* How many sectors */\r
673   hd_Cmd[3] = hd_sector;                                /* Which sector to start on */\r
674   hd_Cmd[4] = cyl & 0xff;                               /* cylinder lobyte */\r
675   hd_Cmd[5] = (cyl >> 8) & 0xff;                /* cylinder hibyte */\r
676   hd_Cmd[6] = (hd_drive << 4) | (hd_head & 0x0f) | 0xa0;\r
677 \r
678   return ok;\r
679 }\r
680 \r
681 \r
682 /*******************************************************\r
683   Move the head to the selected track (cylinder).\r
684 *********************************************************/\r
685 \r
686 static U32  hd_seek(U32 dLBA)\r
687 {\r
688 U32 erc;\r
689 \r
690   erc = setupseek(dLBA, 1);                                     /* sets up for implied seek */\r
691   if (!erc) erc = send_command(HDC_SEEK);       /* Not implied anymore... */\r
692   if (!erc) erc = hd_wait();                            /* wait for interrupt */\r
693   if (!erc) erc = hd_status(HDC_SEEK);\r
694   hdstatus.LastSeekErc0 = erc;\r
695   return(erc);\r
696 }\r
697 \r
698 \r
699 /*******************************************************\r
700  Called to read status and errors from the controller\r
701  after an interrupt generated by a command we sent.\r
702  The error checking is based on the command that we sent.\r
703  This is done because certain bits in the status and error\r
704  registers are actually not errors, but simply indicate\r
705  status or indicate an action we must take next.\r
706  ZERO returned indicates no errors for the command status\r
707  we are checking.\r
708 *********************************************************/\r
709 \r
710 static U32  hd_status(U8  LastCmd)\r
711 {\r
712 U32 erc;\r
713 U8  statbyte, errbyte;\r
714 \r
715   /* We shouldn't see the controller busy. After all,\r
716      he interrupted us with status.\r
717   */\r
718 \r
719   erc = check_busy();           /* puts status byte into global StatByte */\r
720   if (!erc)\r
721     statbyte = InByte(HD_PORT+7);\r
722   else return(erc);\r
723 \r
724   if (hd_drive)\r
725           hdstatus.LastStatByte1 = statbyte;\r
726   else\r
727           hdstatus.LastStatByte0 = statbyte;\r
728 \r
729   if ((statbyte & ERROR) == 0)\r
730   {             /* Error bit not set in status reg */\r
731     erc = ok;  /* default */\r
732 \r
733         switch (LastCmd)\r
734         {\r
735           case HDC_READ:\r
736           case HDC_READ_LONG:\r
737           case HDC_WRITE:\r
738           case HDC_WRITE_LONG:\r
739           case HDC_SEEK:\r
740           case HDC_RECAL:\r
741                 if (statbyte & WRITE_FAULT) erc = ErcWriteFault;\r
742                 else\r
743                         if ((statbyte & SEEKOK) == 0) erc =     ErcBadSeek;\r
744                 break;\r
745           case HDC_SET_PARAMS:\r
746           case HDC_VERIFY:\r
747           case HDC_FORMAT:\r
748           case HDC_DIAG:\r
749                 break;\r
750           default:\r
751                 break;\r
752         }\r
753     return(erc);\r
754         }\r
755   else\r
756   {\r
757         erc = check_busy();\r
758         if (!erc)\r
759           errbyte = InByte(HD_PORT+1);\r
760         else return(erc);\r
761 \r
762         if (hd_drive)\r
763             hdstatus.LastErcByte1 = errbyte;\r
764         else\r
765             hdstatus.LastErcByte0 = errbyte;\r
766 \r
767         if (errbyte & BAD_ADDRESS) erc = ErcAddrMark;\r
768         else if (errbyte & BAD_SEEK) erc = ErcBadSeek;\r
769         else if (errbyte & BAD_CMD) erc = ErcBadCmd;\r
770         else if (errbyte & BAD_IDMARK) erc = ErcSectNotFound;\r
771         else if (errbyte & BAD_ECC) erc = ErcBadECC;\r
772         else if (errbyte & BAD_SECTOR) erc = ErcBadBlock;\r
773         else erc = ErcBadHDC; /* no error bits found but should have been! */\r
774   }\r
775  return erc;\r
776 }\r
777 \r
778 /*************************************************************\r
779  This is called for the DeviceOp code Read.\r
780  This reads 1 or more whole sectors from the calculated values\r
781  in hd_head, hd_sector, and hd_cyl\r
782 *************************************************************/\r
783 \r
784 static U32  hd_read(U32 dLBA, U32 dnBlocks, U8 *pDataRet)\r
785 {\r
786 U32 erc, nleft, nBPS;\r
787 \r
788   nBPS = hdcb[hd_drive].nBPB;                   /* From nBytesPerBlock in DCB */\r
789   nleft = dnBlocks;\r
790   erc = setupseek(dLBA, dnBlocks);              /* sets up for implied seek */\r
791   if (!erc) erc = send_command(HDC_READ);\r
792 \r
793   while ((nleft) && (!erc)) \r
794   {\r
795           erc = hd_wait();                                      /* wait for interrupt */\r
796           if (!erc)\r
797                 erc = hd_status(HDC_READ);\r
798           if (!erc)             /* && (statbyte & DATA_REQ))  */ \r
799           {\r
800                 InWords(HD_PORT, pDataRet, nBPS);\r
801                 pDataRet+=nBPS;\r
802                 --nleft;\r
803           }\r
804   }\r
805   return(erc);\r
806 }\r
807 \r
808 /*************************************************************\r
809  This is called for the DeviceOp code Write.\r
810  This writes 1 or more whole sectors from the calculated values\r
811  in hd_head, hd_sector, and hd_cyl\r
812 *************************************************************/\r
813 \r
814 static U32  hd_write(U32 dLBA, U32 dnBlocks, U8 *pDataOut)\r
815 {\r
816 U32 erc, nSoFar, nBPS;\r
817 \r
818   nBPS = hdcb[hd_drive].nBPB;                   /* From n BytesPerBlock in DCB */\r
819   nSoFar = 0;\r
820   erc = setupseek(dLBA, dnBlocks);              /* sets up for implied seek */\r
821   erc = send_command(HDC_WRITE);\r
822   erc = check_busy();                   /* No INT occurs for first sector of write */\r
823 \r
824   if ((!erc) && (statbyte & DATA_REQ)) \r
825   {\r
826         OutWords(HD_PORT, pDataOut, nBPS);\r
827         pDataOut+=nBPS;\r
828         nSoFar++;\r
829   }\r
830 \r
831   while ((nSoFar < dnBlocks ) && (erc==ok))\r
832   {\r
833           erc = hd_wait();                                      /* wait for interrupt */\r
834       if (erc==ok) erc = hd_status(HDC_WRITE);\r
835           if ((erc==ok) && (statbyte & DATA_REQ)) \r
836           {\r
837                   OutWords(HD_PORT, pDataOut, nBPS);\r
838                   pDataOut+=nBPS;\r
839                   nSoFar++;\r
840           }\r
841   }\r
842   if (!erc) erc = hd_wait();                    /* wait for final interrupt */\r
843   if (!erc) erc = hd_status(HDC_WRITE);\r
844 \r
845   return(erc);\r
846 }\r
847 \r
848 \r
849 /*************************************************************\r
850  This formats the track beginning at the block address given\r
851  in dLBA. dLBA must always be a multiple of the number of\r
852  sectors per track minus 1 for the disk type.\r
853 *************************************************************/\r
854 \r
855 static U32  hd_format_track(U32 dLBA, U32 dnBlocks)\r
856 {\r
857 U32  erc;\r
858   erc = setupseek(dLBA, dnBlocks);              /* sets up for implied seek */\r
859   erc = send_command(HDC_FORMAT);\r
860   erc = hd_wait();                                              /* wait for interrupt */\r
861   if (erc==ok)\r
862       erc = hd_status(HDC_FORMAT);\r
863   return(erc);\r
864 }\r
865 \r
866 /******************************************************************\r
867    ReadSector is the only device specific call in the IDE/MFM hard\r
868    disk device driver.  This allows you to read ONE sector\r
869    specified by Cylinder, head and Sector number.\r
870    Cylinder is LoWord of dLBA in DeviceOp call,\r
871    Head is LoWord of dnBlocks in DeviceOp call, and\r
872    Sector number is HiWord in dnBlocks.\r
873 *******************************************************************/\r
874 \r
875 static U32 ReadSector(U32 Cylinder, U32 HdSect, U8 *pDataRet)\r
876 {\r
877 U32 erc;\r
878 U16  cyl;\r
879 \r
880   cyl = Cylinder;\r
881   hd_head = HdSect & 0xffff;\r
882   hd_sector = (HdSect >> 16) & 0xffff;\r
883 \r
884 /*      For testing\r
885  xprintf("\r\nCYL %d, HD %d, SEC %d\r\n", cyl, hd_head, hd_sector);\r
886 */\r
887 \r
888   hd_Cmd[2] = 1;                                                /* How many sectors */\r
889   hd_Cmd[3] = hd_sector;                                /* Which sector to start on */\r
890   hd_Cmd[4] = cyl & 0xff;                               /* cylinder lobyte */\r
891   hd_Cmd[5] = (cyl >> 8) & 0xff;                /* cylinder hibyte */\r
892   hd_Cmd[6] = (hd_drive << 4) | (hd_head & 0x0f) | 0xa0;\r
893 \r
894   erc = send_command(HDC_READ);\r
895   erc = hd_wait();                                      /* wait for interrupt */\r
896   if (!erc) erc = hd_status(HDC_READ);\r
897   if (!erc)\r
898         InWords(HD_PORT, pDataRet, 512);\r
899   return(erc);\r
900 }\r
901 \r
902 /***************************************************************************\r
903 Now begins the PUBLIC routines that are used for all DEVICE DRIVERS\r
904 */\r
905 \r
906 /******************************************\r
907 Called for all device operations.  This\r
908 assigns physical device from logical number\r
909 that outside callers use. For Hard disk,\r
910 12=0 and 13=1. This will check to make sure a\r
911 drive type is assigned and check to see if \r
912 they are going to exceed max logical blocks.\r
913 *******************************************/\r
914 \r
915 static U32  hddev_op(U32  dDevice,\r
916                       U32  dOpNum,\r
917                       U32  dLBA,\r
918                       U32  dnBlocks,\r
919                       U8  *pData)\r
920 {\r
921 U32  erc;\r
922 \r
923  hdstatus.blocks_done = 0;      /* Reset values in Status record */\r
924 \r
925  erc = 0;\r
926 \r
927  /* Set drive internal drive number */\r
928 \r
929  if (dDevice == 12)\r
930         hd_drive = 0;\r
931  else\r
932         hd_drive = 1;\r
933 \r
934  /* Check to see if we have a leftover interrupt message from last\r
935     command.  If so then we eat it (and do nothing) */\r
936 \r
937   CheckMsg(hd_exch, &hd_msg);           /* Ignore error */\r
938 \r
939  if (hd_drive==0)\r
940  {\r
941         if (hd0_type==0)\r
942                 erc = ErcInvalidDrive;\r
943  }\r
944  else\r
945  {\r
946         if (hd1_type==0)\r
947                 erc = ErcInvalidDrive;\r
948  }\r
949 \r
950  /* make sure they don't exceed max blocks */\r
951 \r
952  if (!erc)\r
953    if (dLBA > hdcb[hd_drive].nBlocks) erc = ErcBadLBA;\r
954 \r
955  if (!erc)\r
956  {\r
957 \r
958    switch(dOpNum)\r
959    {\r
960           case(CmdNull):\r
961                 erc = ok;                                       /* Null Command */\r
962                 break;\r
963           case(CmdRead):                                                                        /* Read */\r
964                 erc = hd_read(dLBA, dnBlocks, pData);\r
965                 break;\r
966           case(CmdWrite):                                                                       /* Write */\r
967                 erc = hd_write(dLBA, dnBlocks, pData);\r
968                 break;\r
969           case(CmdVerify):                                                                      /* Verify */\r
970                 erc = ErcNotSupported;\r
971 \r
972                 /* hd_verify is not supported in this version of the driver */\r
973                 /*              erc = hd_verify(dLBA, dnBlocks, pData); */\r
974                 break;\r
975           case(CmdSeekTrk):                                                             /* Seek Track */\r
976                 erc = hd_seek(dLBA);\r
977                 break;\r
978           case(CmdFmtTrk):                                  /* Format Track */\r
979                 erc = hd_format_track(dLBA, dnBlocks);\r
980                 break;\r
981           case(CmdResetHdw):                                                            /* Reset Ctrlr */\r
982                 hd_reset();\r
983                 erc = 0;\r
984                 break;\r
985           case(CmdReadSect):                                                            /* Read Sector(s) */\r
986                 erc = ReadSector(dLBA, dnBlocks, pData);\r
987                 break;\r
988           default:\r
989                 erc = ErcBadOp;\r
990                 break;\r
991         }\r
992  }\r
993  hdcb[hd_drive].last_erc = erc;                 /* update DCB erc */\r
994  return(erc);\r
995 }\r
996 \r
997 /******************************************\r
998 Called for indepth status report on ctrlr\r
999 and drive specified. Returns 64 byte block\r
1000 of data including current drive geometery.\r
1001 This is called by the PUBLIC call DeviceStat!\r
1002 *******************************************/\r
1003 \r
1004 static U32 hddev_stat(U32  dDevice,\r
1005                            S8 * pStatRet,\r
1006                            U32  dStatusMax,\r
1007                            U32  *pdStatusRet)\r
1008 {\r
1009 U32 i;\r
1010 \r
1011  /* Set status for proper device */\r
1012 \r
1013  if (dDevice == 12)\r
1014  {\r
1015         hdstatus.erc = hdcb[0].last_erc;\r
1016         hdstatus.type_now = hd0_type;\r
1017         hdstatus.nCyl = hd0_cyls;\r
1018         hdstatus.nHead = hd0_heads;\r
1019         hdstatus.nSectors = hd0_secpertrk;\r
1020         hdstatus.nBPS = hdcb[0].nBPB;\r
1021  }\r
1022  else \r
1023  {\r
1024         hdstatus.erc = hdcb[1].last_erc;\r
1025         hdstatus.type_now = hd1_type;\r
1026         hdstatus.nCyl = hd1_cyls;\r
1027         hdstatus.nHead = hd1_heads;\r
1028         hdstatus.nSectors = hd1_secpertrk;\r
1029         hdstatus.nBPS = hdcb[1].nBPB;\r
1030  }\r
1031 \r
1032  /* Calculate size of status to return. Return no more than asked for! */\r
1033 \r
1034  if (dStatusMax <= sStatus) i = dStatusMax;\r
1035  else i = sStatus;\r
1036 \r
1037  CopyData(&hdstatus, pStatRet, i);              /* copy to their status block */\r
1038 \r
1039  *pdStatusRet = i;                                              /* tell em how much it was */\r
1040 \r
1041  return ok;\r
1042 }\r
1043 \r
1044 /******************************************\r
1045 Called to reset the hard disk controller\r
1046 and set drive parameters.  The Initdata\r
1047 is a copy of the 64 byte status block\r
1048 that is read from status.  The caller\r
1049 normally reads the block (DeviceStat),\r
1050 makes changes to certain fields and\r
1051 calls DeviceInit pointing to the block\r
1052 for the changes to take effect.\r
1053 This should ONLY be called once for each HD\r
1054 to set it's parameters before it is used\r
1055 the first time after the driver is loaded,\r
1056 or after a fatal error is received that\r
1057 indicates the controller may need to be\r
1058 reset (multiple timeouts etc.).\r
1059 The DCB values are updated if this is\r
1060 successful.\r
1061 This is called by the PUBLIC call DeviceInit.\r
1062 *******************************************/\r
1063 \r
1064 static U32  hddev_init(U32  dDevice,\r
1065                           S8  *pInitData,\r
1066                           U32   sdInitData)\r
1067 \r
1068 {\r
1069 U32 erc, i;\r
1070 \r
1071   erc = 0;\r
1072 \r
1073  /* Read the init status block in */\r
1074 \r
1075  if (sdInitData > sStatus) i = sStatus;         /* no more than 64 bytes! */\r
1076  else i = sdInitData;\r
1077 \r
1078  CopyData(pInitData, &HDStatTmp, i);            /* copy in their init data */\r
1079 \r
1080  /* Set internal drive number */\r
1081  if (dDevice == 12) hd_drive=0;\r
1082  else hd_drive = 1;\r
1083 \r
1084  if (hd_drive==0)\r
1085  {\r
1086         hd0_type  = HDStatTmp.type_now;\r
1087         if (hd0_type) \r
1088         {\r
1089           hd0_cyls  = HDStatTmp.nCyl;\r
1090           hd0_heads = HDStatTmp.nHead;\r
1091           hd0_secpertrk = HDStatTmp.nSectors;\r
1092         }\r
1093         else erc = ErcInvalidDrive;\r
1094  }\r
1095  else \r
1096  {\r
1097         hd1_type  = HDStatTmp.type_now;\r
1098         if (hd1_type) \r
1099         {\r
1100                 hd1_cyls  = HDStatTmp.nCyl;\r
1101                 hd1_heads = HDStatTmp.nHead;\r
1102                 hd1_secpertrk = HDStatTmp.nSectors;\r
1103         }\r
1104         else erc = ErcInvalidDrive;\r
1105  }\r
1106 \r
1107 /* If no error, initialize it and recal */\r
1108 \r
1109  if (!erc) erc = hd_init(hd_drive);\r
1110  if (!erc) erc = hd_recal(hd_drive);\r
1111 \r
1112 /* If no error, update corresponding DCB values */\r
1113 \r
1114  if (!erc) \r
1115  {\r
1116         hdcb[hd_drive].nBPB = HDStatTmp.nBPS;\r
1117         hdcb[hd_drive].last_erc = 0;\r
1118         hdcb[hd_drive].nBlocks =\r
1119                                 HDStatTmp.nCyl * HDStatTmp.nSectors * HDStatTmp.nHead;\r
1120  }\r
1121 \r
1122  hdcb[hd_drive].last_erc = erc;                 /* update DCB erc */\r
1123 \r
1124  return(erc);\r
1125 }\r
1126 \r
1127 /*===========  THE END  =========================================*/\r