]> pd.if.org Git - mmurtl/blob - ossource/floppy.c
autocommit for file dated 1995-02-09 11:14:18
[mmurtl] / ossource / floppy.c
1 /* Floppy.C  Floppy Disk Device Driver for MMURTL */\r
2 \r
3 /*\r
4   MMURTL Operating System Source Code\r
5   Copyright 1991,1992,1993,1994 Richard A. Burgess\r
6   ALL RIGHTS RESERVED   Version 1.0\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 SpawnTask(S8  *pEntry,\r
19                              U32 dPriority,\r
20                      U32 fDebug,\r
21                      S8  *pStack,\r
22                              U32 fOSCode);\r
23 \r
24 extern far U32 AllocExch(U32 *pExchRet);\r
25 \r
26 extern far U32 AllocDMAPage(U32 nPages, U8 **ppMemRet U32 *pPhyMemRet);\r
27 \r
28 extern far U32 InitDevDr(U32 dDevNum,\r
29                                           S8  *pDCBs,\r
30                                                   U32 nDevices,\r
31                                                   U32 dfReplace);\r
32 \r
33 extern far DmaSetUp(S8  *pPhyMem,\r
34                                         U32 sdMem,                      /* size */\r
35                                         U32 dChannel,           /* channel 2 floppy */\r
36                                         U32 dType,                      /* 0=Verfify, 1=IN,  2=OUT */\r
37                                         U32 dMode);             /* FDC uses 1 (single cycle) */\r
38 \r
39 extern far U32 UnMaskIRQ(U32 IRQNum);\r
40 extern far U32 MaskIRQ(U32 IRQNum);\r
41 extern far U32 SetIRQVector(U32 IRQNum, S8  *pIRQ);\r
42 extern far U32 EndOfIRQ(U32 IRQNum);\r
43 extern far U32 SendMsg(U32 Exch, U32 msg1, U32 msg2);\r
44 extern far U32 ISendMsg(U32 Exch, U32 msg1, U32 msg2);\r
45 extern far U32 WaitMsg(U32 Exch, S8  *pMsgRet);\r
46 extern far U32 CheckMsg(U32 Exch, S8  *pMsgRet);\r
47 extern far U32 GetTimerTick(U32 *pTickRet);\r
48 extern far U32 Alarm(U32 Exch, U32 count);\r
49 extern far U32 KillAlarm(U32 Exch);\r
50 extern far U32 Sleep(U32 count);\r
51 extern far void MicroDelay(U32 us15count);\r
52 extern far void OutByte(U8 Byte, U16 wPort);\r
53 extern far void OutWord(U16 Word, U16 wPort);\r
54 extern far U8 InByte(U16 wPort);\r
55 extern far U16 InWord(U16 wPort);\r
56 extern far U8 ReadCMOS(U16 Address);\r
57 extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
58 \r
59 \r
60 /* THE FLOPPY INTERRUPT FUNCTION PROTOTYPE */\r
61 \r
62 static void interrupt fdisk_isr(void);\r
63 \r
64 /*  PROTOTYPE FOR Floppy motor control task and associated stuff */\r
65 \r
66 static void fdmotor_task(void);\r
67 static void fd_select(U32 drive);\r
68 static void fd_motoroff(U32 drive);\r
69 \r
70 /* THE REST OF THE PROTOTYPES */\r
71 \r
72 U32 fdisk_setup(void);\r
73 static U32 RdWrtVerf(U32 op);\r
74 static U32 format_track(void);\r
75 static U32 Set_Media(U32 drive, U32 type);\r
76 static U32 FDC_reset(void);\r
77 static U8  cmos_type (U8 drive_nr);\r
78 static U32 send_fdc(U8 parm);\r
79 static U8  GetParm(U8 index);\r
80 static U32 wait_int (void);\r
81 static U32 seek(void);\r
82 static U32 recal(void);\r
83 static U32 read_data(U8 *pDataRet);\r
84 static U32 results(U32 expect);\r
85 static void purge_fdc (void);\r
86 static void wait_for_head(void);\r
87 static U32 med_change(void);\r
88 static U32 get_fdc_status(void);\r
89 \r
90 /* The following 3 calls are required in every MMURTL device driver */\r
91 \r
92 static U32 dev_op(U32 dDevice,\r
93                     U32 dOpNum,\r
94                     U32 dLBA,\r
95                     U32 dnBlocks,\r
96                     S8  *pData);\r
97 \r
98 static U32 dev_stat(U32 dDevice,\r
99                           S8 * pStatRet,\r
100                           U32 dStatusMax,\r
101                           U32 *pdSatusRet);\r
102 \r
103 static U32 dev_init(U32 dDevNum,\r
104                           S8  *pInitData,\r
105                           U32  sdInitData);\r
106 \r
107 /* Near External for troubleshooting */\r
108 \r
109 extern long xprintf(char *fmt, ...);\r
110 \r
111 /* LOCAL DEFINITIONS */\r
112 \r
113 #define ok    0\r
114 #define TRUE  1\r
115 #define FALSE 0\r
116 \r
117 #define RATE_500                0x00\r
118 #define RATE_300                0x01\r
119 #define RATE_250                0x02\r
120 #define RATE_1000       0x03\r
121 #define INT_FLAG                0x80\r
122 \r
123 /* Error Codes to return */\r
124 \r
125 #define ErcNotInstalled 504\r
126 #define ErcAddrMark             602\r
127 #define ErcReadOnly             603\r
128 #define ErcSectNotFound 604\r
129 #define ErcNewMedia             605\r
130 #define ErcNotMounted   606\r
131 #define ErcCRC                  607\r
132 #define ErcBadFDC               608\r
133 #define ErcBadSeek              609\r
134 #define ErcFDCTimeOut   610\r
135 #define ErcOverRun              611\r
136 #define ErcBadLBA               612\r
137 #define ErcDriveType    613\r
138 #define ErcBadOp                614\r
139 #define ErcBadRecal             615\r
140 #define ErcSendFDC              616\r
141 #define ErcResults              617\r
142 #define ErcBadCmd               618\r
143 #define ErcReadyLine    619\r
144 \r
145 /* Commands accepted by driver */\r
146 \r
147 #define CmdNull     0\r
148 #define CmdRead     1\r
149 #define CmdWrite    2\r
150 #define CmdVerify   3\r
151 #define CmdFmtBlk   4\r
152 #define CmdFmtTrk   5\r
153 #define CmdSeekTrk  6\r
154 \r
155 /* FDC port definitions */\r
156 \r
157 #define DOR_PORT        0x3f2\r
158 #define MSR_PORT        0x3f4\r
159 #define DATA_PORT       0x3f5\r
160 #define DIR_PORT        0x3f7\r
161 #define DRR_PORT        0x3f7\r
162 \r
163 /* FDC Return Status bit definitions */\r
164 \r
165 #define BUSY            0x10    /* was BIT4 */\r
166 #define DSKCHANGE_BIT 0x80\r
167 #define BIT7            0x80\r
168 #define BIT6            0x40\r
169 #define BIT5            0x20\r
170 #define BIT4            0x10\r
171 #define BIT3            0x08\r
172 #define BIT2            0x04\r
173 #define BIT1            0x02\r
174 #define BIT0            0x01\r
175 \r
176 #define RQM                     0x80\r
177 #define DIO                     0x40\r
178 \r
179 /* FDC commands */\r
180 \r
181 #define FDC_READ        0xe6\r
182 #define FDC_WRITE       0xc5\r
183 #define FDC_FORMAT      0x4d\r
184 \r
185 /* FDC DOR register bits */\r
186 \r
187 #define FD_MOTOR0       0x10\r
188 #define FD_MOTOR1   0x20\r
189 #define FD_INTS         0x08\r
190 #define FD_RESET        0x04\r
191 #define FD_DRV1SEL      0x01\r
192 #define FD_MOTMASK      0xf0            /* mask to see motor bits */\r
193 \r
194 /* L O C A L   C O N S T A N T S */\r
195 \r
196 /* The drive table contains parameters for each disk\r
197    type.  The values are:\r
198    0  - FDC SPECIFY Command byte 1\r
199    1  - FDC SPECIFY Command byte 2\r
200    2  - Unused\r
201    3  - Bytes per Sector (coded 0=128, 1=256, 2=512, 3=1024);\r
202    4  - number of sectors per track (Last sector)\r
203    5  - Intersector Gap Size\r
204    6  - Data Length (FFh = 512)\r
205    7  - GAP 3 for Format Command\r
206    8  - Fill Byte for Format command\r
207    9  - Head settle time in milliseconds\r
208    10 - Motor start time in milliseconds/10 (mult value by 10 to use)\r
209    11 - max cylinder index (number of cyls - 1)\r
210    12 - Xfer rate Command\r
211    13 - Unused\r
212    14 - Double Step Flag (e.g., 360K in a 1.2Mb drive)\r
213    15 - Unused\r
214 */\r
215 \r
216 \r
217 static U8 fdisk_table[5][16]= {\r
218 \r
219  /* 0 = NO DRIVE - first two params set to allow FDC_reset */\r
220   {0x0af,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\r
221 \r
222  /* 1 = 360 kb drive */\r
223  {0x0af, 2, 0, 2, 9, 0x2a, -1, 0x50, 0x0f6, 15, 8, 39, RATE_250, 0, 0, 0},\r
224 \r
225  /* 2 = 1.2 mb drive */\r
226  {0xaf, 2, 0, 2, 15, 0x1b, -1, 0x54, 0x0f6, 15, 8, 79, RATE_500, 0, 0, 0},\r
227 \r
228  /* 3 = 720 type drive */\r
229  {0x0af, 2, 0, 2, 9, 0x2a, -1, 0x50, 0x0f6, 15, 8, 79, RATE_250, 0, 0, 0},\r
230 \r
231  /* 4 = 1.44 mb drive */\r
232  {0xaf, 2, 0, 2, 18, 0x1b, -1, 0x6c, 0x0f6, 15, 8, 79, RATE_500, 0, 0, 0},\r
233 \r
234 };\r
235 \r
236 static U32 MotorStk[100];  /* 400 byte stack for motor task */\r
237 static U32 MotorStkTop;\r
238 \r
239 static U8 fd_fdc_command;\r
240 static U8 fd_drive;\r
241 static U8 fd_nr_sectors;\r
242 static U8 fd_head;\r
243 static U8 fd_sector;\r
244 static U8 fd_track;\r
245 static U8 seek_status;\r
246 static U8 fwrite;\r
247 static S8  *fd_pData;\r
248 \r
249 /* current fdisk_table[x] for drive 0 & 1 */\r
250 \r
251 static U8 type0\r
252 static U8 type1\r
253 \r
254 /* Record for 64 byte status and init record */\r
255 \r
256 #define sStatus 64\r
257 \r
258 static struct statstruct{\r
259   U32 erc;\r
260   U32 blocks_done;\r
261   U32 BlocksMax;\r
262   U8 fNewMedia;\r
263   U8 type_now;          /* current fdisk_table for drive selected */\r
264   U8 resvd1[2];         /* padding for DWord align  */\r
265   U32 nCyl;                     /* total physical cylinders */\r
266   U32 nHead;            /* total heads on device    */\r
267   U32 nSectors;         /* Sectors per track        */\r
268   U32 nBPS;                     /* Number of bytes per sect */\r
269   U8 params[16];        /* begin device specific fields */\r
270   U8 STATUS[8];         /* status returned from FDC (for user status) */\r
271   U32 resvd3;\r
272   U32 resvd4;           /* 64 bytes total */\r
273   };\r
274 \r
275 static struct statstruct fdstatus;\r
276 static struct statstruct FDStatTmp;\r
277 \r
278 static U8 FDC_STATUS[8];        /* status returned from FDC */\r
279 static U8 LAST_TRACK[3];  /* holds last track number */\r
280 \r
281 static struct dcbtype {\r
282         S8   Name[12];\r
283         S8   sbName;\r
284         S8   type;\r
285         S16  nBPB;\r
286         U32  last_erc;\r
287         U32  nBlocks;\r
288         S8  *pDevOp;\r
289         S8  *pDevInit;\r
290         S8  *pDevSt;\r
291         S8   fDevReent;\r
292         S8   fSingleUser;\r
293         S16  wJob;\r
294         U32  OS1;\r
295         U32  OS2;\r
296         U32  OS3;\r
297         U32  OS4;\r
298         U32  OS5;\r
299         U32  OS6;\r
300         };\r
301 \r
302 static struct dcbtype fdcb[2];          /* two floppy device control blcocks */\r
303 \r
304 \r
305 /* Exch, msgs space, and vars for FD Motor task */\r
306 static U8 dor_crnt;             /* last value sent to DOR port */\r
307 \r
308 static U8 motor0_want;          /* desired motor0 state, TRUE = want ON */\r
309 static U8 motor1_want;          /* desired motor1 state, TRUE = want ON */\r
310 static U32 fd_tick;             /* Set to tick everytime we select a floppy */\r
311 static U32 fd_newtick;          /* used to check tick time */\r
312 \r
313 /* Exch and msgs space for FD ISR */\r
314 static U32 fd_exch;\r
315 static U32 fd_msg[2];\r
316 \r
317 static U32 rgSectorMax[10] = {0, 720, 2400, 1440, 2880,\r
318                                                 0, 0, 0, 0, 0};         /* set max sectors */\r
319 \r
320 static U8 *sectbuf;             /* sector buffer, 1 Page with AllocDMAPage */\r
321 static U32 physectbuf;  /* physical address of DMA buffer */\r
322 \r
323 /*======================================================*/\r
324 /*=================== START OF CODE ====================*/\r
325 /*======================================================*/\r
326 \r
327 static void enable_ints(void)\r
328 {\r
329 ;\r
330 #asm\r
331         STI\r
332 #endasm\r
333 }\r
334 \r
335 static void disable_ints(void)\r
336 {\r
337 ;\r
338 #asm\r
339         CLI\r
340 #endasm\r
341 }\r
342 \r
343 \r
344 /********************************************************************\r
345  This small function becomes a thread (task) to control the motor.\r
346  The floppy motors are controlled serarately from the rest\r
347  of the floppy drive electronics. A single port controls both\r
348  floppies.  After a floppy is used you don't want to shut it right\r
349  off or you can forget about any dreams of decent throughput.\r
350  This wakes up every 3 seconds and checks to see if there was\r
351  any floppy activity in the past 3 seconds. If not, we shut off\r
352  the motor(s).\r
353 **********************************************************************/\r
354 \r
355 static void fdmotor_task(void)\r
356 {\r
357         enable_ints();\r
358 \r
359 MotorLoop:\r
360 \r
361         Sleep(300);                                                     /* 3 seconds */\r
362 \r
363         GetTimerTick(&fd_newtick);\r
364 \r
365         if ((fd_newtick - fd_tick) > 300) {     /* not used in last 3 seconds */\r
366 \r
367                 if ((!motor0_want) && (dor_crnt & FD_MOTOR0))\r
368                 { /* They want 0 off */\r
369                         disable_ints();\r
370                         dor_crnt &= ~FD_MOTOR0;\r
371                         OutByte( dor_crnt, DOR_PORT);\r
372                         enable_ints();\r
373                 }\r
374 \r
375                 if ((!motor1_want) && (dor_crnt & FD_MOTOR1)) { /* They want 1 off */\r
376                         disable_ints();\r
377                         dor_crnt &= ~FD_MOTOR1;\r
378                         OutByte( dor_crnt, DOR_PORT);\r
379                         enable_ints();\r
380                 }\r
381         }\r
382         goto MotorLoop;\r
383 }\r
384 \r
385 /*******************************************\r
386  Set desired motor status flags and drive\r
387  select bit.  If drive select bit has changed\r
388  this sends the current DOR value to select\r
389  the correct drive. If the motor bit we wanted\r
390  isn't on, we will also send the command.\r
391  We DO NOT mess with the other motor bit.\r
392 *******************************************/\r
393 \r
394 static void fd_select(U32 drive)\r
395 {\r
396 U8 fsend;\r
397 U8 fdelay;\r
398 \r
399         GetTimerTick(&fd_tick);         /* update last time selected */\r
400 \r
401         fsend = FALSE;\r
402         fdelay = FALSE;\r
403 \r
404         if (drive) {                                    /* drive 1 */\r
405                 if (!(dor_crnt & 0x01)) {\r
406                         fsend = TRUE;\r
407                         dor_crnt |= 0x01;               /* select 1 */\r
408                 }\r
409                 if (!(dor_crnt & FD_MOTOR1)) {\r
410                         fsend = TRUE;\r
411                         fdelay = TRUE;\r
412                         dor_crnt |= FD_MOTOR1;  /* motor 1 on */\r
413                 }\r
414         }\r
415         else {\r
416                 if (dor_crnt & 0x01) {\r
417                         fsend = TRUE;\r
418                         dor_crnt &= 0xFE;               /* select 0 (turn 1 off) */\r
419                 }\r
420                 if (!(dor_crnt & FD_MOTOR0)) {\r
421                         fsend = TRUE;\r
422                         fdelay = TRUE;\r
423                         dor_crnt |= FD_MOTOR0;  /* motor 0 on */\r
424                 }\r
425         }\r
426 \r
427         if (fsend) {\r
428 \r
429                 disable_ints();\r
430 \r
431                 OutByte( dor_crnt, DOR_PORT);\r
432 \r
433                 enable_ints();\r
434 \r
435                 if (fdelay)\r
436                         Sleep(33);  /* delay 1/3th second if not already on! */\r
437         }\r
438 \r
439 }\r
440 \r
441 \r
442 /*******************************************\r
443  Sets motor_want bit for specified motor.\r
444 *******************************************/\r
445 \r
446 static void fd_motoroff(U32 motor)\r
447 {\r
448 /* Set what we want so motor task will know */\r
449 \r
450 if (motor)\r
451         motor1_want = FALSE;\r
452 else\r
453         motor0_want = FALSE;\r
454 }\r
455 \r
456 /*********************************************************\r
457     This is called ONCE to initialize the Driver.\r
458 *********************************************************/\r
459 \r
460 U32 fdisk_setup(void)\r
461 {\r
462 U32 erc;\r
463 \r
464 fdcb[0].Name[0] = 'F';\r
465 fdcb[0].Name[1] = 'D';\r
466 fdcb[0].Name[2] = '0';\r
467 fdcb[0].sbName  = 3;\r
468 fdcb[0].type    = 1;\r
469 fdcb[0].fDevReent = 0;  /* not reentrant */\r
470 fdcb[0].fSingleUser = 0;\r
471 \r
472 fdcb[1].Name[0] = 'F';\r
473 fdcb[1].Name[1] = 'D';\r
474 fdcb[1].Name[2] = '1';\r
475 fdcb[1].sbName  = 3;\r
476 fdcb[1].type    = 1;    /* this is set set to zero if not installed */\r
477 fdcb[1].fDevReent = 0;  /* not reentrant */\r
478 fdcb[1].fSingleUser = 0;\r
479 \r
480  dor_crnt |= (FD_INTS | FD_RESET);       /* set ints & reset bits high */\r
481  motor0_want = FALSE;\r
482  motor1_want = FALSE;\r
483  seek_status = 0;       /* clear seek status */\r
484  fdstatus.erc = 0;      /* clear global disk error */\r
485 \r
486  erc = AllocExch(&fd_exch);             /* allocates exchanges for messaging */\r
487  if (erc)\r
488         return(erc);\r
489  erc = AllocDMAPage(1, &sectbuf, &physectbuf);\r
490  if (erc)\r
491         return(erc);\r
492 \r
493 /* Used to test DMA memory call\r
494  xprintf("DMA Address:  %08x, DMA Physical : %08x\r\n", sectbuf, physectbuf);\r
495  Sleep(200);\r
496 */\r
497 \r
498  /* Set up the motor task */\r
499 \r
500  SpawnTask( &fdmotor_task, 18, 0, &MotorStkTop, 1);\r
501 \r
502  SetIRQVector(6, &fdisk_isr);\r
503  UnMaskIRQ(6);    /*  unmask IRQ 6 */\r
504 \r
505  type0 = cmos_type(0);\r
506  if (type0)\r
507  {\r
508     fdcb[0].nBPB    = 512;\r
509     fdcb[0].nBlocks = rgSectorMax[type0];\r
510  }\r
511 \r
512  type1 = cmos_type(1);\r
513  if (type1)\r
514  {\r
515     fdcb[1].nBPB    = 512;\r
516     fdcb[1].nBlocks = rgSectorMax[type1];\r
517  }\r
518 \r
519 /* reset the FDC */\r
520 \r
521  if (fdstatus.erc= FDC_reset())\r
522         return (fdstatus.erc);\r
523 \r
524 /* now we attempt to select and recal both drives */\r
525 \r
526  if (type0) {\r
527         fd_drive = 0;           /* select drive 0 */\r
528         fd_select(0);\r
529         if (erc = recal())\r
530         {\r
531                 erc = recal();          /* try twice */\r
532         }\r
533         fd_motoroff(0);\r
534         if (erc) return (fdstatus.erc = erc);\r
535         }\r
536 \r
537  if (type1) {\r
538         fd_drive = 1;           /* select drive 1 */\r
539         fd_select(1);\r
540         if (erc = recal())\r
541                 erc = recal();          /* try twice */\r
542         if (erc) fdcb[1].type = 0;\r
543         fd_motoroff(1);\r
544         }\r
545 \r
546  fdcb[0].pDevOp = &dev_op;\r
547  fdcb[0].pDevInit = &dev_init;\r
548  fdcb[0].pDevSt = &dev_stat;\r
549 \r
550  fdcb[1].pDevOp = &dev_op;\r
551  fdcb[1].pDevInit = &dev_init;\r
552  fdcb[1].pDevSt = &dev_stat;\r
553 \r
554  return(erc = InitDevDr(10, &fdcb, 2, 1));\r
555 \r
556 }\r
557 \r
558 /************************************************************\r
559  Reset the disk system\r
560 *************************************************************/\r
561 \r
562 static U32 FDC_reset(void)\r
563 {\r
564  U32 erc;\r
565 \r
566  seek_status = 0;                                        /* Set to Recal on next seek */\r
567 \r
568  disable_ints();\r
569 \r
570  OutByte(0x08, DOR_PORT);        /* Drop reset signal on disk controller */\r
571  MicroDelay(75);                         /* Wait 1ms */\r
572  OutByte(0x0C, DOR_PORT);        /* Raise reset line on disk controller */\r
573 \r
574  enable_ints();\r
575 \r
576  /* wait for interrupt and return if error (timeout...?) */\r
577 \r
578  if (erc = wait_int())\r
579         return(erc);\r
580 \r
581   /* Send sense command to current drive to see if reset took.\r
582      If this is the first system reset it defaults to drive 0,\r
583      Head 0.\r
584   */\r
585 \r
586 \r
587  if (erc = send_fdc(8)) return(erc);\r
588 \r
589   /* check results */\r
590 \r
591  if (erc = results(2)) return(erc);\r
592 \r
593  if (((FDC_STATUS[0] & 0xc0) == 0xc0 ) ||\r
594     ((FDC_STATUS[0] & 0xc0) == 0)) {\r
595 \r
596      /* Send Specify command to controller */\r
597          send_fdc(3);\r
598          send_fdc(GetParm(0));\r
599          send_fdc(GetParm(1));\r
600          return(ok);\r
601  }\r
602  return(ErcBadFDC);\r
603 }\r
604 \r
605 /*************************************************************\r
606  The ISR is very simple.\r
607  It just waits for an interrupt then sends a message to the\r
608  exchange where the FD Driver will be waiting.\r
609  The call that caused an interrupt to be generated reads the status\r
610  codes from the controller to determine if the last status was OK.\r
611 ****************************************************************/\r
612 static void interrupt fdisk_isr(void)\r
613 {\r
614         ISendMsg(fd_exch, 0xfffffffd, 0xfffffffd);\r
615         EndOfIRQ(6);\r
616 }\r
617 \r
618 /*=========================================================\r
619 Return the drive type for a specified drive.\r
620 ---------------------------------------------------------*/\r
621 \r
622 static U8 cmos_type (U8 drive_nr)\r
623 {\r
624  U8 drive_type;\r
625  drive_type = 0;\r
626  if (drive_nr)\r
627         drive_type = ReadCMOS(0x10) & 0x0f;\r
628  else\r
629         drive_type = (ReadCMOS(0x10) >> 4) & 0x0f;\r
630  return(drive_type);\r
631 }\r
632 \r
633 /*========================================================\r
634 Send a byte to the FDC.\r
635 --------------------------------------------------------*/\r
636 \r
637 static U32 send_fdc(U8 parm)\r
638 {\r
639 U32 i;\r
640 U8  b, bjunk;\r
641 \r
642 i = 100;  /* try 100 times to send FDC command */\r
643 do\r
644 {\r
645         b = InByte(MSR_PORT);                           /* Get I/O status byte */\r
646         MicroDelay(100);                                        /* 1.5 milliseconds between tries */\r
647         if (b & RQM) {\r
648                 if (b & DIO) {                                  /* He has something to send us... */\r
649                         bjunk = InByte(DATA_PORT);      /* Eat it! */\r
650                         MicroDelay(100);                        /* 1.5 milliseconds between I/O */\r
651                 }\r
652                 else {                                                  /* OK to send to him */\r
653                         OutByte(parm,DATA_PORT);\r
654                         MicroDelay(100);                        /* 1.5 milliseconds between I/O */\r
655                         return (ok);                            /* Sent it OK */\r
656                 }\r
657         }\r
658  }\r
659  while (i--);\r
660  return (ErcSendFDC);           /* return ERROR */\r
661 }\r
662 \r
663 /*==========================================================\r
664 Get the indexed value from the disk parameter table.\r
665 -------------------------------------------------------*/\r
666 \r
667 static U8 GetParm(U8 index)\r
668 {\r
669  return( fdisk_table [fdstatus.type_now][index]);\r
670 }\r
671 \r
672 \r
673 /******************************************\r
674 Wait for the hardware interrupt to occur.\r
675 Time-out and return if no interrupt.\r
676 ********************************************/\r
677 \r
678 static U32 wait_int(void)\r
679 {\r
680 U32 erc;\r
681 \r
682 /* Set alarm for 3 seconds */\r
683 erc = Alarm(fd_exch, 300);\r
684 \r
685 erc = WaitMsg(fd_exch, fd_msg);  /* Wait for message from ISR or Alarm */\r
686 if (erc) {\r
687         KillAlarm(fd_exch);\r
688         return(erc); /* BAD NEWS FROM KERNEL!!! */\r
689         }\r
690 \r
691 if (fd_msg[0] != 0xfffffffd)\r
692 {\r
693         return(fdstatus.erc = ErcFDCTimeOut);\r
694 }\r
695 else {\r
696         KillAlarm(fd_exch);\r
697         return(fdstatus.erc = ok);\r
698         }\r
699 }\r
700 \r
701 \r
702 /*******************************\r
703     Recalibrate the drive.\r
704 ********************************/\r
705 \r
706 static U32 recal(void)\r
707 {\r
708 U32 erc;\r
709 \r
710  erc = send_fdc(7);                                                     /* recal command (2 bytes) */\r
711  if (!erc) erc = send_fdc(fd_drive);            /* second byte is drive */\r
712 \r
713  /* wait for int on recal */\r
714  if (!erc) erc = wait_int();\r
715  if (!erc) erc = send_fdc(8);                           /* Sense command */\r
716  if (!erc) erc = results(2);                            /* expect 2 bytes */\r
717  if (!erc) {\r
718    if (FDC_STATUS[0] & 0x20)                            /* Check seek-Ok bit */\r
719    {\r
720     if (FDC_STATUS[1]) return(ErcBadRecal);     /* Was it track 0? */\r
721     return(ok);\r
722    }\r
723    else return (ErcBadSeek);                            /* Seek bit NOT set */\r
724  }\r
725  return(erc);\r
726 }\r
727 /*******************************************************\r
728  Move the head to the selected track.\r
729 *********************************************************/\r
730 \r
731 static U32 seek(void)\r
732 {\r
733 U32 erc;\r
734 \r
735  if ((seek_status & (1 << fd_drive)) == 0)      /* need recal */\r
736  {\r
737 \r
738   /* try 2 attemps at recalibrate, then error out */\r
739   if (recal())\r
740         if (erc = recal()) return (erc);                /* try again */\r
741 \r
742   seek_status |= (1 << fd_drive);                       /* recal was done */\r
743 \r
744   LAST_TRACK[fd_drive] = 0;                             /* clear track number */\r
745 \r
746   /* if we want track zero, then just wait for head and exit */\r
747 \r
748   if (fd_track == 0) {\r
749         wait_for_head();\r
750         return (ok);\r
751         }\r
752  }\r
753 \r
754  if (fdisk_table[fdstatus.type_now][14] != 0)\r
755         fd_track *= 2;\r
756  if (LAST_TRACK[fd_drive] == fd_track)          /* already there */\r
757          return(ok);\r
758 \r
759   /* update new position */\r
760  LAST_TRACK[fd_drive] = fd_track;\r
761 \r
762  erc = send_fdc(0x0f);                                          /*Seek Cmd */\r
763  if (!erc) erc = send_fdc((fd_head << 2) | fd_drive);\r
764  if (!erc) erc = send_fdc(fd_track);\r
765 \r
766   /* wait for int on seek command */\r
767  if (!erc) erc = wait_int();\r
768  if (!erc) erc = send_fdc(8);                           /* Sense command */\r
769  if (!erc) erc = results(2);                            /* expect 2 bytes */\r
770  if (!erc)\r
771         if (!(FDC_STATUS[0] & 0x20))                    /* Look for seek-Ok bit */\r
772         {\r
773      seek_status &= ~(1 << fd_drive);           /* needs recal! */\r
774          erc = ErcBadSeek;\r
775          return (erc);\r
776         }\r
777  if (!erc)\r
778          wait_for_head();\r
779  return (ok);\r
780 }\r
781 \r
782 /*=======================================================\r
783 Read a single byte from the data port (for status).\r
784 Returns TRUE if it got one.\r
785 --------------------------------------------------------*/\r
786 \r
787 static U32 read_data(U8 *pDataRet)\r
788 {\r
789 U16 tries;\r
790 U8 status;\r
791 \r
792         /* try 100 times while Busy */\r
793 \r
794         for (tries=0; tries<1000; tries++) {\r
795                 status = InByte(MSR_PORT);\r
796                 MicroDelay(100);\r
797                 if (status & RQM) {                                             /* RQM set */\r
798                         if (status & DIO) {                                     /* Direction IN if set */\r
799                                 *pDataRet = InByte(DATA_PORT);  /* Get the data byte */\r
800                                 MicroDelay(100);                                /* digest it...*/\r
801                                 return(TRUE);\r
802                         }\r
803                 }\r
804         }\r
805         return(FALSE);\r
806 }\r
807 \r
808 \r
809 /*=======================================================\r
810 Read anything from the controller following an interrupt.\r
811 This may include up to seven bytes of status.\r
812 --------------------------------------------------------*/\r
813 \r
814 static U32 results(U32 expect)\r
815 {\r
816 unsigned indx;\r
817 U8 status;\r
818 \r
819         indx = 0;\r
820         while (indx < expect) {\r
821                 if (read_data(&status)) {\r
822                         FDC_STATUS[indx++] = status;            /* save status */\r
823                         MicroDelay(100);\r
824                 }\r
825                 else return(ErcResults);\r
826         }\r
827         return(0);\r
828 }\r
829 \r
830 \r
831 \r
832 /****************************************************\r
833   Purge the FDC of any status it is waiting to send.\r
834 *****************************************************/\r
835 \r
836 static void purge_fdc (void)\r
837 {\r
838  U8 b;\r
839 \r
840         do {\r
841                 b = InByte(MSR_PORT);\r
842                 if (b & RQM) {\r
843                         if (b & DIO) {\r
844                                 InByte(DATA_PORT);  /* eat the byte */\r
845                                 MicroDelay(100);        /* Breath (1.5ms) */\r
846                          }\r
847                          else return;\r
848                 }\r
849                 else return;\r
850         }\r
851         while (1);\r
852 }\r
853 \r
854 /*======================================================\r
855 Wait for the head to settle after a seek for write.\r
856 MicroDelay is in 15 us increments, while settle value\r
857 is in milliseconds, so we turn millies into micros and\r
858 divide by 15 to get value for MicroDelay call.\r
859 -------------------------------------------------------*/\r
860 \r
861 static void wait_for_head(void)\r
862 {\r
863 U32 wait;\r
864 \r
865 if (fwrite) {\r
866  wait = GetParm(9); /* Head settle for write in milliseconds */\r
867  wait = (wait * 1000) / 15;\r
868  MicroDelay(wait);       /* delay in 15 us increments */\r
869  }\r
870 }\r
871 \r
872 /*=========================================================\r
873  Checks for a media change for selected drive.\r
874  Returns:\r
875    0, or ErcNewMedia\r
876 -------------------------------------------------------*/\r
877 \r
878 static U32 med_change(void)\r
879 {\r
880 \r
881 /* if no disk change indicated return OK */\r
882 \r
883  if (InByte(DIR_PORT) & DSKCHANGE_BIT) {\r
884         fdstatus.fNewMedia = 1;\r
885         return (ErcNewMedia);\r
886  }\r
887  else {\r
888         fdstatus.fNewMedia = 0;\r
889         return (ok);\r
890  }\r
891 }\r
892 \r
893 \r
894 /***********************************************************\r
895  Wait until an operation is complete, then accept the status\r
896  from the controller.\r
897 ************************************************************/\r
898 \r
899 static U32 get_fdc_status(void)\r
900 {\r
901 U32 erc;\r
902 \r
903  if (erc = wait_int()) return(erc);\r
904  if (erc = results(7)) return(erc);\r
905  if (!(FDC_STATUS[0] & 0xc0)) return(ok);\r
906  if ((FDC_STATUS[0] & 0xc0) == 0x80) return(ErcBadCmd);\r
907  if ((FDC_STATUS[0] & 0xc0) == 0xC0) return(ErcReadyLine);\r
908 \r
909  /* If we got here, get controller error status */\r
910 \r
911  if (FDC_STATUS[1] & BIT7)\r
912         erc = ErcSectNotFound;\r
913  else if (FDC_STATUS[1] & BIT5)\r
914         erc = ErcCRC;\r
915  else if (FDC_STATUS[1] & BIT4)\r
916         erc = ErcOverRun;\r
917  else if (FDC_STATUS[1] & BIT2)\r
918         erc = ErcSectNotFound;\r
919  else if (FDC_STATUS[1] & BIT1)\r
920         erc = ErcReadOnly;\r
921  else if (FDC_STATUS[1] & BIT0)\r
922         erc = ErcAddrMark;\r
923  else fdstatus.erc = ErcBadFDC;\r
924  return(erc);\r
925 }\r
926 \r
927 \r
928 /*************************************************************\r
929  This is called for Read, Write or Verify commmands. The only\r
930  differences in these 3 commands is the DMA type/direction and\r
931  a single byte FDC command.\r
932 *************************************************************/\r
933 \r
934 static U32 RdWrtVerf(U32 op)\r
935 {\r
936 U32 erc;\r
937 S8  dmatype;                    /* 0 = Verify, 1 = IN, 2 = Out */\r
938 S8  retrys;\r
939 U32 count;\r
940 \r
941 erc = 0;\r
942 retrys = 5;\r
943 count = fd_nr_sectors * 512;\r
944 \r
945 while ((fd_nr_sectors) && (!erc)) {\r
946 \r
947         switch(op) {\r
948                 case(CmdRead):                                  /* Read */\r
949                         dmatype = 1;\r
950                         fd_fdc_command = FDC_READ;\r
951                         break;\r
952                 case(2):                                        /* Write */\r
953                         fwrite = 1;\r
954                         dmatype = 2;\r
955                         CopyData(fd_pData, sectbuf, 512);\r
956                         fd_fdc_command = FDC_WRITE;\r
957                         break;\r
958                 case(3):                                        /* Verify */\r
959                         dmatype = 0;\r
960                         fd_fdc_command = FDC_READ;\r
961         }\r
962 \r
963         /*             PhyAddress  nBytes  ch   Type     Mode  */\r
964         erc = DmaSetUp(physectbuf, 512,    2,   dmatype, 1 );\r
965 \r
966         if (!erc) {\r
967           while(retrys--)\r
968           {\r
969            erc = 0;\r
970            if (!erc) erc = seek();\r
971            if (!erc) erc = send_fdc(fd_fdc_command);\r
972            if (!erc) erc = send_fdc(((fd_head <<2) & BIT2) | fd_drive);\r
973            if (!erc) erc = send_fdc(fd_track);\r
974            if (!erc) erc = send_fdc(fd_head);\r
975            if (!erc) erc = send_fdc(fd_sector);\r
976            if (!erc) erc = send_fdc(GetParm(3));\r
977            if (!erc) erc = send_fdc(GetParm(4));\r
978            if (!erc) erc = send_fdc(GetParm(5));\r
979            if (!erc) erc = send_fdc(GetParm(6));\r
980            if (!erc) erc = get_fdc_status();\r
981            if (!erc) break;                                     /* exit loop with good operation */\r
982            MicroDelay(200);                                     /* wait 3 milliseconds... */\r
983           } /* While statement for 5 retrys */\r
984         }\r
985 \r
986         if (!erc) {\r
987                 if (op==CmdRead)\r
988                         CopyData(sectbuf, fd_pData, 512);\r
989             fdstatus.blocks_done++;\r
990             fd_pData+=512;\r
991         --fd_nr_sectors;                                /* One less sector */\r
992         ++fd_sector;                                    /* Next sector please */\r
993         if (fd_sector > fdisk_table[fdstatus.type_now][4]) {\r
994                 fd_sector = 1;                          /* back to sect one */\r
995                 ++fd_head;                                      /* next head please */\r
996                 }\r
997         if (fd_head > 1) {\r
998                 fd_head = 0;                            /* back to head 0 */\r
999                 ++fd_track;                                     /* next track please */\r
1000                 }\r
1001         }\r
1002 } /* while */\r
1003 return(erc);\r
1004 }\r
1005 \r
1006 \r
1007 /*************************************************************\r
1008  This formats the track beginning at the block address given\r
1009  in dLBA. dLBA must always be a multiple of the number of\r
1010  sectors per track minus 1 for the disk type (usually 9 or 15).\r
1011 *************************************************************/\r
1012 \r
1013 static U32 format_track(void)\r
1014 {\r
1015 U32 erc;\r
1016 \r
1017  fd_fdc_command = FDC_FORMAT;\r
1018  fwrite = 1;            /* indicate write operation */\r
1019 \r
1020  erc = DmaSetUp(fd_pData, 512, 2, 1, 1);\r
1021  if (!erc) erc = send_fdc(3);                   /* specify command */\r
1022  if (!erc) erc = send_fdc(GetParm(0));\r
1023  if (!erc) erc = send_fdc(GetParm(1));\r
1024  if (!erc) erc = seek();\r
1025  if (!erc) erc = send_fdc(fd_fdc_command);\r
1026  if (!erc) erc = send_fdc(((fd_head <<2) & BIT2) | fd_drive);\r
1027  if (!erc) erc = send_fdc(GetParm(3));\r
1028  if (!erc) erc = send_fdc(GetParm(4));\r
1029  if (!erc) erc = send_fdc(GetParm(7));\r
1030  if (!erc) erc = send_fdc(GetParm(8));\r
1031  if (!erc) erc = get_fdc_status();\r
1032  return(erc);\r
1033 }\r
1034 \r
1035 /******************************************************************************\r
1036 Now begins the PUBLIC routines that are interfaced to for all DEVICE DRIVERS\r
1037 */\r
1038 \r
1039 /******************************************\r
1040 Called for all device operations.  This\r
1041 assigns physical device from logical number\r
1042 that outside callers use. For Floppy, 5=0\r
1043 and 6=1. This will check to make sure a\r
1044 drive type is assigned then calc physical\r
1045 head, cly, sector from dLBA.\r
1046 *******************************************/\r
1047 \r
1048 static U32 dev_op(U32 dDevice,\r
1049                     U32 dOpNum,\r
1050                     U32 dLBA,\r
1051                     U32 dnBlocks,\r
1052                     S8  *pData)\r
1053 {\r
1054 U32 erc, j;\r
1055 \r
1056  fd_pData = pData;\r
1057  fwrite = 0;\r
1058  fdstatus.blocks_done = 0;      /* Reset values in Status record */\r
1059  fdstatus.erc = 0;\r
1060 \r
1061  /* Set internal drive number */\r
1062 \r
1063  if (dDevice == 10)\r
1064         fd_drive = 0;\r
1065  else\r
1066         fd_drive = 1;\r
1067 \r
1068 /* Check to see if we have a leftover interrupt message from last\r
1069    command.  If so then we eat it (do nothing)\r
1070 */\r
1071 \r
1072  erc = CheckMsg(fd_exch, fd_msg);\r
1073 \r
1074  if (fd_drive==0){\r
1075         if (!type0) return(fdstatus.erc = ErcDriveType);\r
1076         fdstatus.type_now = type0;\r
1077         }\r
1078  else {\r
1079         if (!type1) return(fdstatus.erc = ErcDriveType);\r
1080         fdstatus.type_now = type1;\r
1081         }\r
1082 \r
1083  if (dLBA > fdstatus.BlocksMax) \r
1084         {\r
1085 /* for troubleshooting\r
1086             xprintf("dLBA: %d, BlocksMax: %d, DriveType: %d\r\n",\r
1087                  dLBA, fdstatus.BlocksMax, fdstatus.type_now);\r
1088 */\r
1089                 return(fdstatus.erc = ErcBadLBA);\r
1090         }\r
1091 \r
1092  fd_select(fd_drive);           /* turn motor on and select drive */\r
1093 \r
1094  /* set the data rate register to value the drive */\r
1095 \r
1096  OutByte(fdisk_table[fdstatus.type_now][12], DRR_PORT); /* Rate Cmd */\r
1097 \r
1098  /* make sure any residual status is unloaded */\r
1099 \r
1100  purge_fdc();\r
1101 \r
1102  /* if a media change sensed, update status */\r
1103 \r
1104 /*\r
1105 \r
1106  erc = med_change();\r
1107  if (erc) {\r
1108                 erc = recal();\r
1109                 fd_motoroff(fd_drive);\r
1110                 if (erc) {\r
1111                         return(fdstatus.erc=erc);\r
1112                 }\r
1113                 else\r
1114                         return(fdstatus.erc=ErcNewMedia);\r
1115  }\r
1116 \r
1117 */\r
1118 \r
1119  fdstatus.BlocksMax = rgSectorMax[fdstatus.type_now];           /* set max sectors */\r
1120 \r
1121  fd_nr_sectors = dnBlocks;\r
1122 \r
1123  /* calculate the cylinder, head and sector from LBA */\r
1124  /* 3 = nHeads, 4 = sectors/track */\r
1125 \r
1126  fd_track = dLBA / (GetParm(3) * GetParm(4));\r
1127  j = dLBA % (GetParm(3) * GetParm(4));\r
1128 \r
1129  /* We now know what cylinder, calc head and sector */\r
1130 \r
1131  fd_head = j / GetParm(4);\r
1132  fd_sector = j % GetParm(4) + 1;  /* sector numbers start at 1 !!!! */\r
1133 \r
1134 /* for troubleshooting...\r
1135  xprintf("\r\nDevice: %d, dLBA: %d, dOpNum: %d\r\n", dDevice, dLBA, dOpNum);\r
1136 */\r
1137 \r
1138  switch(dOpNum) {\r
1139         case(CmdNull):\r
1140                 fdstatus.erc = ok;                              /* Null Command */\r
1141                 break;\r
1142         case(CmdRead):                                          /* Read */\r
1143         case(CmdWrite):                                         /* Write */\r
1144         case(CmdVerify):                                        /* Verify */\r
1145                 fdstatus.erc = RdWrtVerf(dOpNum);\r
1146                 break;\r
1147         case(CmdFmtBlk):                                        /*      Format Block */\r
1148                 fdstatus.erc = ErcBadOp;\r
1149                 break;\r
1150         case(CmdFmtTrk):\r
1151                 break;                                                  /* Format Track */\r
1152         case(CmdSeekTrk):                                       /* Seek Track */\r
1153                 break;\r
1154         default:\r
1155                 fdstatus.erc = ErcBadOp;\r
1156                 break;\r
1157         }\r
1158 \r
1159  fd_motoroff(fd_drive);\r
1160  return(fdstatus.erc);\r
1161 }\r
1162 \r
1163 \r
1164 /******************************************\r
1165 Called for indepth status report on ctrlr\r
1166 and drive specified. Returns 80 byte block\r
1167 of data including the drive parameter\r
1168 block (a 16 byte structure with all the\r
1169 timing and size params of the drive).\r
1170 This is called by the PUBLIC call DeviceStat!\r
1171 *******************************************/\r
1172 \r
1173 static U32 dev_stat(U32 dDevice,\r
1174                           S8 * pStatRet,\r
1175                           U32 dStatusMax,\r
1176                           U32 *pdStatusRet)\r
1177 {\r
1178 S32 j;\r
1179 \r
1180  /* Set internal drive number */\r
1181  if (dDevice == 10)\r
1182         fd_drive=0;\r
1183  else fd_drive = 1;\r
1184 \r
1185  if (fd_drive==0){\r
1186         if (type0==0)\r
1187                 return(fdstatus.erc = ErcDriveType);\r
1188         fdstatus.type_now = type0;\r
1189     fdstatus.nCyl  = fdisk_table[type0][11]+1;  /* total physical cyls */\r
1190     fdstatus.nHead = 2;                                                 /* total heads on device */\r
1191     fdstatus.nSectors= fdisk_table[type0][4];   /* Sectors per track */\r
1192     fdstatus.nBPS = 512;                                                /* Bytes per sect */\r
1193  }\r
1194  else {\r
1195         if (type1==0) return(fdstatus.erc = ErcDriveType);\r
1196         fdstatus.type_now = type1;\r
1197     fdstatus.nCyl  = fdisk_table[type1][11]+1;  /* total physical cyls */\r
1198     fdstatus.nHead = 2;                                                 /* total heads on device */\r
1199     fdstatus.nSectors= fdisk_table[type1][4];   /* Sectors per track */\r
1200     fdstatus.nBPS = 512;                                                /* Bytes per sect */\r
1201  }\r
1202 \r
1203  /* set max sectors in status record */\r
1204 \r
1205  fdstatus.BlocksMax = rgSectorMax[fdstatus.type_now];\r
1206 \r
1207  /* copy in the 16 bytes of floppy specific data */\r
1208 \r
1209  CopyData(&fdisk_table[fdstatus.type_now], &fdstatus.params, 16);\r
1210 \r
1211  /* Update disk status bytes for status return */\r
1212 \r
1213  CopyData(&FDC_STATUS, &fdstatus.STATUS, 8);\r
1214 \r
1215  if (dStatusMax < sStatus) j = dStatusMax;\r
1216  else j = sStatus;\r
1217 \r
1218  CopyData(&fdstatus, pStatRet, j);              /* copy the status data */\r
1219 \r
1220  *pdStatusRet = j;              /* give em the size returned */\r
1221 \r
1222 return(0);\r
1223 }\r
1224 \r
1225 /******************************************\r
1226 Called to reset the floppy disk controller\r
1227 and to set the TYPE.  The type is one byte\r
1228 of the 64 byte status record passed to\r
1229 dev_init (offset 13). See the fdstatus struct.\r
1230 This should be called once for each floppy\r
1231 before it is used the first time after\r
1232 the driver is loaded, or after a fatal\r
1233 error is received (timeout etc.).\r
1234 This is called by the PUBLIC call DeviceInit!\r
1235 *******************************************/\r
1236 \r
1237 static S32 dev_init(U32  dDevice,\r
1238                          S8  *pInitData,\r
1239                          U32  sdInitData)\r
1240 \r
1241 {\r
1242 U32 i;\r
1243 \r
1244  /* Read the init status block in */\r
1245 \r
1246  if (sdInitData > sStatus) i = sStatus;         /* no more than 64 bytes! */\r
1247  else i = sdInitData;\r
1248 \r
1249  CopyData(pInitData, &FDStatTmp, i);            /* copy in their init data */\r
1250 \r
1251  if (dDevice == 10)\r
1252         fd_drive=0;                     /* Set internal drive number */\r
1253  else\r
1254         fd_drive = 1;\r
1255 \r
1256  if (fd_drive==0){                                              /* set up for drive 0 */\r
1257         type0 = FDStatTmp.type_now;\r
1258         if (type0==0) return(fdstatus.erc = ErcDriveType);\r
1259         fdstatus.type_now = type0;\r
1260  }\r
1261  else {\r
1262         type1 = FDStatTmp.type_now;\r
1263         if (type1==0) return(fdstatus.erc = ErcDriveType);\r
1264         fdstatus.type_now = type1;\r
1265  }\r
1266  fd_select(fd_drive);\r
1267  fdstatus.erc = FDC_reset();\r
1268  fd_motoroff(fd_drive);\r
1269  return(fdstatus.erc);\r
1270 }\r
1271 \r
1272 /*===========  THE END  =========================================*/\r