1 /* Floppy.C Floppy Disk Device Driver for MMURTL */
\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
9 #define U32 unsigned long
\r
11 #define U16 unsigned int
\r
13 #define U8 unsigned char
\r
16 /* MMURTL OS PROTOTYPES */
\r
18 extern far SpawnTask(S8 *pEntry,
\r
24 extern far U32 AllocExch(U32 *pExchRet);
\r
26 extern far U32 AllocDMAPage(U32 nPages, U8 **ppMemRet U32 *pPhyMemRet);
\r
28 extern far U32 InitDevDr(U32 dDevNum,
\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
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
60 /* THE FLOPPY INTERRUPT FUNCTION PROTOTYPE */
\r
62 static void interrupt fdisk_isr(void);
\r
64 /* PROTOTYPE FOR Floppy motor control task and associated stuff */
\r
66 static void fdmotor_task(void);
\r
67 static void fd_select(U32 drive);
\r
68 static void fd_motoroff(U32 drive);
\r
70 /* THE REST OF THE PROTOTYPES */
\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
90 /* The following 3 calls are required in every MMURTL device driver */
\r
92 static U32 dev_op(U32 dDevice,
\r
98 static U32 dev_stat(U32 dDevice,
\r
103 static U32 dev_init(U32 dDevNum,
\r
107 /* Near External for troubleshooting */
\r
109 extern long xprintf(char *fmt, ...);
\r
111 /* LOCAL DEFINITIONS */
\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
123 /* Error Codes to return */
\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
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
145 /* Commands accepted by driver */
\r
150 #define CmdVerify 3
\r
151 #define CmdFmtBlk 4
\r
152 #define CmdFmtTrk 5
\r
153 #define CmdSeekTrk 6
\r
155 /* FDC port definitions */
\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
163 /* FDC Return Status bit definitions */
\r
165 #define BUSY 0x10 /* was BIT4 */
\r
166 #define DSKCHANGE_BIT 0x80
\r
181 #define FDC_READ 0xe6
\r
182 #define FDC_WRITE 0xc5
\r
183 #define FDC_FORMAT 0x4d
\r
185 /* FDC DOR register bits */
\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
194 /* L O C A L C O N S T A N T S */
\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
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
212 14 - Double Step Flag (e.g., 360K in a 1.2Mb drive)
\r
217 static U8 fdisk_table[5][16]= {
\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
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
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
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
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
236 static U32 MotorStk[100]; /* 400 byte stack for motor task */
\r
237 static U32 MotorStkTop;
\r
239 static U8 fd_fdc_command;
\r
240 static U8 fd_drive;
\r
241 static U8 fd_nr_sectors;
\r
243 static U8 fd_sector;
\r
244 static U8 fd_track;
\r
245 static U8 seek_status;
\r
247 static S8 *fd_pData;
\r
249 /* current fdisk_table[x] for drive 0 & 1 */
\r
254 /* Record for 64 byte status and init record */
\r
258 static struct statstruct{
\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
272 U32 resvd4; /* 64 bytes total */
\r
275 static struct statstruct fdstatus;
\r
276 static struct statstruct FDStatTmp;
\r
278 static U8 FDC_STATUS[8]; /* status returned from FDC */
\r
279 static U8 LAST_TRACK[3]; /* holds last track number */
\r
281 static struct dcbtype {
\r
302 static struct dcbtype fdcb[2]; /* two floppy device control blcocks */
\r
305 /* Exch, msgs space, and vars for FD Motor task */
\r
306 static U8 dor_crnt; /* last value sent to DOR port */
\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
313 /* Exch and msgs space for FD ISR */
\r
314 static U32 fd_exch;
\r
315 static U32 fd_msg[2];
\r
317 static U32 rgSectorMax[10] = {0, 720, 2400, 1440, 2880,
\r
318 0, 0, 0, 0, 0}; /* set max sectors */
\r
320 static U8 *sectbuf; /* sector buffer, 1 Page with AllocDMAPage */
\r
321 static U32 physectbuf; /* physical address of DMA buffer */
\r
323 /*======================================================*/
\r
324 /*=================== START OF CODE ====================*/
\r
325 /*======================================================*/
\r
327 static void enable_ints(void)
\r
335 static void disable_ints(void)
\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
353 **********************************************************************/
\r
355 static void fdmotor_task(void)
\r
361 Sleep(300); /* 3 seconds */
\r
363 GetTimerTick(&fd_newtick);
\r
365 if ((fd_newtick - fd_tick) > 300) { /* not used in last 3 seconds */
\r
367 if ((!motor0_want) && (dor_crnt & FD_MOTOR0))
\r
368 { /* They want 0 off */
\r
370 dor_crnt &= ~FD_MOTOR0;
\r
371 OutByte( dor_crnt, DOR_PORT);
\r
375 if ((!motor1_want) && (dor_crnt & FD_MOTOR1)) { /* They want 1 off */
\r
377 dor_crnt &= ~FD_MOTOR1;
\r
378 OutByte( dor_crnt, DOR_PORT);
\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
394 static void fd_select(U32 drive)
\r
399 GetTimerTick(&fd_tick); /* update last time selected */
\r
404 if (drive) { /* drive 1 */
\r
405 if (!(dor_crnt & 0x01)) {
\r
407 dor_crnt |= 0x01; /* select 1 */
\r
409 if (!(dor_crnt & FD_MOTOR1)) {
\r
412 dor_crnt |= FD_MOTOR1; /* motor 1 on */
\r
416 if (dor_crnt & 0x01) {
\r
418 dor_crnt &= 0xFE; /* select 0 (turn 1 off) */
\r
420 if (!(dor_crnt & FD_MOTOR0)) {
\r
423 dor_crnt |= FD_MOTOR0; /* motor 0 on */
\r
431 OutByte( dor_crnt, DOR_PORT);
\r
436 Sleep(33); /* delay 1/3th second if not already on! */
\r
442 /*******************************************
\r
443 Sets motor_want bit for specified motor.
\r
444 *******************************************/
\r
446 static void fd_motoroff(U32 motor)
\r
448 /* Set what we want so motor task will know */
\r
451 motor1_want = FALSE;
\r
453 motor0_want = FALSE;
\r
456 /*********************************************************
\r
457 This is called ONCE to initialize the Driver.
\r
458 *********************************************************/
\r
460 U32 fdisk_setup(void)
\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
469 fdcb[0].fDevReent = 0; /* not reentrant */
\r
470 fdcb[0].fSingleUser = 0;
\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
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
486 erc = AllocExch(&fd_exch); /* allocates exchanges for messaging */
\r
489 erc = AllocDMAPage(1, §buf, &physectbuf);
\r
493 /* Used to test DMA memory call
\r
494 xprintf("DMA Address: %08x, DMA Physical : %08x\r\n", sectbuf, physectbuf);
\r
498 /* Set up the motor task */
\r
500 SpawnTask( &fdmotor_task, 18, 0, &MotorStkTop, 1);
\r
502 SetIRQVector(6, &fdisk_isr);
\r
503 UnMaskIRQ(6); /* unmask IRQ 6 */
\r
505 type0 = cmos_type(0);
\r
508 fdcb[0].nBPB = 512;
\r
509 fdcb[0].nBlocks = rgSectorMax[type0];
\r
512 type1 = cmos_type(1);
\r
515 fdcb[1].nBPB = 512;
\r
516 fdcb[1].nBlocks = rgSectorMax[type1];
\r
519 /* reset the FDC */
\r
521 if (fdstatus.erc= FDC_reset())
\r
522 return (fdstatus.erc);
\r
524 /* now we attempt to select and recal both drives */
\r
527 fd_drive = 0; /* select drive 0 */
\r
531 erc = recal(); /* try twice */
\r
534 if (erc) return (fdstatus.erc = erc);
\r
538 fd_drive = 1; /* select drive 1 */
\r
541 erc = recal(); /* try twice */
\r
542 if (erc) fdcb[1].type = 0;
\r
546 fdcb[0].pDevOp = &dev_op;
\r
547 fdcb[0].pDevInit = &dev_init;
\r
548 fdcb[0].pDevSt = &dev_stat;
\r
550 fdcb[1].pDevOp = &dev_op;
\r
551 fdcb[1].pDevInit = &dev_init;
\r
552 fdcb[1].pDevSt = &dev_stat;
\r
554 return(erc = InitDevDr(10, &fdcb, 2, 1));
\r
558 /************************************************************
\r
559 Reset the disk system
\r
560 *************************************************************/
\r
562 static U32 FDC_reset(void)
\r
566 seek_status = 0; /* Set to Recal on next seek */
\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
576 /* wait for interrupt and return if error (timeout...?) */
\r
578 if (erc = wait_int())
\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
587 if (erc = send_fdc(8)) return(erc);
\r
589 /* check results */
\r
591 if (erc = results(2)) return(erc);
\r
593 if (((FDC_STATUS[0] & 0xc0) == 0xc0 ) ||
\r
594 ((FDC_STATUS[0] & 0xc0) == 0)) {
\r
596 /* Send Specify command to controller */
\r
598 send_fdc(GetParm(0));
\r
599 send_fdc(GetParm(1));
\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
614 ISendMsg(fd_exch, 0xfffffffd, 0xfffffffd);
\r
618 /*=========================================================
\r
619 Return the drive type for a specified drive.
\r
620 ---------------------------------------------------------*/
\r
622 static U8 cmos_type (U8 drive_nr)
\r
627 drive_type = ReadCMOS(0x10) & 0x0f;
\r
629 drive_type = (ReadCMOS(0x10) >> 4) & 0x0f;
\r
630 return(drive_type);
\r
633 /*========================================================
\r
634 Send a byte to the FDC.
\r
635 --------------------------------------------------------*/
\r
637 static U32 send_fdc(U8 parm)
\r
642 i = 100; /* try 100 times to send FDC command */
\r
645 b = InByte(MSR_PORT); /* Get I/O status byte */
\r
646 MicroDelay(100); /* 1.5 milliseconds between tries */
\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
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
660 return (ErcSendFDC); /* return ERROR */
\r
663 /*==========================================================
\r
664 Get the indexed value from the disk parameter table.
\r
665 -------------------------------------------------------*/
\r
667 static U8 GetParm(U8 index)
\r
669 return( fdisk_table [fdstatus.type_now][index]);
\r
673 /******************************************
\r
674 Wait for the hardware interrupt to occur.
\r
675 Time-out and return if no interrupt.
\r
676 ********************************************/
\r
678 static U32 wait_int(void)
\r
682 /* Set alarm for 3 seconds */
\r
683 erc = Alarm(fd_exch, 300);
\r
685 erc = WaitMsg(fd_exch, fd_msg); /* Wait for message from ISR or Alarm */
\r
687 KillAlarm(fd_exch);
\r
688 return(erc); /* BAD NEWS FROM KERNEL!!! */
\r
691 if (fd_msg[0] != 0xfffffffd)
\r
693 return(fdstatus.erc = ErcFDCTimeOut);
\r
696 KillAlarm(fd_exch);
\r
697 return(fdstatus.erc = ok);
\r
702 /*******************************
\r
703 Recalibrate the drive.
\r
704 ********************************/
\r
706 static U32 recal(void)
\r
710 erc = send_fdc(7); /* recal command (2 bytes) */
\r
711 if (!erc) erc = send_fdc(fd_drive); /* second byte is drive */
\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
718 if (FDC_STATUS[0] & 0x20) /* Check seek-Ok bit */
\r
720 if (FDC_STATUS[1]) return(ErcBadRecal); /* Was it track 0? */
\r
723 else return (ErcBadSeek); /* Seek bit NOT set */
\r
727 /*******************************************************
\r
728 Move the head to the selected track.
\r
729 *********************************************************/
\r
731 static U32 seek(void)
\r
735 if ((seek_status & (1 << fd_drive)) == 0) /* need recal */
\r
738 /* try 2 attemps at recalibrate, then error out */
\r
740 if (erc = recal()) return (erc); /* try again */
\r
742 seek_status |= (1 << fd_drive); /* recal was done */
\r
744 LAST_TRACK[fd_drive] = 0; /* clear track number */
\r
746 /* if we want track zero, then just wait for head and exit */
\r
748 if (fd_track == 0) {
\r
754 if (fdisk_table[fdstatus.type_now][14] != 0)
\r
756 if (LAST_TRACK[fd_drive] == fd_track) /* already there */
\r
759 /* update new position */
\r
760 LAST_TRACK[fd_drive] = fd_track;
\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
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
771 if (!(FDC_STATUS[0] & 0x20)) /* Look for seek-Ok bit */
\r
773 seek_status &= ~(1 << fd_drive); /* needs recal! */
\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
787 static U32 read_data(U8 *pDataRet)
\r
792 /* try 100 times while Busy */
\r
794 for (tries=0; tries<1000; tries++) {
\r
795 status = InByte(MSR_PORT);
\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
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
814 static U32 results(U32 expect)
\r
820 while (indx < expect) {
\r
821 if (read_data(&status)) {
\r
822 FDC_STATUS[indx++] = status; /* save status */
\r
825 else return(ErcResults);
\r
832 /****************************************************
\r
833 Purge the FDC of any status it is waiting to send.
\r
834 *****************************************************/
\r
836 static void purge_fdc (void)
\r
841 b = InByte(MSR_PORT);
\r
844 InByte(DATA_PORT); /* eat the byte */
\r
845 MicroDelay(100); /* Breath (1.5ms) */
\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
861 static void wait_for_head(void)
\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
872 /*=========================================================
\r
873 Checks for a media change for selected drive.
\r
876 -------------------------------------------------------*/
\r
878 static U32 med_change(void)
\r
881 /* if no disk change indicated return OK */
\r
883 if (InByte(DIR_PORT) & DSKCHANGE_BIT) {
\r
884 fdstatus.fNewMedia = 1;
\r
885 return (ErcNewMedia);
\r
888 fdstatus.fNewMedia = 0;
\r
894 /***********************************************************
\r
895 Wait until an operation is complete, then accept the status
\r
896 from the controller.
\r
897 ************************************************************/
\r
899 static U32 get_fdc_status(void)
\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
909 /* If we got here, get controller error status */
\r
911 if (FDC_STATUS[1] & BIT7)
\r
912 erc = ErcSectNotFound;
\r
913 else if (FDC_STATUS[1] & BIT5)
\r
915 else if (FDC_STATUS[1] & BIT4)
\r
917 else if (FDC_STATUS[1] & BIT2)
\r
918 erc = ErcSectNotFound;
\r
919 else if (FDC_STATUS[1] & BIT1)
\r
921 else if (FDC_STATUS[1] & BIT0)
\r
923 else fdstatus.erc = ErcBadFDC;
\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
934 static U32 RdWrtVerf(U32 op)
\r
937 S8 dmatype; /* 0 = Verify, 1 = IN, 2 = Out */
\r
943 count = fd_nr_sectors * 512;
\r
945 while ((fd_nr_sectors) && (!erc)) {
\r
948 case(CmdRead): /* Read */
\r
950 fd_fdc_command = FDC_READ;
\r
952 case(2): /* Write */
\r
955 CopyData(fd_pData, sectbuf, 512);
\r
956 fd_fdc_command = FDC_WRITE;
\r
958 case(3): /* Verify */
\r
960 fd_fdc_command = FDC_READ;
\r
963 /* PhyAddress nBytes ch Type Mode */
\r
964 erc = DmaSetUp(physectbuf, 512, 2, dmatype, 1 );
\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
988 CopyData(sectbuf, fd_pData, 512);
\r
989 fdstatus.blocks_done++;
\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
998 fd_head = 0; /* back to head 0 */
\r
999 ++fd_track; /* next track please */
\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
1013 static U32 format_track(void)
\r
1017 fd_fdc_command = FDC_FORMAT;
\r
1018 fwrite = 1; /* indicate write operation */
\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
1035 /******************************************************************************
\r
1036 Now begins the PUBLIC routines that are interfaced to for all DEVICE DRIVERS
\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
1048 static U32 dev_op(U32 dDevice,
\r
1058 fdstatus.blocks_done = 0; /* Reset values in Status record */
\r
1061 /* Set internal drive number */
\r
1063 if (dDevice == 10)
\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
1072 erc = CheckMsg(fd_exch, fd_msg);
\r
1075 if (!type0) return(fdstatus.erc = ErcDriveType);
\r
1076 fdstatus.type_now = type0;
\r
1079 if (!type1) return(fdstatus.erc = ErcDriveType);
\r
1080 fdstatus.type_now = type1;
\r
1083 if (dLBA > fdstatus.BlocksMax)
\r
1085 /* for troubleshooting
\r
1086 xprintf("dLBA: %d, BlocksMax: %d, DriveType: %d\r\n",
\r
1087 dLBA, fdstatus.BlocksMax, fdstatus.type_now);
\r
1089 return(fdstatus.erc = ErcBadLBA);
\r
1092 fd_select(fd_drive); /* turn motor on and select drive */
\r
1094 /* set the data rate register to value the drive */
\r
1096 OutByte(fdisk_table[fdstatus.type_now][12], DRR_PORT); /* Rate Cmd */
\r
1098 /* make sure any residual status is unloaded */
\r
1102 /* if a media change sensed, update status */
\r
1106 erc = med_change();
\r
1109 fd_motoroff(fd_drive);
\r
1111 return(fdstatus.erc=erc);
\r
1114 return(fdstatus.erc=ErcNewMedia);
\r
1119 fdstatus.BlocksMax = rgSectorMax[fdstatus.type_now]; /* set max sectors */
\r
1121 fd_nr_sectors = dnBlocks;
\r
1123 /* calculate the cylinder, head and sector from LBA */
\r
1124 /* 3 = nHeads, 4 = sectors/track */
\r
1126 fd_track = dLBA / (GetParm(3) * GetParm(4));
\r
1127 j = dLBA % (GetParm(3) * GetParm(4));
\r
1129 /* We now know what cylinder, calc head and sector */
\r
1131 fd_head = j / GetParm(4);
\r
1132 fd_sector = j % GetParm(4) + 1; /* sector numbers start at 1 !!!! */
\r
1134 /* for troubleshooting...
\r
1135 xprintf("\r\nDevice: %d, dLBA: %d, dOpNum: %d\r\n", dDevice, dLBA, dOpNum);
\r
1140 fdstatus.erc = ok; /* Null Command */
\r
1142 case(CmdRead): /* Read */
\r
1143 case(CmdWrite): /* Write */
\r
1144 case(CmdVerify): /* Verify */
\r
1145 fdstatus.erc = RdWrtVerf(dOpNum);
\r
1147 case(CmdFmtBlk): /* Format Block */
\r
1148 fdstatus.erc = ErcBadOp;
\r
1151 break; /* Format Track */
\r
1152 case(CmdSeekTrk): /* Seek Track */
\r
1155 fdstatus.erc = ErcBadOp;
\r
1159 fd_motoroff(fd_drive);
\r
1160 return(fdstatus.erc);
\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
1173 static U32 dev_stat(U32 dDevice,
\r
1180 /* Set internal drive number */
\r
1181 if (dDevice == 10)
\r
1183 else fd_drive = 1;
\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
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
1203 /* set max sectors in status record */
\r
1205 fdstatus.BlocksMax = rgSectorMax[fdstatus.type_now];
\r
1207 /* copy in the 16 bytes of floppy specific data */
\r
1209 CopyData(&fdisk_table[fdstatus.type_now], &fdstatus.params, 16);
\r
1211 /* Update disk status bytes for status return */
\r
1213 CopyData(&FDC_STATUS, &fdstatus.STATUS, 8);
\r
1215 if (dStatusMax < sStatus) j = dStatusMax;
\r
1218 CopyData(&fdstatus, pStatRet, j); /* copy the status data */
\r
1220 *pdStatusRet = j; /* give em the size returned */
\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
1237 static S32 dev_init(U32 dDevice,
\r
1244 /* Read the init status block in */
\r
1246 if (sdInitData > sStatus) i = sStatus; /* no more than 64 bytes! */
\r
1247 else i = sdInitData;
\r
1249 CopyData(pInitData, &FDStatTmp, i); /* copy in their init data */
\r
1251 if (dDevice == 10)
\r
1252 fd_drive=0; /* Set internal drive number */
\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
1262 type1 = FDStatTmp.type_now;
\r
1263 if (type1==0) return(fdstatus.erc = ErcDriveType);
\r
1264 fdstatus.type_now = type1;
\r
1266 fd_select(fd_drive);
\r
1267 fdstatus.erc = FDC_reset();
\r
1268 fd_motoroff(fd_drive);
\r
1269 return(fdstatus.erc);
\r
1272 /*=========== THE END =========================================*/
\r