2 * Copyright (C) 2013, The AROS Development Team
4 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
6 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
9 #include <aros/debug.h>
11 #include <aros/symbolsets.h>
13 #include <devices/cd.h>
15 #include <proto/exec.h>
16 #include <proto/alib.h>
18 #include <hardware/intbits.h>
23 #include "cd_intern.h"
25 static inline ULONG
readl(ULONG addr
)
27 return *((volatile ULONG
*)addr
);
30 static inline VOID
writel(ULONG value
, ULONG addr
)
32 *((volatile ULONG
*)addr
) = value
;
35 static inline UWORD
readw(ULONG addr
)
37 return *((volatile UWORD
*)addr
);
40 static inline VOID
writew(UWORD value
, ULONG addr
)
42 *((volatile UWORD
*)addr
) = value
;
45 static inline UBYTE
readb(ULONG addr
)
47 return *((volatile UBYTE
*)addr
);
50 static inline VOID
writeb(UBYTE value
, ULONG addr
)
52 *((volatile UBYTE
*)addr
) = value
;
64 UBYTE cs_Reserved2
[8];
69 LIBBASETYPE
*cu_CDBase
;
73 } *cu_Data
; /* x16 of these */
75 UBYTE Response
[0x100];
78 UBYTE Reserved
[0x100];
80 struct Interrupt cu_Interrupt
;
81 UBYTE cu_TxHead
, cu_RxHead
;
83 struct CDInfo cu_CDInfo
;
84 struct QCode cu_QCode
;
85 ULONG cu_TotalSectors
;
87 union CDTOC cu_CDTOC
[100];
92 UBYTE
dec2bcd(UBYTE dec
)
96 return ((dec
/ 10) << 4) | (dec
% 10);
99 static void sec2msf(LONG sec
, UBYTE
*msf
)
101 msf
[0] = dec2bcd(sec
/ (60 * 75));
102 sec
= sec
% (60 * 75);
103 msf
[1] = dec2bcd(sec
/ 75);
104 msf
[2] = dec2bcd(sec
% 75);
107 static ULONG
msf2sec(struct RMSF
*msf
)
109 return ((msf
->Minute
* 60) + msf
->Second
) * 75 + msf
->Frame
;
112 static VOID
CD32_IntEnable(struct CD32Unit
*cu
, ULONG mask
)
114 cu
->cu_IntEnable
|= mask
;
115 writel(cu
->cu_IntEnable
, AKIKO_CDINTENA
);
118 static VOID
CD32_IntDisable(struct CD32Unit
*cu
, ULONG mask
)
120 cu
->cu_IntEnable
&= ~mask
;
121 writel(cu
->cu_IntEnable
, AKIKO_CDINTENA
);
124 static AROS_INTH1(CD32_Interrupt
, struct CD32Unit
*, cu
)
126 const UBYTE resp_len
[16] = {
127 1, 2, 2, 2, 2, 2, 15, 20, 0, 0, 2, 0, 0, 0, 0, 0
134 status
= readl(AKIKO_CDINTREQ
);
138 if (status
& AKIKO_CDINT_RXDMA
) {
139 rxtail
= readb(AKIKO_CDRXINX
);
140 if (((cu
->cu_RxHead
+1)&0xff) == rxtail
) {
141 /* Add the correct length for the full response */
142 UBYTE len
= resp_len
[cu
->cu_Misc
->Response
[cu
->cu_RxHead
] & 0xf];
144 D(bug("%s: Insane response byte 0x%02x\n", cu
->cu_Misc
->Response
[cu
->cu_RxHead
]));
146 writeb(cu
->cu_RxHead
+len
+1, AKIKO_CDRXCMP
);
149 CD32_IntDisable(cu
, AKIKO_CDINT_RXDMA
);
150 D(bug("%s: Signal %p\n", __func__
, cu
->cu_Task
));
151 Signal(cu
->cu_Task
, SIGF_SINGLE
);
154 if (status
& AKIKO_CDINT_TXDMA
) {
155 CD32_IntDisable(cu
, AKIKO_CDINT_TXDMA
);
159 if (status
& AKIKO_CDINT_OVERFLOW
) {
160 CD32_IntDisable(cu
, AKIKO_CDINT_OVERFLOW
);
161 Signal(cu
->cu_Task
, SIGF_SINGLE
);
165 if (status
& AKIKO_CDINT_PBX
) {
166 UWORD pbx
= readw(AKIKO_CDPBX
);
167 writew(0, AKIKO_CDPBX
);
169 Signal(cu
->cu_Task
, SIGF_SINGLE
);
177 static UBYTE
bcd2dec(UBYTE bcd
)
179 return (bcd
& 0xf) + ((bcd
>> 4) & 0xf) * 10;
182 static VOID
CD32_UpdateTOC(struct CD32Unit
*cu
)
184 struct QCode
*qc
= &cu
->cu_QCode
;
186 D(bug("%s: Index 0x%02x\n", __func__
, qc
->Index
));
189 cu
->cu_CDTOC
[0].Summary
.FirstTrack
= bcd2dec(qc
->DiskPosition
.MSF
.Minute
);
192 cu
->cu_CDTOC
[0].Summary
.LastTrack
= bcd2dec(qc
->DiskPosition
.MSF
.Minute
);
195 cu
->cu_CDTOC
[0].Summary
.LeadOut
= qc
->DiskPosition
;
196 cu
->cu_CDInfo
.Status
|= CDSTSF_TOC
;
199 if (((qc
->Index
& 0xf) <= 9) && ((qc
->Index
& 0xf0) <= 0x90) &&
201 int track
= bcd2dec(qc
->Index
);
202 cu
->cu_CDTOC
[track
].Entry
.CtlAdr
= qc
->CtlAdr
;
203 cu
->cu_CDTOC
[track
].Entry
.Track
= track
;
204 cu
->cu_CDTOC
[track
].Entry
.Position
= qc
->DiskPosition
;
205 if (track
== 1 && qc
->CtlAdr
== 0x41) {
206 cu
->cu_CDInfo
.Status
|= CDSTSF_CDROM
;
209 D(bug("%s: Illegal track number 0x%02x ignored\n", __func__
, qc
->Index
));
215 static LONG
CD32_Cmd(struct CD32Unit
*cu
, UBYTE
*cmd
, LONG cmd_len
, UBYTE
*resp
, LONG resp_len
)
221 if (cu
->cu_Sequence
>= 16)
224 cmd
[0] = (cmd
[0] & 0xf) | (cu
->cu_Sequence
<< 4);
227 for (i
= 0; i
< cmd_len
; i
++) {
228 /* NOTE: We are relying on the fact that cu_TxHead is a
229 * UBYTE, so that it wraps at 0x100
231 cu
->cu_Misc
->Command
[cu
->cu_TxHead
++] = cmd
[i
];
235 cu
->cu_Misc
->Command
[cu
->cu_TxHead
++] = ~csum
;
237 /* Just wait for the RX to complete of the status */
238 CD32_IntEnable(cu
, AKIKO_CDINT_RXDMA
| AKIKO_CDINT_TXDMA
);
239 writel(AKIKO_CDFLAG_TXD
| AKIKO_CDFLAG_RXD
| AKIKO_CDFLAG_CAS
| AKIKO_CDFLAG_PBX
| AKIKO_CDFLAG_MSB
, AKIKO_CDFLAG
);
241 /* Trigger the command by updating AKIKO_CDTXCMP */
242 SetSignal(0, SIGF_SINGLE
);
243 writeb((cu
->cu_RxHead
+ 1) & 0xff, AKIKO_CDRXCMP
);
244 writeb(cu
->cu_TxHead
, AKIKO_CDTXCMP
);
247 UBYTE RxHead
= cu
->cu_RxHead
;
249 D(bug("%s: %02x Wait...\n", __func__
, cmd
[0]));
251 D(bug("%s: %02x Waited.\n", __func__
, cmd
[0]));
253 RxTail
= readb(AKIKO_CDRXINX
);
254 D(bug("%s: RxHead=%02x RxTail=%02x\n", __func__
, RxHead
, RxTail
));
256 if (cu
->cu_Misc
->Response
[RxHead
] == cmd
[0])
259 D(bug("%s: Found unexpected: 0x%02x (%d)\n", __func__
, cu
->cu_Misc
->Response
[RxHead
], (RxTail
+ 256 - RxHead
) & 0xff));
262 while (RxHead
!= RxTail
) {
263 /* NOTE: We are relying on the fact that cu_RxHead is a
264 * UBYTE, so that it wraps at 0x100
266 UBYTE val
= cu
->cu_Misc
->Response
[RxHead
++];
267 D(bug("%02x ", val
));
270 D(bug(" (%02x)\n", csum
));
273 D(bug("%s: Checksum failed on RX\n", __func__
));
274 return CDERR_NotSpecified
;
277 RxHead
= cu
->cu_RxHead
;
278 cu
->cu_RxHead
= RxTail
;
280 switch (cu
->cu_Misc
->Response
[RxHead
]) {
283 if (cu
->cu_Misc
->Response
[RxHead
] == 0x83) {
284 if (!(cu
->cu_CDInfo
.Status
& CDSTSF_DISK
)) {
286 cu
->cu_CDInfo
.Status
|= CDSTSF_CLOSED
| CDSTSF_DISK
| CDSTSF_SPIN
;
289 if (cu
->cu_CDInfo
.Status
& CDSTSF_DISK
) {
291 cu
->cu_CDInfo
.Status
= 0;
299 cu
->cu_QCode
.CtlAdr
= cu
->cu_Misc
->Response
[RxHead
++];
300 cu
->cu_QCode
.Track
= cu
->cu_Misc
->Response
[RxHead
++];
301 cu
->cu_QCode
.Index
= cu
->cu_Misc
->Response
[RxHead
++];
302 cu
->cu_QCode
.Zero
= 0;
303 cu
->cu_QCode
.TrackPosition
.MSF
.Reserved
= 0;
304 cu
->cu_QCode
.TrackPosition
.MSF
.Minute
= cu
->cu_Misc
->Response
[RxHead
++];
305 cu
->cu_QCode
.TrackPosition
.MSF
.Second
= cu
->cu_Misc
->Response
[RxHead
++];
306 cu
->cu_QCode
.TrackPosition
.MSF
.Frame
= cu
->cu_Misc
->Response
[RxHead
++];
307 cu
->cu_QCode
.DiskPosition
.MSF
.Reserved
= cu
->cu_Misc
->Response
[RxHead
++];
308 cu
->cu_QCode
.DiskPosition
.MSF
.Minute
= cu
->cu_Misc
->Response
[RxHead
++];
309 cu
->cu_QCode
.DiskPosition
.MSF
.Second
= cu
->cu_Misc
->Response
[RxHead
++];
310 cu
->cu_QCode
.DiskPosition
.MSF
.Frame
= cu
->cu_Misc
->Response
[RxHead
++];
311 if (!(cu
->cu_CDInfo
.Status
& CDSTSF_TOC
))
317 D(bug("%s: Command mismatch: got 0x%02x, expected 0x%02x\n", __func__
, cu
->cu_Misc
->Response
[RxHead
], cmd
[0]));
318 return CDERR_InvalidState
;
321 writeb((RxTail
+ 1) & 0xff, AKIKO_CDRXCMP
);
324 D(bug("%s: Found expected: 0x%02x (%d)\n", __func__
, cu
->cu_Misc
->Response
[cu
->cu_RxHead
], (RxTail
+ 256 - cu
->cu_RxHead
) & 0xff));
327 while (cu
->cu_RxHead
!= RxTail
) {
328 /* NOTE: We are relying on the fact that cu_RxHead is a
329 * UBYTE, so that it wraps at 0x100
331 UBYTE val
= cu
->cu_Misc
->Response
[cu
->cu_RxHead
++];
332 D(bug("%02x ", val
));
339 D(bug("(%02x)\n", csum
));
342 D(bug("%s: Checksum failed on RX\n", __func__
));
343 return CDERR_NotSpecified
;
346 D(bug("%s: -- RXHead %2x --\n", __func__
, cu
->cu_RxHead
));
351 static VOID
CD32_Led(struct CD32Unit
*cu
, BOOL led_on
)
353 UBYTE cmd
[2] = { CHCD_LED
, (led_on
? 1 : 0) | 0x80 };
356 CD32_Cmd(cu
, cmd
, 2, resp
, 2);
359 static LONG
CD32_CmdRead(struct CD32Unit
*cu
, LONG sect_start
, LONG sectors
, void (*copy_sector
)(APTR sector
, APTR priv
), APTR priv
)
362 UBYTE cmd
[12], resp
[2];
363 UBYTE cmd_pause
[1] = { CHCD_PAUSE
};
364 UBYTE cmd_unpause
[1] = { CHCD_UNPAUSE
};
368 while (sectors
> 0) {
369 cu
->cu_CDInfo
.Status
&= ~(CDSTSF_PLAYING
| CDSTSF_PAUSED
| CDSTSF_SEARCH
| CDSTSF_DIRECTION
);
372 sec2msf(sect_start
, &cmd
[1]);
373 sec2msf(sect_start
+ 16, &cmd
[4]);
374 cmd
[7] = 0x80; /* Data read */
375 cmd
[8] = (cu
->cu_CDInfo
.PlaySpeed
>= 150) ? 0x40 : 0x00;
378 cmd
[10] = 0x04; /* 0x04 to enable the motor */
381 writew(0xffff, AKIKO_CDPBX
);
385 CD32_Cmd(cu
, cmd_unpause
, 1, resp
, sizeof(resp
));
386 err
= CD32_Cmd(cu
, cmd
, sizeof(cmd
), resp
, sizeof(resp
));
390 if ((resp
[1] & 2) != 2)
391 return CDERR_SeekError
;
394 /* Wait for (most) sectors to come in */
395 CD32_IntEnable(cu
, AKIKO_CDINT_PBX
);
397 writel(readl(AKIKO_CDFLAG
) | AKIKO_CDFLAG_ENABLE
, AKIKO_CDFLAG
);
400 CD32_Cmd(cu
, cmd_pause
, 1, resp
, sizeof(resp
));
402 pbx
= readw(AKIKO_CDPBX
);
405 D(bug("%s: Overflow during read\n", __func__
));
409 for (i
= 15; i
>= 0; i
--) {
410 if (!(pbx
& (1 << i
))) {
411 D(bug("%s: SECTOR: %d\n", __func__
, *(ULONG
*)(&cu
->cu_Data
[i
])));
412 copy_sector(&cu
->cu_Data
[i
], priv
);
425 static UBYTE
CD32_Status(struct CD32Unit
*cu
)
427 UBYTE cmd
[1], res
[21];
429 cu
->cu_Task
= FindTask(NULL
);
430 D(bug("%s: %p\n", __func__
, cu
));
431 cmd
[0] = CHCD_STATUS
;
433 CD32_Cmd(cu
, cmd
, 1, res
, 20);
435 D(bug("%s: Drive \"%s\", state 0x%02x\n", __func__
, &res
[2], res
[1]));
437 if (res
[1] & CHERR_BADCOMMAND
) {
438 if (cu
->cu_CDInfo
.Status
& CDSTSF_DISK
) {
440 cu
->cu_CDInfo
.Status
= 0;
442 } else if (res
[1] & CHERR_DISKPRESENT
) {
443 if (!(cu
->cu_CDInfo
.Status
& CDSTSF_DISK
)) {
445 cu
->cu_CDInfo
.Status
|= CDSTSF_CLOSED
| CDSTSF_DISK
| CDSTSF_SPIN
;
449 D(bug("%s: %p = 0x%08x\n", __func__
, cu
, cu
->cu_CDInfo
.Status
));
450 return cu
->cu_CDInfo
.Status
;
453 static VOID
CD32_ReadTOC(struct CD32Unit
*cu
)
455 UBYTE toc_cmd
[12] = { CHCD_MULTI
, 0, 0, 0, 0, 0, 0, 0x03, 0x40, 0, 0x00, 0 };
456 UBYTE idle_cmd
[1] = { CHCD_UNPAUSE
};
458 ULONG timeout
= 5 * 1000; /* 5 second timeout */
461 /* Start the read TOC command */
462 if (CD32_Cmd(cu
, toc_cmd
, 12, resp
, 2) != 0)
465 /* Until the TOC is read, blink the LED and unpause */
466 D(bug("%s: Waiting for TOC for up to 10 seconds\n", __func__
));
467 while (!(cu
->cu_CDInfo
.Status
& CDSTSF_TOC
) && timeout
> 0) {
470 cdDelayMS(cu
->cu_CDBase
, 100); /* Delay 100ms */
471 CD32_Cmd(cu
, idle_cmd
, 1, resp
, 2);
475 if (cu
->cu_CDInfo
.Status
& CDSTSF_CDROM
) {
476 if (cu
->cu_CDTOC
[0].Summary
.LastTrack
== 1) {
477 cu
->cu_TotalSectors
= msf2sec(&cu
->cu_CDTOC
[0].Summary
.LeadOut
.MSF
) -
478 msf2sec(&cu
->cu_CDTOC
[1].Entry
.Position
.MSF
);
480 cu
->cu_TotalSectors
= msf2sec(&cu
->cu_CDTOC
[2].Entry
.Position
.MSF
) -
481 msf2sec(&cu
->cu_CDTOC
[1].Entry
.Position
.MSF
);
484 cu
->cu_TotalSectors
= 0;
487 D(bug("%s: TotalSectors = %d\n", __func__
, cu
->cu_TotalSectors
));
490 static LONG
CD32_IsCDROM(struct CD32Unit
*cu
)
492 if ((cu
->cu_CDInfo
.Status
& CDSTSF_DISK
) == 0) {
493 if ((CD32_Status(cu
) & CDSTSF_DISK
) == 0)
497 if ((cu
->cu_CDInfo
.Status
& CDSTSF_TOC
) == 0) {
501 if ((cu
->cu_CDInfo
.Status
& CDSTSF_CDROM
) == 0) {
502 return CDERR_SeekError
;
515 static VOID
CD32_CopyREAD(APTR frame
, APTR priv
)
517 struct CD32Mode1
*mode1
= frame
;
518 struct CopyREAD
*cr
= priv
;
521 tocopy
= 2048 - cr
->offset
;
522 if (tocopy
> cr
->length
)
525 D(bug("%s: Copy from 0x%08x to 0x%08x, %d\n", __func__
, &mode1
->cs_Data
[cr
->offset
], cr
->buffer
, tocopy
));
526 CopyMem(&mode1
->cs_Data
[cr
->offset
], cr
->buffer
, tocopy
);
528 for (i
= 0; i
< tocopy
; i
++) {
530 if (mod
== 0) D(bug("%08x:", cr
->buffer
- (UBYTE
*)cr
->io
->io_Data
+ i
));
531 D(bug("%c%02x", (mod
==8) ? '-' : ' ', cr
->buffer
[i
]));
532 if (mod
== 15) D(bug("\n"));
538 cr
->length
-= tocopy
;
539 cr
->buffer
+= tocopy
;
540 cr
->io
->io_Actual
+= tocopy
;
544 static LONG
CD32_DoIO(struct IOStdReq
*io
, APTR priv
)
546 struct CD32Unit
*cu
= priv
;
547 LONG err
= CDERR_NOCMD
;
548 UBYTE cmd
[16], res
[16];
549 LONG sect_start
, sect_end
, origin
;
552 D(bug("%s:%p io_Command=%d\n", __func__
, io
, io
->io_Command
));
554 cu
->cu_Task
= FindTask(NULL
);
556 switch (io
->io_Command
) {
558 io
->io_Actual
= cu
->cu_ChangeNum
;
562 io
->io_Actual
= (cu
->cu_CDInfo
.Status
& CDSTSF_TOC
) ? 0 : 1;
563 D(bug("CD_CHANGESTATE: %d\n", io
->io_Actual
));
567 D(bug("CD_CONFIG: Data %p, Length %d\n", io
->io_Data
, io
->io_Length
));
568 if (io
->io_Length
!= 0) {
569 err
= CDERR_BADLENGTH
;
572 /* Gah. Some bright spark decided to use tags with
573 * the same values as TAG_IGNORE, TAG_MORE, and TAG_SKIP,
574 * so we have to process the TagItem array manually.
577 if (io
->io_Data
!= NULL
) {
578 struct TagItem
*ti
= io
->io_Data
;
581 switch (ti
->ti_Tag
) {
582 case TAGCD_PLAYSPEED
:
583 if (((ti
->ti_Data
% 75) != 0) ||
584 (ti
->ti_Data
> cu
->cu_CDInfo
.MaxSpeed
)) {
585 err
= CDERR_InvalidState
;
589 cu
->cu_CDInfo
.PlaySpeed
= ti
->ti_Data
;
591 case TAGCD_READSPEED
:
592 if (((ti
->ti_Data
% 75) != 0) ||
593 (ti
->ti_Data
> cu
->cu_CDInfo
.MaxSpeed
)) {
594 err
= CDERR_InvalidState
;
598 cu
->cu_CDInfo
.ReadSpeed
= ti
->ti_Data
;
601 case TAGCD_READXLSPEED
:
602 if (((ti
->ti_Data
% 75) != 0) ||
603 (ti
->ti_Data
> cu
->cu_CDInfo
.MaxSpeed
)) {
604 err
= CDERR_InvalidState
;
608 cu
->cu_CDInfo
.ReadXLSpeed
= ti
->ti_Data
;
611 case TAGCD_SECTORSIZE
:
612 if (ti
->ti_Data
!= 2048 &&
613 ti
->ti_Data
!= 2328) {
614 err
= CDERR_InvalidState
;
618 cu
->cu_CDInfo
.SectorSize
= ti
->ti_Data
;
622 cu
->cu_CDInfo
.XLECC
= ti
->ti_Data
? 1 : 0;
624 case TAGCD_EJECTRESET
:
625 cu
->cu_CDInfo
.EjectReset
= ti
->ti_Data
? 1 : 0;
634 D(bug("CD_CONFIG: Tag %d, Item %d, err %d, done %d\n", ti
->ti_Tag
, ti
->ti_Data
, err
, done
));
639 D(bug("CD_TOCMSF: %d\n", io
->io_Length
));
640 err
= CD32_IsCDROM(cu
);
641 D(bug("CD_TOCMSF: check %d\n", err
));
642 if (io
->io_Data
!= NULL
&& (cu
->cu_CDInfo
.Status
& CDSTSF_TOC
)) {
644 ULONG track
= io
->io_Offset
;
645 APTR buff
= io
->io_Data
;
646 for (i
= 0; i
< io
->io_Length
&& track
<= cu
->cu_CDTOC
[0].Summary
.LastTrack
; i
++, track
++) {
647 CopyMem(&cu
->cu_CDTOC
[track
], buff
, sizeof(union CDTOC
));
649 buff
+=sizeof(union CDTOC
);
651 io
->io_Actual
= actual
;
654 D(bug("CD_TOCMSF: err %d\n", err
));
661 err
= CD32_IsCDROM(cu
);
663 D(bug("CD_GETGEOMETRY: Not a data disk\n"));
667 if (io
->io_Length
>= sizeof(struct DriveGeometry
)) {
668 struct DriveGeometry
*dg
= (struct DriveGeometry
*)io
->io_Data
;
669 dg
->dg_SectorSize
= cu
->cu_CDInfo
.SectorSize
;
670 dg
->dg_TotalSectors
= cu
->cu_TotalSectors
;
671 dg
->dg_Cylinders
= cu
->cu_TotalSectors
;
672 dg
->dg_CylSectors
= 1;
674 dg
->dg_TrackSectors
= 1;
675 dg
->dg_BufMemType
= MEMF_PUBLIC
;
676 dg
->dg_DeviceType
= DG_CDROM
;
677 dg
->dg_Flags
= DGF_REMOVABLE
;
679 io
->io_Actual
= sizeof(*dg
);
682 err
= CDERR_BADLENGTH
;
686 if (io
->io_Length
>= sizeof(struct CDInfo
)) {
688 CopyMem(&cu
->cu_CDInfo
, io
->io_Data
, sizeof(struct CDInfo
));
689 io
->io_Actual
= sizeof(struct CDInfo
);
694 /* FIXME: Should this be a pause/unpause command?
701 if (io
->io_Offset
<= cu
->cu_CDTOC
[0].Summary
.LastTrack
) {
702 ULONG last
= io
->io_Offset
+ io
->io_Length
;
703 UBYTE cmd
[12], res
[2];
705 cmd
[1] = cu
->cu_CDTOC
[io
->io_Offset
].Entry
.Position
.MSF
.Minute
;
706 cmd
[2] = cu
->cu_CDTOC
[io
->io_Offset
].Entry
.Position
.MSF
.Second
;
707 cmd
[3] = cu
->cu_CDTOC
[io
->io_Offset
].Entry
.Position
.MSF
.Frame
;
708 if (last
> cu
->cu_CDTOC
[0].Summary
.LastTrack
) {
709 cmd
[4] = cu
->cu_CDTOC
[0].Summary
.LeadOut
.MSF
.Minute
;
710 cmd
[5] = cu
->cu_CDTOC
[0].Summary
.LeadOut
.MSF
.Minute
;
711 cmd
[6] = cu
->cu_CDTOC
[0].Summary
.LeadOut
.MSF
.Minute
;
713 cmd
[4] = cu
->cu_CDTOC
[last
].Entry
.Position
.MSF
.Minute
;
714 cmd
[5] = cu
->cu_CDTOC
[last
].Entry
.Position
.MSF
.Second
;
715 cmd
[6] = cu
->cu_CDTOC
[last
].Entry
.Position
.MSF
.Frame
;
718 cmd
[8] = (cu
->cu_CDInfo
.ReadSpeed
>= 150) ? 0x40 : 0x00;
722 err
= CD32_Cmd(cu
, cmd
, 12, res
, 2);
723 D(bug("CD_PLAYTRACK: err=%d, res[1]=0x%02x\n", err
, res
[1]));
725 (res
[1] & 0x80) == 0 &&
727 cu
->cu_CDInfo
.Status
&= ~(CDSTSF_PLAYING
| CDSTSF_PAUSED
| CDSTSF_SEARCH
| CDSTSF_DIRECTION
);
728 cu
->cu_CDInfo
.Status
|= CDSTSF_PLAYING
;
729 D(bug("CD_PLAYTRACK: Playing tracks %d-%d\n", io
->io_Offset
, last
-1));
735 err
= CD32_IsCDROM(cu
);
739 /* Convert offset and length to MSF */
745 origin
= msf2sec(&cu
->cu_CDTOC
[1].Entry
.Position
.MSF
);
746 sect_start
= io
->io_Offset
/ cu
->cu_CDInfo
.SectorSize
;
747 sect_end
= (io
->io_Offset
+ io
->io_Length
+ cu
->cu_CDInfo
.SectorSize
- 1) / cu
->cu_CDInfo
.SectorSize
;
750 cr
.buffer
= io
->io_Data
;
751 cr
.offset
= io
->io_Offset
% cu
->cu_CDInfo
.SectorSize
;
752 cr
.length
= io
->io_Length
;
754 err
= CD32_CmdRead(cu
, sect_start
+ origin
, sect_end
- sect_start
, CD32_CopyREAD
, &cr
);
758 err
= CD32_Cmd(cu
, cmd
, 1, res
, 1);
764 D(bug("%s:%p err=%d (Actual %d)\n", __func__
, io
, err
, io
->io_Actual
));
768 static VOID
CD32_Expunge(APTR priv
)
770 struct CD32Unit
*cu
= priv
;
771 RemIntServer(INTB_PORTS
, &cu
->cu_Interrupt
);
772 FreeMem(cu
->cu_Misc
, sizeof(struct CD32Misc
));
773 FreeMem(cu
->cu_Data
, sizeof(struct CD32Data
) * 16);
777 static const struct cdUnitOps CD32Ops
= {
778 .uo_Name
= "CD32 (Akiko)",
779 .uo_Expunge
= CD32_Expunge
,
780 .uo_DoIO
= CD32_DoIO
,
783 static const struct DosEnvec CD32Envec
= {
784 .de_TableSize
= DE_MASK
,
785 .de_SizeBlock
= 2048 >> 2,
787 .de_SectorPerBlock
= 1,
788 .de_BlocksPerTrack
= 1,
795 .de_BufMemType
= MEMF_24BITDMA
,
796 .de_MaxTransfer
= 32 * 2048,
797 .de_Mask
= 0x00fffffe,
801 static int CD32_InitLib(LIBBASETYPE
*cb
)
804 struct CD32Unit
*priv
;
806 if (readw(AKIKO_ID
) != AKIKO_ID_MAGIC
) {
807 D(bug("%s: No Akiko detected\n", __func__
));
811 /* Reset the Akiko CD32 interface */
812 writeb(readb(AKIKO_CDRESET
) | 0x80, AKIKO_CDRESET
);
813 writeb(readb(AKIKO_CDRESET
) & ~0x80, AKIKO_CDRESET
);
815 /* Disable the CD32 interface for the moment */
816 priv
= AllocVec(sizeof(*priv
), MEMF_ANY
| MEMF_CLEAR
);
818 priv
->cu_CDBase
= cb
;
819 priv
->cu_Misc
= LibAllocAligned(sizeof(struct CD32Misc
), MEMF_24BITDMA
| MEMF_CLEAR
, 1024);
821 priv
->cu_Data
= LibAllocAligned(sizeof(struct CD32Data
) * 16, MEMF_24BITDMA
| MEMF_CLEAR
, 4096);
823 priv
->cu_Interrupt
.is_Node
.ln_Pri
= 0;
824 priv
->cu_Interrupt
.is_Node
.ln_Name
= (STRPTR
)CD32Ops
.uo_Name
;
825 priv
->cu_Interrupt
.is_Node
.ln_Type
= NT_INTERRUPT
;
826 priv
->cu_Interrupt
.is_Data
= priv
;
827 priv
->cu_Interrupt
.is_Code
= (VOID_FUNC
)CD32_Interrupt
;
828 AddIntServer(INTB_PORTS
, &priv
->cu_Interrupt
);
830 writel((IPTR
)priv
->cu_Data
, AKIKO_CDADRDATA
);
831 writel((IPTR
)priv
->cu_Misc
, AKIKO_CDADRMISC
);
832 priv
->cu_TxHead
= readb(AKIKO_CDTXINX
);
833 priv
->cu_RxHead
= readb(AKIKO_CDRXINX
);
834 writeb(priv
->cu_TxHead
, AKIKO_CDTXCMP
);
835 writeb(priv
->cu_RxHead
, AKIKO_CDRXCMP
);
837 priv
->cu_CDInfo
.SectorSize
= 2048;
838 priv
->cu_CDInfo
.MaxSpeed
= 150;
839 priv
->cu_CDInfo
.PlaySpeed
= 75;
840 priv
->cu_CDInfo
.ReadSpeed
= 150;
841 priv
->cu_CDInfo
.ReadXLSpeed
= 150;
844 unit
= cdAddUnit(cb
, &CD32Ops
, priv
, &CD32Envec
);
846 D(bug("%s: Akiko as CD Unit %d\n", __func__
, unit
));
850 RemIntServer(INTB_PORTS
, &priv
->cu_Interrupt
);
852 FreeMem(priv
->cu_Data
, sizeof(struct CD32Data
) * 16);
854 FreeMem(priv
->cu_Misc
, sizeof(struct CD32Misc
));
862 static int CD32_ExpungeLib(LIBBASETYPE
*cb
)
869 ADD2INITLIB(CD32_InitLib
, 32);
870 ADD2EXPUNGELIB(CD32_ExpungeLib
, 32);