2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
5 Desc: Amigastyle device for trackdisk
10 #include <aros/debug.h>
12 #include <devices/trackdisk.h>
13 #include <exec/resident.h>
14 #include <exec/errors.h>
15 #include <exec/memory.h>
16 #include <exec/lists.h>
17 #include <exec/alerts.h>
18 #include <exec/tasks.h>
19 #include <exec/interrupts.h>
20 #include <clib/alib_protos.h>
21 #include <aros/symbolsets.h>
23 #include <dos/filehandler.h>
24 #include <libraries/expansion.h>
25 #include <libraries/expansionbase.h>
27 #include <proto/exec.h>
28 #include <proto/disk.h>
29 #include <proto/expansion.h>
31 #include <hardware/custom.h>
32 #include <hardware/cia.h>
34 #include <resources/disk.h>
36 #include "trackdisk_device.h"
37 #include "trackdisk_hw.h"
39 #include LC_LIBDEFS_FILE
41 static BOOL
ishd(ULONG id
)
43 return id
== DRT_150RPM
;
46 static UBYTE
getsectors(struct TDU
*tdu
)
48 BYTE hdmult
= tdu
->tdu_hddisk
? 2 : 1;
49 if (tdu
->tdu_disktype
== DT_UNDETECTED
)
51 return tdu
->tdu_sectors
;
54 static void getunit(struct TrackDiskBase
*tdb
)
56 struct DiskBase
*DiskBase
= tdb
->td_DiskBase
;
57 while (GetUnit(&tdb
->td_dru
) == NULL
) {
58 WaitPort (&tdb
->td_druport
);
61 static void giveunit(struct TrackDiskBase
*tdb
)
63 struct DiskBase
*DiskBase
= tdb
->td_DiskBase
;
67 static AROS_INTH1(disk_block_interrupt
, struct TrackDiskBase
*, tdb
)
71 Signal (tdb
->td_task
, 1L << tdb
->td_IntBit
);
77 static void TestInsert(struct TrackDiskBase
*tdb
, struct TDU
*tdu
, BOOL dostep
)
82 if (tdu
->pub
.tdu_PubFlags
& TDPF_NOCLICK
) {
83 td_seek(tdu
, -1, 0, tdb
);
85 // step towards cyl 0 if > 0, if not, step to cyl 1
86 td_seek(tdu
, tdu
->pub
.tdu_CurrTrk
>= 2 ? (tdu
->pub
.tdu_CurrTrk
- 2) / 2 : 1, 0, tdb
);
89 if (td_getDiskChange (tdu
, tdb
)) {
90 struct DiskBase
*DiskBase
= tdb
->td_DiskBase
;
91 D(bug("[Floppy] Insertion detected\n"));
92 td_recalibrate(tdu
, tdb
);
93 tdu
->tdu_hddisk
= ishd(ReadUnitID(tdu
->tdu_UnitNum
));
94 td_detectformat(tdu
, tdb
);
95 tdu
->tdu_DiskIn
= TDU_DISK
;
96 tdu
->pub
.tdu_Counter
++;
97 tdu
->tdu_ProtStatus
= td_getprotstatus(tdu
,tdb
);
99 ForeachNode(&tdu
->tdu_Listeners
,iotd
) {
100 Cause((struct Interrupt
*)((struct IOExtTD
*)iotd
->iotd_Req
.io_Data
));
106 static BOOL
TD_PerformIO(struct IOExtTD
*iotd
, struct TrackDiskBase
*tdb
)
108 struct TDU
*tdu
= (struct TDU
*)iotd
->iotd_Req
.io_Unit
;
109 struct DriveGeometry
*geo
;
113 D(bug("TD%d PerformIO cmd=%d\n", tdu
->tdu_UnitNum
, iotd
->iotd_Req
.io_Command
));
117 switch(iotd
->iotd_Req
.io_Command
)
120 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
121 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
128 iotd
->iotd_Req
.io_Error
= 0;
131 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
132 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
136 iotd
->iotd_Req
.io_Error
= td_read(iotd
, tdu
, tdb
);
139 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
140 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
144 iotd
->iotd_Req
.io_Error
= td_flush(tdu
, tdb
);
147 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
148 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
152 iotd
->iotd_Req
.io_Error
= td_write(iotd
, tdu
, tdb
);
154 case TD_ADDCHANGEINT
:
156 AddTail(&tdu
->tdu_Listeners
,(struct Node
*)iotd
);
161 iotd
->iotd_Req
.io_Actual
= tdu
->pub
.tdu_Counter
;
162 iotd
->iotd_Req
.io_Error
=0;
166 if (tdu
->tdu_DiskIn
== TDU_NODISK
)
167 TestInsert(tdb
, tdu
, FALSE
);
168 if (tdu
->tdu_DiskIn
== TDU_DISK
) {
169 /* test if disk is still in there */
170 temp
= td_getDiskChange(tdu
, tdb
);
171 iotd
->iotd_Req
.io_Actual
= temp
? 0 : 1;
172 tdu
->tdu_DiskIn
= temp
? TDU_DISK
: TDU_NODISK
;
174 /* No disk in drive */
175 iotd
->iotd_Req
.io_Actual
= 1;
177 D(bug("TD%d_CHANGESTATE=%d\n", tdu
->tdu_UnitNum
, iotd
->iotd_Req
.io_Actual
));
178 iotd
->iotd_Req
.io_Error
=0;
181 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
182 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
186 iotd
->iotd_Req
.io_Error
= td_format(iotd
, tdu
, tdb
);
189 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
190 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
194 iotd
->iotd_Req
.io_Error
=0;
195 switch (iotd
->iotd_Req
.io_Length
)
200 td_motoroff(tdu
, tdb
);
204 td_motoron(tdu
, tdb
, TRUE
);
207 iotd
->iotd_Req
.io_Error
= TDERR_NotSpecified
;
212 iotd
->iotd_Req
.io_Actual
= tdu
->tdu_ProtStatus
;
213 iotd
->iotd_Req
.io_Error
=0;
215 case TD_REMCHANGEINT
:
217 Remove((struct Node
*)iotd
);
222 BYTE sectors
= getsectors(tdu
);
223 geo
= (struct DriveGeometry
*)iotd
->iotd_Req
.io_Data
;
224 geo
->dg_SectorSize
= 512;
225 geo
->dg_Cylinders
= 80;
226 geo
->dg_CylSectors
= sectors
* 2;
228 geo
->dg_TrackSectors
= sectors
;
229 geo
->dg_CylSectors
= geo
->dg_TrackSectors
* geo
->dg_Heads
;
230 geo
->dg_TotalSectors
= geo
->dg_CylSectors
* geo
->dg_Cylinders
;
231 geo
->dg_BufMemType
= MEMF_PUBLIC
;
232 geo
->dg_DeviceType
= DG_DIRECT_ACCESS
;
233 geo
->dg_Flags
= DGF_REMOVABLE
;
234 iotd
->iotd_Req
.io_Error
=0;
237 case TD_GETDRIVETYPE
:
238 iotd
->iotd_Req
.io_Actual
= tdu
->tdu_hddisk
? DRIVE3_5_150RPM
: DRIVE3_5
;
239 iotd
->iotd_Req
.io_Error
= 0;
241 case TD_GETNUMTRACKS
:
242 iotd
->iotd_Req
.io_Actual
= 80 * 2;
243 iotd
->iotd_Req
.io_Error
= 0;
246 if (iotd
->iotd_Count
> tdu
->pub
.tdu_Counter
) {
247 iotd
->iotd_Req
.io_Error
= TDERR_DiskChanged
;
252 BYTE sectors
= getsectors(tdu
);
254 temp
= (iotd
->iotd_Req
.io_Offset
>> 10) / sectors
;
255 iotd
->iotd_Req
.io_Error
= td_seek(tdu
, temp
>> 1, temp
& 1, tdb
);
260 D(bug("TD: Unknown command received\n"));
261 iotd
->iotd_Req
.io_Error
= IOERR_NOCMD
;
263 } /* switch(iotd->iotd_Req.io_Command) */
264 td_deselect(tdu
, tdb
);
269 AROS_LH1(void, beginio
,
270 AROS_LHA(struct IOExtTD
*, iotd
, A1
),
271 struct TrackDiskBase
*, TDBase
, 5, TrackDisk
)
275 iotd
->iotd_Req
.io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
276 if (iotd
->iotd_Req
.io_Flags
& IOF_QUICK
) {
277 switch(iotd
->iotd_Req
.io_Command
)
279 case TD_GETNUMTRACKS
:
280 case TD_GETDRIVETYPE
:
282 case TD_REMCHANGEINT
:
283 case TD_ADDCHANGEINT
:
286 TD_PerformIO(iotd
,TDBase
);
291 iotd
->iotd_Req
.io_Flags
&= ~IOF_QUICK
;
292 /* Forward to devicetask */
293 PutMsg(&TDBase
->td_Port
, &iotd
->iotd_Req
.io_Message
);
298 AROS_LH1(LONG
, abortio
,
299 AROS_LHA(struct IOExtTD
*, iotd
, A1
),
300 struct TrackDiskBase
*, TDBase
, 6, TrackDisk
)
309 static void TD_DevTask(struct Task
*parent
, struct TrackDiskBase
*tdb
)
312 struct IOExtTD
*iotd
;
314 ULONG tasig
,tisig
,sigs
,i
;
315 struct Interrupt
*inter
;
316 struct DiskBase
*DiskBase
= tdb
->td_DiskBase
;
318 D(bug("[TDTask] TD_DevTask(tdb=%p)\n", tdb
));
323 NEWLIST(&tdb
->td_Port
.mp_MsgList
);
324 tdb
->td_Port
.mp_Node
.ln_Type
= NT_MSGPORT
;
325 tdb
->td_Port
.mp_Flags
= PA_SIGNAL
;
326 tdb
->td_Port
.mp_SigBit
= SIGBREAKB_CTRL_F
;
327 tdb
->td_Port
.mp_SigTask
= me
;
328 tdb
->td_Port
.mp_Node
.ln_Name
= "trackdisk.device";
330 D(bug("[TDTask] TD_DevTask: struct TaskData @ %p\n", td
));
332 tdb
->td_IntBit
= AllocSignal(-1);
333 tdb
->td_TimerMP
= CreateMsgPort();
334 tdb
->td_TimerMP2
= CreateMsgPort();
335 tdb
->td_TimerIO
= (struct timerequest
*) CreateIORequest(tdb
->td_TimerMP
, sizeof(struct timerequest
));
336 tdb
->td_TimerIO2
= (struct timerequest
*) CreateIORequest(tdb
->td_TimerMP2
, sizeof(struct timerequest
));
337 if (tdb
->td_IntBit
== -1)
338 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_NoSignal
);
339 if (!tdb
->td_TimerMP
|| !tdb
->td_TimerMP2
|| !tdb
->td_TimerIO
|| !tdb
->td_TimerIO2
)
340 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_NoMemory
);
341 if (OpenDevice("timer.device", UNIT_VBLANK
, (struct IORequest
*)tdb
->td_TimerIO
, 0))
342 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_OpenDev
);
343 if (OpenDevice("timer.device", UNIT_MICROHZ
, (struct IORequest
*)tdb
->td_TimerIO2
, 0))
344 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_OpenDev
);
347 NEWLIST(&tdb
->td_druport
.mp_MsgList
);
348 tdb
->td_dru
.dru_Message
.mn_ReplyPort
= &tdb
->td_druport
;
349 tdb
->td_druport
.mp_SigBit
= AllocSignal(-1);
351 tdb
->td_druport
.mp_SigTask
= tdb
->td_task
;
353 inter
= &tdb
->td_dru
.dru_DiscBlock
;
354 inter
->is_Node
.ln_Pri
= 0;
355 inter
->is_Node
.ln_Type
= NT_INTERRUPT
;
356 inter
->is_Node
.ln_Name
= "trackdisk.device disk dma done";
357 inter
->is_Code
= (APTR
)disk_block_interrupt
;
358 inter
->is_Data
= tdb
;
360 tdb
->td_buffer_unit
= -1;
361 tdb
->td_buffer_track
= -1;
363 /* Initial check for floppies */
364 for (i
= 0; i
< TD_NUMUNITS
; i
++) {
365 struct TDU
*tdu
= tdb
->td_Units
[i
];
369 td_motoroff(tdu
, tdb
);
370 tdu
->tdu_broken
= td_recalibrate(tdu
, tdb
) == 0;
372 bug("DF%d failed to recalibrate!?\n", i
);
374 D(bug("DF%d initialized\n", i
));
375 tdu
->tdu_DiskIn
= td_getDiskChange(tdu
, tdb
) ? TDU_DISK
: TDU_NODISK
;
376 tdu
->tdu_ProtStatus
= td_getprotstatus(tdu
,tdb
);
377 tdu
->tdu_hddisk
= ishd(GetUnitID(i
));
378 tdu
->tdu_sectors
= tdu
->tdu_hddisk
? 22 : 11;
379 tdu
->tdu_disktype
= DT_UNDETECTED
;
380 td_deselect(tdu
, tdb
);
385 tasig
= 1L << tdb
->td_Port
.mp_SigBit
;
386 tisig
= 1L << tdb
->td_TimerMP
->mp_SigBit
;
388 /* Reply to startup message */
389 Signal(parent
, SIGBREAKF_CTRL_F
);
391 tdb
->td_TimerIO
->tr_node
.io_Command
= TR_ADDREQUEST
;
392 tdb
->td_TimerIO
->tr_time
.tv_secs
= 2;
393 tdb
->td_TimerIO
->tr_time
.tv_micro
= 500000;
394 SendIO((struct IORequest
*)tdb
->td_TimerIO
);
396 /* Endless task loop */
400 sigs
= Wait(tasig
| tisig
); /* Wait for a message */
401 /* If unit was not active process message */
403 /* We received a message. Deal with it */
404 while((iotd
= (struct IOExtTD
*)GetMsg(&tdb
->td_Port
)) != NULL
) {
405 /* Execute command */
406 if (TD_PerformIO(iotd
, tdb
)) {
408 ReplyMsg((struct Message
*)iotd
);
414 /* We were woken up by the timer. */
415 WaitIO((struct IORequest
*)tdb
->td_TimerIO
);
416 for(i
= 0; i
< TD_NUMUNITS
; i
++) {
417 tdu
= tdb
->td_Units
[i
];
418 /* If there is no floppy in drive, scan for changes */
419 if (tdu
&& !tdu
->tdu_broken
) {
422 switch (tdu
->tdu_DiskIn
)
425 TestInsert(tdb
, tdu
, TRUE
);
428 if (td_getDiskChange(tdu
, tdb
) == 0) {
429 D(bug("[Floppy] Removal detected\n"));
430 /* Go to cylinder 0 */
431 td_motoroff(tdu
, tdb
);
432 td_recalibrate(tdu
, tdb
);
433 tdu
->tdu_DiskIn
= TDU_NODISK
;
434 tdu
->tdu_sectors
= 11;
436 tdu
->tdu_disktype
= DT_UNDETECTED
;
437 tdu
->pub
.tdu_Counter
++;
438 if (tdu
->tdu_UnitNum
== tdb
->td_buffer_unit
)
441 ForeachNode(&tdu
->tdu_Listeners
,iotd
) {
442 Cause((struct Interrupt
*)((struct IOExtTD
*)iotd
->iotd_Req
.io_Data
));
450 td_deselect(tdu
, tdb
);
455 /* Reload the timer again */
456 GetMsg(tdb
->td_TimerMP
);
457 tdb
->td_TimerIO
->tr_node
.io_Command
= TR_ADDREQUEST
;
458 tdb
->td_TimerIO
->tr_time
.tv_secs
= 2;
459 tdb
->td_TimerIO
->tr_time
.tv_micro
= 500000;
460 SendIO((struct IORequest
*)tdb
->td_TimerIO
);
465 ULONG
TD_InitTask(struct TrackDiskBase
*tdb
)
469 D(bug("TD: Creating devicetask...\n"));
472 TASKTAG_PC
, TD_DevTask
,
473 TASKTAG_NAME
, "trackdisk.device",
475 TASKTAG_ARG1
, FindTask(0),
479 /* Wait until started */
480 Wait(SIGBREAKF_CTRL_F
);
493 struct TDU
*TD_InitUnit(ULONG num
, struct TrackDiskBase
*tdb
)
496 struct DiskBase
*DiskBase
= tdb
->td_DiskBase
;
498 if (AllocUnit(num
) == 0)
501 /* Try to get memory for structure */
502 unit
= AllocMem(sizeof(struct TDU
), MEMF_PUBLIC
| MEMF_CLEAR
);
505 unit
->tdu_DiskIn
= TDU_NODISK
; /* Assume there is no floppy in there */
506 unit
->pub
.tdu_StepDelay
= 3; /* Standard values here */
507 unit
->pub
.tdu_SettleDelay
= 15;
508 unit
->pub
.tdu_RetryCnt
= 3;
509 unit
->pub
.tdu_CalibrateDelay
= 3;
510 unit
->tdu_UnitNum
= num
;
511 NEWLIST(&unit
->tdu_Listeners
);
513 /* Store the unit in TDBase */
514 tdb
->td_Units
[num
] = unit
;
520 * Create BootNodes for all 4 possible devices
522 static void TD_BootNode(
523 struct ExpansionBase
*ExpansionBase
,
524 ULONG unit
, ULONG id
)
526 TEXT dosdevname
[4] = "DF0";
527 IPTR pp
[4 + DE_BOOTBLOCKS
+ 1] = {};
528 struct DeviceNode
*devnode
;
529 BOOL hddisk
= ishd(id
);
531 dosdevname
[2] += unit
;
532 D(bug("trackdisk.device: Adding bootnode %s: DDHD=%d\n", dosdevname
, hddisk
? 1 : 0));
534 pp
[0] = (IPTR
)dosdevname
;
535 pp
[1] = (IPTR
)"trackdisk.device";
537 pp
[DE_TABLESIZE
+ 4] = DE_BOOTBLOCKS
;
538 pp
[DE_SIZEBLOCK
+ 4] = 128;
539 pp
[DE_NUMHEADS
+ 4] = 2;
540 pp
[DE_SECSPERBLOCK
+ 4] = 1;
541 pp
[DE_BLKSPERTRACK
+ 4] = hddisk
? 22 : 11;
542 pp
[DE_RESERVEDBLKS
+ 4] = 2;
543 pp
[DE_LOWCYL
+ 4] = 0;
544 pp
[DE_HIGHCYL
+ 4] = 79;
545 pp
[DE_NUMBUFFERS
+ 4] = 10;
546 pp
[DE_BUFMEMTYPE
+ 4] = MEMF_PUBLIC
;
547 pp
[DE_MAXTRANSFER
+ 4] = 0x00200000;
548 pp
[DE_MASK
+ 4] = 0x7FFFFFFE;
549 pp
[DE_BOOTPRI
+ 4] = 5 - (unit
* 10);
550 pp
[DE_DOSTYPE
+ 4] = 0;
551 pp
[DE_BOOTBLOCKS
+ 4] = 2;
552 devnode
= MakeDosNode(pp
);
555 AddBootNode(pp
[DE_BOOTPRI
+ 4], 0, devnode
, NULL
);
557 static int GM_UNIQUENAME(init
)(LIBBASETYPEPTR TDBase
)
561 struct DiskBase
*DiskBase
;
562 struct ExpansionBase
*ExpansionBase
;
563 ULONG ids
[TD_NUMUNITS
];
565 D(bug("TD: Init\n"));
567 TDBase
->td_supportHD
= 0;
568 DiskBase
= OpenResource("disk.resource");
570 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_OpenRes
);
572 TDBase
->td_DiskBase
= DiskBase
;
573 TDBase
->ciaa
= (struct CIA
*)0xbfe001;
574 TDBase
->ciab
= (struct CIA
*)0xbfd000;
575 TDBase
->custom
= (struct Custom
*)0xdff000;
578 for (i
= 0; i
< TD_NUMUNITS
; i
++) {
579 ids
[i
] = GetUnitID(i
);
580 if (ids
[i
] != DRT_EMPTY
)
585 /* No drives here. abort */
586 D(bug("TD: No drives\n"));
590 ExpansionBase
= (struct ExpansionBase
*)TaggedOpenLibrary(TAGGEDOPEN_EXPANSION
);
592 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_OpenLib
);
594 /* Alloc memory for track buffering, DD buffer only, reallocated
595 * later if HD disk detected to save RAM on unexpanded machines */
596 TDBase
->td_DMABuffer
= AllocMem(DISK_BUFFERSIZE
, MEMF_CHIP
);
597 TDBase
->td_DataBuffer
= AllocMem(11 * 512, MEMF_ANY
);
598 if (!TDBase
->td_DMABuffer
|| !TDBase
->td_DataBuffer
)
599 Alert(AT_DeadEnd
| AO_TrackDiskDev
| AG_NoMemory
);
601 for (i
= 0; i
< TD_NUMUNITS
; i
++) {
602 TDBase
->td_Units
[i
] = NULL
;
603 if (ids
[i
] != DRT_EMPTY
)
604 TD_InitUnit(i
, TDBase
);
605 D(bug("TD%d id=%08x status=%d\n", i
, id
, TDBase
->td_Units
[i
] ? 1 : 0));
608 /* Create the message processor task */
611 /* Only add bootnode if recalibration succeeded */
612 for (i
= 0; i
< TD_NUMUNITS
; i
++) {
613 if (TDBase
->td_Units
[i
] && !TDBase
->td_Units
[i
]->tdu_broken
)
614 TD_BootNode(ExpansionBase
, i
, ids
[i
]);
617 CloseLibrary((struct Library
*)ExpansionBase
);
619 D(bug("TD: done %d\n", drives
));
624 static int GM_UNIQUENAME(open
)
626 LIBBASETYPEPTR TDBase
,
627 struct IOExtTD
*iotd
,
632 iotd
->iotd_Req
.io_Error
= IOERR_OPENFAIL
;
634 /* Is the requested unitNumber valid? */
635 if (unitnum
< TD_NUMUNITS
) {
638 iotd
->iotd_Req
.io_Device
= (struct Device
*)TDBase
;
640 /* Get TDU structure */
641 unit
= TDBase
->td_Units
[unitnum
];
642 if (unit
&& !unit
->tdu_broken
) {
643 iotd
->iotd_Req
.io_Unit
= (struct Unit
*)unit
;
644 ((struct Unit
*)unit
)->unit_OpenCnt
++;
645 iotd
->iotd_Req
.io_Error
= 0;
648 D(bug("TD%d: Open=%d\n", unitnum
, iotd
->iotd_Req
.io_Error
));
650 return iotd
->iotd_Req
.io_Error
== 0;
654 static int GM_UNIQUENAME(close
)
656 LIBBASETYPEPTR TDBase
,
660 iotd
->iotd_Req
.io_Unit
->unit_OpenCnt
--;
665 ADD2INITLIB(GM_UNIQUENAME(init
), 0)
666 ADD2OPENDEV(GM_UNIQUENAME(open
), 0)
667 ADD2CLOSEDEV(GM_UNIQUENAME(close
), 0)