2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
5 Desc: Intel HW floppy stuff
9 #include <exec/types.h>
10 #include <devices/trackdisk.h>
12 #include "trackdisk_device.h"
13 #include "trackdisk_hw.h"
16 #include <aros/debug.h>
20 #define ioStd(x) ((struct IOStdReq *)x)
22 /*-------------------- Hardware section --------------------*/
24 /* All procedures here are based on Intel documentation */
26 #define TD_DMA 2 /* Define DMA channel */
28 /* Wait for status flags to settle */
29 int td_waitUntilReady(void)
33 for (i
=0; i
< 10000; i
++)
35 status
= inb(FDC_MSR
);
36 if (status
& MSRF_RQM
)
43 /* Start motor and select drive */
44 void td_motoron(UBYTE unitnum
, struct TrackDiskBase
*tdb
, BOOL wait
)
48 dor
= tdb
->td_dor
& (DORF_MOT0
|DORF_MOT1
);
49 dor
|= DORF_DMA
| DORF_RESET
| (DORF_MOT0
<< unitnum
) | unitnum
;
53 /* Did the user want to wait for spinup? */
61 void td_motoroff(UBYTE unitnum
, struct TrackDiskBase
*tdb
)
63 tdb
->td_dor
= tdb
->td_dor
& ~(DORF_MOT0
<< unitnum
);
64 outb(tdb
->td_dor
,FDC_DOR
);
67 // Send byte to drive. Returns DriveInUse error if busy, 0 otherwise
68 int td_sendbyte(unsigned char byte
, struct TrackDiskBase
*TDBase
)
74 if ((inb(FDC_MSR
) & (MSRF_RQM
| MSRF_DIO
))==MSRF_RQM
)
80 D(bug("TD: sendbyte failed\n"));
81 return TDERR_DriveInUse
;
85 // Get byte from drive. Returns the same as td_sendbyte
86 int td_getbyte(unsigned char *byte
, struct TrackDiskBase
*TDBase
)
92 if ((inb(FDC_MSR
) & (MSRF_RQM
| MSRF_DIO
))==(MSRF_RQM
| MSRF_DIO
))
94 *byte
= inb(FDC_FIFO
);
98 return TDERR_DriveInUse
;
101 // Send full command to drive
102 int td_sendcommand(struct TrackDiskBase
*TDBase
)
106 if (TDBase
->td_comsize
)
109 for (i
=0; (i
< TDBase
->td_comsize
) && !err
; i
++)
111 err
= td_sendbyte(TDBase
->td_rawcom
[i
], TDBase
);
114 // If there was error just reset drive
119 D(bug("[Floppy] Resending command\n"));
123 for (i
=0; (i
< TDBase
->td_comsize
) && !err
; i
++)
124 err
= td_sendbyte(TDBase
->td_rawcom
[i
], TDBase
);
129 int td_sendcommand2(struct TrackDiskBase
*TDBase
)
133 if (TDBase
->td_comsize2
)
136 for (i
=0; (i
< TDBase
->td_comsize2
) && !err
; i
++)
138 err
= td_sendbyte(TDBase
->td_rawcom2
[i
], TDBase
);
144 /* Wait for interrupt */
145 int td_waitint(struct TrackDiskBase
*TDBase
, UBYTE results
,BOOL sensei
)
150 TDBase
->td_inttmo
= 150; // Each IO command has 1s to complete before error occurs
151 sigs
= Wait((1L << TDBase
->td_IntBit
)|(1L<<TDBase
->td_TmoBit
));
152 if (sigs
& (1L << TDBase
->td_IntBit
))
154 TDBase
->td_inttmo
= 0;
158 while ( (i
<results
) && (inb(FDC_MSR
) & MSRF_CMDBSY
) )
160 td_getbyte(&TDBase
->td_result
[i
],TDBase
);
163 /* Do we run a Sense Interrupt? */
166 td_sendbyte(FD_SENSEI
,TDBase
);
167 td_getbyte(&TDBase
->td_sr0
,TDBase
);
168 td_getbyte(&TDBase
->td_pcn
,TDBase
);
172 return TDERR_NotSpecified
;
176 int td_readstatus(struct TrackDiskBase
*TDBase
, int num
)
179 if (num
> 7) num
= 7;
180 for (i
=0; (i
< num
) && !err
; i
++)
182 err
= td_getbyte(&TDBase
->td_result
[i
], TDBase
);
187 #define MORE_OUTPUT 1
189 int needMoreOutput(struct TrackDiskBase
*TDBase
)
193 if ((status
=td_waitUntilReady())<0)
195 if ((status
& (MSRF_RQM
|MSRF_DIO
|MSRF_NONDMA
)) == MSRF_RQM
)
197 td_readstatus(TDBase
, 7);
201 int td_configure(struct TrackDiskBase
*TDBase
)
204 // Do Configure (enable FIFO and turn polling off)
205 // We also enable implied seeks
206 td_sendbyte(FD_CONFIGURE
, TDBase
);
207 if (needMoreOutput(TDBase
) == MORE_OUTPUT
)
209 td_sendbyte(0, TDBase
);
210 td_sendbyte(0x1a, TDBase
);
211 td_sendbyte(0, TDBase
);
217 UBYTE
td_getprotstatus(UBYTE unitnum
, struct TrackDiskBase
*tdb
)
219 /* Issue Sense Drive Status command to check whether write is possible */
221 tdb
->td_rawcom
[0] = FD_GETSTATUS
;
222 tdb
->td_rawcom
[1] = unitnum
;
224 td_readstatus(tdb
,1);
225 if (tdb
->td_result
[0] & 0x40)
226 return(TDU_READONLY
);
228 return(TDU_WRITABLE
);
233 * Reset FDC, and initialize it again
236 int td_dinit(struct TrackDiskBase
*TDBase
)
241 // Assert RESET in DOR
242 dor
= TDBase
->td_dor
& 0x39;
245 // deassert RESET signal
247 TDBase
->td_dor
= dor
;
249 td_waitint(TDBase
,0,FALSE
);
250 /* Issue configure */
251 td_configure(TDBase
);
252 // programm data rate
254 // issue Sense Interrupt Status (loop 4 times)
257 TDBase
->td_comsize
= 1;
258 TDBase
->td_rawcom
[0] = FD_SENSEI
;
259 td_sendcommand(TDBase
);
260 td_readstatus(TDBase
, 2);
263 TDBase
->td_comsize2
= 3;
264 TDBase
->td_rawcom2
[0] = FD_SPECIFY
;
265 TDBase
->td_rawcom2
[1] = DP_SPEC1
;
266 TDBase
->td_rawcom2
[2] = DP_SPEC2
;
267 td_sendcommand2(TDBase
);
271 // Recalibrate (type != 0) or seek (type = 0) command.
272 int td_recalibrate(unsigned char unitn
, char type
, int sector
, struct TrackDiskBase
*TDBase
)
276 /* If we are not running already, this will spin up. */
277 td_motoron(unitn
,TDBase
,FALSE
);
282 TDBase
->td_comsize
= 3;
283 TDBase
->td_rawcom
[0] = FD_SPECIFY
;
284 TDBase
->td_rawcom
[1] = DP_SPEC1
;
285 TDBase
->td_rawcom
[2] = DP_SPEC2
;
286 td_sendcommand(TDBase
);
287 /* Issue recalibrate */
288 TDBase
->td_comsize
= 2;
289 TDBase
->td_rawcom
[0] = FD_RECALIBRATE
;
290 TDBase
->td_rawcom
[1] = unitn
;
291 err
= td_sendcommand(TDBase
);
296 /* Issue seek command */
297 TDBase
->td_comsize
= 3;
298 TDBase
->td_rawcom
[0] = FD_SEEK
;
299 TDBase
->td_rawcom
[1] = unitn
; /* Unit number */
300 TDBase
->td_rawcom
[2] = sector
; /* New Track Number */
301 err
= td_sendcommand(TDBase
);
305 /* Wait for interrupt */
306 err
= td_waitint(TDBase
,0,TRUE
);
309 /* if drive doesn't report any error return 0 */
310 if (((TDBase
->td_sr0
& 0xf0) == 0x20) && (TDBase
->td_pcn
== sector
))
312 /* Store current track */
313 if (TDBase
->td_Units
[unitn
])
314 TDBase
->td_Units
[unitn
]->pub
.tdu_CurrTrk
=TDBase
->td_pcn
;
317 /* SeekError otherwise */
318 return TDERR_SeekError
;
324 int td_rseek(UBYTE unitn
, UBYTE dir
, UBYTE cyls
, struct TrackDiskBase
*TDBase
)
328 /* Select the unit, start motor */
329 td_motoron(unitn
,TDBase
,FALSE
);
333 /* Issue seek command */
334 TDBase
->td_comsize
= 3;
335 TDBase
->td_rawcom
[0] = FD_RSEEK_OUT
;
336 TDBase
->td_rawcom
[1] = unitn
; /* Unit number */
337 TDBase
->td_rawcom
[2] = cyls
; /* New Track Number */
338 err
= td_sendcommand(TDBase
);
342 TDBase
->td_comsize
= 3;
343 TDBase
->td_rawcom
[0] = FD_RSEEK_IN
;
344 TDBase
->td_rawcom
[1] = unitn
;
345 TDBase
->td_rawcom
[2] = cyls
;
346 err
= td_sendcommand(TDBase
);
350 /* Wait for interrupt */
351 err
= td_waitint(TDBase
,0,TRUE
);
354 /* if drive doesn't report any error return 0 */
355 if (((TDBase
->td_sr0
& 0xf0) == 0x20))
357 if (TDBase
->td_Units
[unitn
])
358 TDBase
->td_Units
[unitn
]->pub
.tdu_CurrTrk
= TDBase
->td_pcn
;
361 /* SeekError otherwise */
362 return TDERR_SeekError
;
368 /************************************************
370 Descr.: checks for disk in drive
372 Output: 0 - disk in drive
374 Note : The correct drive must be selected
375 *************************************************/
376 UBYTE
td_getDiskChange( void )
378 return(inb(FDC_DIR
)>>DIRB_DCHG
);
382 int td_readwritetrack(UBYTE unitnum
, char cyl
, char hd
, char mode
, struct TrackDiskBase
*TDBase
)
384 int rwcnt
; // Read/Write retries
385 int skcnt
; // Seek retries
389 skcnt
= TDBase
->td_Units
[unitnum
]->pub
.tdu_RetryCnt
;
390 /* Program data rate */
391 /* Gentlemen, start your engines */
392 TDBase
->td_Units
[unitnum
]->tdu_MotorOn
= 1;
393 td_motoron(unitnum
,TDBase
,TRUE
);
394 outb(0, FDC_CCR
); // 500kbit/s only!
397 rwcnt
= TDBase
->td_Units
[unitnum
]->pub
.tdu_RetryCnt
;
399 /* If we are on the correct track, dont seek */
400 if (TDBase
->td_Units
[unitnum
]->pub
.tdu_CurrTrk
!= cyl
)
401 err
= td_recalibrate(unitnum
,0,cyl
,TDBase
);
409 clear_dma_ff(TD_DMA
);
410 // Should place some cache flush in future (when cache implemented)
411 set_dma_addr(TD_DMA
, (ULONG
)(IPTR
)(TDBase
->td_Units
[unitnum
]->td_DMABuffer
));
412 set_dma_count(TD_DMA
, DP_SECTORS
*512);
413 set_dma_mode(TD_DMA
, (mode
== FD_READ
) ? DMA_MODE_READ
: DMA_MODE_WRITE
);
415 /* Issue read/write command */
416 TDBase
->td_comsize
= 9;
417 buf
= TDBase
->td_rawcom
;
418 *buf
++ = mode
; // Command
419 *buf
++ = unitnum
| (hd
<< 2); // Drive Select
420 *buf
++ = cyl
; // Cylinder
422 *buf
++ = 1; // First sector
423 *buf
++ = DP_SSIZE
; // Sector size
424 *buf
++ = DP_SECTORS
; // Last sector
425 *buf
++ = DP_GAP1
; // Gap length
427 /* Command prepared, now send it */
428 td_sendcommand(TDBase
);
429 /* Wait for end phase */
430 err
= td_waitint(TDBase
,7,FALSE
);
433 /* Check if everything went OK */
434 if (!(TDBase
->td_result
[0] & 0xc0))
439 /* Something went wrong. Let's see what. */
440 /* if err != then timeout err. */
443 err
= TDERR_NotSpecified
;
444 if (TDBase
->td_result
[1] & 0x80)
445 err
= TDERR_TooFewSecs
;
446 else if (TDBase
->td_result
[1] & 0x20)
448 err
= TDERR_BadHdrSum
;
449 if (TDBase
->td_result
[2] & 0x20)
450 err
= TDERR_BadSecSum
;
452 else if (TDBase
->td_result
[1] & 0x04)
453 err
= TDERR_TooFewSecs
;
457 td_recalibrate(unitnum
,1,0,TDBase
);
462 int td_update(struct TDU
*unit
, struct TrackDiskBase
*TDBase
)
466 if (unit
->tdu_flags
& TDUF_WRITE
)
468 if (td_getDiskChange())
469 return TDERR_DiskChanged
; /* No disk in drive */
470 err
=td_readwritetrack(unit
->tdu_UnitNum
, unit
->tdu_lastcyl
, unit
->tdu_lasthd
, FD_WRITE
, TDBase
);
473 unit
->tdu_flags
&= ~TDUF_WRITE
;
478 int td_readtrack(struct IOExtTD
*iotd
, UBYTE cyl
, UBYTE hd
, struct TrackDiskBase
*TDBase
)
480 struct TDU
*unit
= (struct TDU
*)iotd
->iotd_Req
.io_Unit
;
483 if (td_getDiskChange())
484 return TDERR_DiskChanged
; /* No disk in drive */
485 if ((unit
->tdu_lastcyl
!=cyl
) || (unit
->tdu_lasthd
!=hd
))
487 err
=td_update(unit
, TDBase
);
492 err
=td_readwritetrack(unit
->tdu_UnitNum
, cyl
, hd
, FD_READ
, TDBase
);
495 unit
->tdu_lastcyl
=cyl
;
501 int td_read(struct IOExtTD
*iotd
, struct TrackDiskBase
*TDBase
)
503 struct TDU
*unit
=(struct TDU
*)iotd
->iotd_Req
.io_Unit
;
504 int cyl
, hd
, sec
; // C/H/S for operation
509 if (unit
->tdu_DiskIn
== TDU_NODISK
)
511 iotd
->iotd_Req
.io_Actual
= 0;
512 return TDERR_DiskChanged
;
515 sec
= iotd
->iotd_Req
.io_Offset
>> 9; // sector is wrong right now (LBA)
516 cyl
= (sec
>> 1) / DP_SECTORS
; // cyl contains real cyl number
517 sec
%= 2*DP_SECTORS
; // sector on track (on both sides)
518 hd
= sec
/ DP_SECTORS
; // head number
519 sec
%= DP_SECTORS
; // real sector number
520 remain
= iotd
->iotd_Req
.io_Length
;
521 while (length
<iotd
->iotd_Req
.io_Length
)
523 size
= (DP_SECTORS
*512) - (sec
*512);
526 err
=td_readtrack(iotd
, (UBYTE
)cyl
, (UBYTE
)hd
, TDBase
);
529 iotd
->iotd_Req
.io_Actual
= length
;
532 CopyMem(unit
->td_DMABuffer
+(sec
*512), iotd
->iotd_Req
.io_Data
+length
, size
);
544 iotd
->iotd_Req
.io_Actual
= iotd
->iotd_Req
.io_Length
;
548 int td_write(struct IOExtTD
*iotd
, struct TrackDiskBase
*TDBase
)
550 struct TDU
*unit
=(struct TDU
*)iotd
->iotd_Req
.io_Unit
;
551 int cyl
, hd
, sec
; // C/H/S for operation
556 if (unit
->tdu_DiskIn
== TDU_NODISK
)
558 iotd
->iotd_Req
.io_Actual
= 0;
559 return TDERR_DiskChanged
;
562 sec
= iotd
->iotd_Req
.io_Offset
>> 9; // sector is wrong right now (LBA)
563 cyl
= (sec
>> 1) / DP_SECTORS
; // cyl contains real cyl number
564 sec
%= 2*DP_SECTORS
; // sector on track (on both sides)
565 hd
= sec
/ DP_SECTORS
; // head number
566 sec
%= DP_SECTORS
; // real sector number
568 remain
= iotd
->iotd_Req
.io_Length
;
569 while (length
<iotd
->iotd_Req
.io_Length
)
571 size
= (DP_SECTORS
*512) - (sec
*512);
574 if (size
<(DP_SECTORS
*512))
576 /* read new track only if we don't
577 want to write a full track
579 err
=td_readtrack(iotd
, (UBYTE
)cyl
, (UBYTE
)hd
, TDBase
);
581 iotd
->iotd_Req
.io_Actual
= length
;
587 err
=td_update(unit
, TDBase
);
590 unit
->tdu_lastcyl
=(UBYTE
)cyl
;
591 unit
->tdu_lasthd
=(UBYTE
)hd
;
593 CopyMem(iotd
->iotd_Req
.io_Data
+length
, unit
->td_DMABuffer
+(sec
*512), size
);
594 unit
->tdu_flags
|= TDUF_WRITE
;
606 iotd
->iotd_Req
.io_Actual
= iotd
->iotd_Req
.io_Length
;
612 * Probably needs a lot more error checking/handling,
613 * but at least it works.
615 int td_formattrack(struct TDU
*unit
, UBYTE cyl
, UBYTE hd
, struct TrackDiskBase
*tdb
)
622 skcnt
= unit
->pub
.tdu_RetryCnt
;
624 unit
->tdu_MotorOn
= 1;
625 td_motoron(unit
->tdu_UnitNum
,tdb
,TRUE
);
629 /* We start by filling the DMA buffer with values needed */
630 dmabuf
= (UBYTE
*)unit
->td_DMABuffer
;
632 for (i
=1;i
<=DP_SECTORS
;i
++)
634 dmabuf
[off
++] = (UBYTE
)cyl
;
639 unit
->tdu_lastcyl
= -1;
640 unit
->tdu_lasthd
= -1;
643 /* Go to the correct track,. If we are already on it, we omit seeking in order to
644 speed up the operation. */
645 if (unit
->pub
.tdu_CurrTrk
== cyl
)
648 err
= td_recalibrate(unit
->tdu_UnitNum
,0,cyl
,tdb
);
651 rwcnt
= unit
->pub
.tdu_RetryCnt
;
655 clear_dma_ff(TD_DMA
);
656 // Should place some cache flush in future (when cache implemented)
657 set_dma_addr(TD_DMA
, (ULONG
)(IPTR
)unit
->td_DMABuffer
);
658 set_dma_count(TD_DMA
, DP_SECTORS
*512);
659 set_dma_mode(TD_DMA
, DMA_MODE_WRITE
);
661 /* Issue format command */
663 tdb
->td_rawcom
[0] = FD_FORMAT
;
664 tdb
->td_rawcom
[1] = unit
->tdu_UnitNum
| (hd
<< 2);
665 tdb
->td_rawcom
[2] = DP_SSIZE
;
666 tdb
->td_rawcom
[3] = DP_SECTORS
;
667 tdb
->td_rawcom
[4] = DP_GAP2
;
668 tdb
->td_rawcom
[5] = 0;
669 /* Command prepared, now send it */
671 /* Wait for end phase */
672 err
= td_waitint(tdb
,7,FALSE
);
675 /* Check if everything went OK */
676 if (tdb
->td_result
[0] & 0xc0)
678 err
= TDERR_NotSpecified
;
685 td_recalibrate(unit
->tdu_UnitNum
,1,0,tdb
);
690 int td_format(struct IOExtTD
*iotd
, struct TrackDiskBase
*tdb
)
693 iotd
->iotd_Req
.io_Actual
= 0;
694 return TDERR_WriteProt
;
696 struct TDU
*unit
=(struct TDU
*)iotd
->iotd_Req
.io_Unit
;
702 if (unit
->tdu_DiskIn
== TDU_NODISK
)
704 D(bug("td_format(): No disk in drive!\n"));
705 return TDERR_DiskChanged
;
708 /* Calculate CHS style address */
709 sec
= iotd
->iotd_Req
.io_Offset
>> 9; // sector is wrong right now (LBA)
710 cyl
= (sec
>> 1) / DP_SECTORS
; // cyl contains real cyl number
711 sec
%= 2*DP_SECTORS
; // sector on track (on both sides)
712 hd
= sec
/ DP_SECTORS
; // head number
714 remain
= iotd
->iotd_Req
.io_Length
;
715 while (length
< iotd
->iotd_Req
.io_Length
) {
716 err
= td_formattrack(unit
, cyl
, hd
, tdb
);
719 /* We are fine, now write that data! */
721 if (size
> 512*DP_SECTORS
)
722 size
= 512*DP_SECTORS
;
723 CopyMem(iotd
->iotd_Req
.io_Data
+ length
, unit
->td_DMABuffer
, size
);
724 /* Note that we don't remember last track number stored in the buffer.
725 We do it because next TD_READ issued in order to verify the data
726 should really read the data from the disk, not just get them back
728 err
= td_readwritetrack(unit
->tdu_UnitNum
, cyl
, hd
, FD_WRITE
, tdb
);
731 iotd
->iotd_Req
.io_Actual
= length
;
744 iotd
->iotd_Req
.io_Actual
= iotd
->iotd_Req
.io_Length
;