tools/adflib: build only host variant which is used by Sam440 target
[AROS.git] / arch / m68k-amiga / devs / cd / cd32.c
blob0e5f8a0da889f19288ea4045c18bae11dd14febf
1 /*
2 * Copyright (C) 2013, The AROS Development Team
3 * All right reserved.
4 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
6 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
7 */
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>
20 #include "chinon.h"
21 #include "cd32.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;
55 struct CD32Mode1 {
56 ULONG cs_Sector;
57 UBYTE cs_Reserved[8];
58 UBYTE cs_MinuteBCD;
59 UBYTE cs_SecondBCD;
60 UBYTE cs_FrameBCD;
61 UBYTE cs_Mode;
62 UBYTE cs_Data[2048];
63 UBYTE cs_EDC[4];
64 UBYTE cs_Reserved2[8];
65 UBYTE cs_ECC[276];
68 struct CD32Unit {
69 LIBBASETYPE *cu_CDBase;
70 struct CD32Data {
71 UBYTE Data[0xc00];
72 UBYTE Error[0x400];
73 } *cu_Data; /* x16 of these */
74 struct CD32Misc {
75 UBYTE Response[0x100];
76 UBYTE Subcode[0x100];
77 UBYTE Command[0x100];
78 UBYTE Reserved[0x100];
79 } *cu_Misc;
80 struct Interrupt cu_Interrupt;
81 UBYTE cu_TxHead, cu_RxHead;
82 struct Task *cu_Task;
83 struct CDInfo cu_CDInfo;
84 struct QCode cu_QCode;
85 ULONG cu_TotalSectors;
86 UBYTE cu_Sequence;
87 UBYTE cu_Muted;
88 union CDTOC cu_CDTOC[100];
89 ULONG cu_IntEnable;
90 ULONG cu_ChangeNum;
93 static inline void CD32_Mute(struct CD32Unit *cu, int muted)
95 UBYTE val;
97 Forbid();
98 val = readb(0xbfe001);
99 if (!muted) {
100 val |= 1;
101 } else {
102 val &= ~1;
104 writeb(val, 0xbfe001);
105 Permit();
107 cu->cu_Muted = muted ? 1 : 0;
111 UBYTE dec2bcd(UBYTE dec)
113 if (dec > 99)
114 return 99;
115 return ((dec / 10) << 4) | (dec % 10);
118 static void sec2msf(LONG sec, UBYTE *msf)
120 msf[0] = dec2bcd(sec / (60 * 75));
121 sec = sec % (60 * 75);
122 msf[1] = dec2bcd(sec / 75);
123 msf[2] = dec2bcd(sec % 75);
126 static ULONG msf2sec(struct RMSF *msf)
128 return ((msf->Minute * 60) + msf->Second) * 75 + msf->Frame;
131 static VOID CD32_IntEnable(struct CD32Unit *cu, ULONG mask)
133 cu->cu_IntEnable |= mask;
134 writel(cu->cu_IntEnable, AKIKO_CDINTENA);
137 static VOID CD32_IntDisable(struct CD32Unit *cu, ULONG mask)
139 cu->cu_IntEnable &= ~mask;
140 writel(cu->cu_IntEnable, AKIKO_CDINTENA);
143 static AROS_INTH1(CD32_Interrupt, struct CD32Unit *, cu)
145 const UBYTE resp_len[16] = {
146 1, 2, 2, 2, 2, 2, 15, 20, 0, 0, 2, 0, 0, 0, 0, 0
148 ULONG status;
149 UBYTE rxtail;
151 AROS_INTFUNC_INIT
153 status = readl(AKIKO_CDINTREQ);
154 if (!status)
155 return FALSE;
157 if (status & AKIKO_CDINT_RXDMA) {
158 rxtail = readb(AKIKO_CDRXINX);
159 if (((cu->cu_RxHead+1)&0xff) == rxtail) {
160 /* Add the correct length for the full response */
161 UBYTE len = resp_len[cu->cu_Misc->Response[cu->cu_RxHead] & 0xf];
162 if (len == 0) {
163 D(bug("%s: Insane response byte 0x%02x\n", cu->cu_Misc->Response[cu->cu_RxHead]));
165 writeb(cu->cu_RxHead+len+1, AKIKO_CDRXCMP);
166 return TRUE;
168 CD32_IntDisable(cu, AKIKO_CDINT_RXDMA);
169 D(bug("%s: Signal %p\n", __func__, cu->cu_Task));
170 Signal(cu->cu_Task, SIGF_SINGLE);
173 if (status & AKIKO_CDINT_TXDMA) {
174 CD32_IntDisable(cu, AKIKO_CDINT_TXDMA);
177 #if 0
178 if (status & AKIKO_CDINT_OVERFLOW) {
179 CD32_IntDisable(cu, AKIKO_CDINT_OVERFLOW);
180 Signal(cu->cu_Task, SIGF_SINGLE);
182 #endif
184 if (status & AKIKO_CDINT_PBX) {
185 UWORD pbx = readw(AKIKO_CDPBX);
186 writew(0, AKIKO_CDPBX);
187 if (pbx < 0x000f)
188 Signal(cu->cu_Task, SIGF_SINGLE);
191 return FALSE;
193 AROS_INTFUNC_EXIT
196 static UBYTE bcd2dec(UBYTE bcd)
198 return (bcd & 0xf) + ((bcd >> 4) & 0xf) * 10;
201 static VOID CD32_UpdateTOC(struct CD32Unit *cu)
203 struct QCode *qc = &cu->cu_QCode;
205 D(bug("%s: Index 0x%02x\n", __func__, qc->Index));
206 switch (qc->Index) {
207 case 0xA0:
208 cu->cu_CDTOC[0].Summary.FirstTrack = bcd2dec(qc->DiskPosition.MSF.Minute);
209 break;
210 case 0xA1:
211 cu->cu_CDTOC[0].Summary.LastTrack = bcd2dec(qc->DiskPosition.MSF.Minute);
212 break;
213 case 0xA2:
214 cu->cu_CDTOC[0].Summary.LeadOut = qc->DiskPosition;
215 cu->cu_CDInfo.Status |= CDSTSF_TOC;
216 break;
217 default:
218 if (((qc->Index & 0xf) <= 9) && ((qc->Index & 0xf0) <= 0x90) &&
219 (qc->Index != 0)) {
220 int track = bcd2dec(qc->Index);
221 cu->cu_CDTOC[track].Entry.CtlAdr = qc->CtlAdr;
222 cu->cu_CDTOC[track].Entry.Track = track;
223 cu->cu_CDTOC[track].Entry.Position = qc->DiskPosition;
224 if (track == 1 && qc->CtlAdr == 0x41) {
225 cu->cu_CDInfo.Status |= CDSTSF_CDROM;
227 } else {
228 D(bug("%s: Illegal track number 0x%02x ignored\n", __func__, qc->Index));
230 break;
234 static LONG CD32_Cmd(struct CD32Unit *cu, UBYTE *cmd, LONG cmd_len, UBYTE *resp, LONG resp_len)
236 UBYTE csum, RxTail;
237 int i;
239 cu->cu_Sequence++;
240 if (cu->cu_Sequence >= 16)
241 cu->cu_Sequence = 1;
243 cmd[0] = (cmd[0] & 0xf) | (cu->cu_Sequence << 4);
245 csum = 0;
246 for (i = 0; i < cmd_len; i++) {
247 /* NOTE: We are relying on the fact that cu_TxHead is a
248 * UBYTE, so that it wraps at 0x100
250 cu->cu_Misc->Command[cu->cu_TxHead++] = cmd[i];
251 csum += cmd[i];
254 cu->cu_Misc->Command[cu->cu_TxHead++] = ~csum;
256 /* Just wait for the RX to complete of the status */
257 CD32_IntEnable(cu, AKIKO_CDINT_RXDMA | AKIKO_CDINT_TXDMA);
258 writel(AKIKO_CDFLAG_TXD | AKIKO_CDFLAG_RXD | AKIKO_CDFLAG_CAS | AKIKO_CDFLAG_PBX | AKIKO_CDFLAG_MSB, AKIKO_CDFLAG);
260 /* Trigger the command by updating AKIKO_CDTXCMP */
261 SetSignal(0, SIGF_SINGLE);
262 writeb((cu->cu_RxHead + 1) & 0xff, AKIKO_CDRXCMP);
263 writeb(cu->cu_TxHead, AKIKO_CDTXCMP);
265 for (;;) {
266 UBYTE RxHead = cu->cu_RxHead;
268 D(bug("%s: %02x Wait...\n", __func__, cmd[0]));
269 Wait(SIGF_SINGLE);
270 D(bug("%s: %02x Waited.\n", __func__, cmd[0]));
272 RxTail = readb(AKIKO_CDRXINX);
273 D(bug("%s: RxHead=%02x RxTail=%02x\n", __func__, RxHead, RxTail));
275 if (cu->cu_Misc->Response[RxHead] == cmd[0])
276 break;
278 D(bug("%s: Found unexpected: 0x%02x (%d)\n", __func__, cu->cu_Misc->Response[RxHead], (RxTail + 256 - RxHead) & 0xff));
280 csum = 0;
281 while (RxHead != RxTail) {
282 /* NOTE: We are relying on the fact that cu_RxHead is a
283 * UBYTE, so that it wraps at 0x100
285 UBYTE val = cu->cu_Misc->Response[RxHead++];
286 D(bug("%02x ", val));
287 csum += val;
289 D(bug(" (%02x)\n", csum));
291 if (csum != 0xff) {
292 D(bug("%s: Checksum failed on RX\n", __func__));
293 return CDERR_NotSpecified;
296 RxHead = cu->cu_RxHead;
297 cu->cu_RxHead = RxTail;
299 switch (cu->cu_Misc->Response[RxHead]) {
300 case CHCD_MEDIA:
301 RxHead++;
302 if (cu->cu_Misc->Response[RxHead] == 0x83) {
303 if (!(cu->cu_CDInfo.Status & CDSTSF_DISK)) {
304 cu->cu_ChangeNum++;
305 cu->cu_CDInfo.Status |= CDSTSF_CLOSED | CDSTSF_DISK | CDSTSF_SPIN;
307 } else {
308 if (cu->cu_CDInfo.Status & CDSTSF_DISK) {
309 cu->cu_ChangeNum++;
310 cu->cu_CDInfo.Status = 0;
313 break;
314 case CHCD_SUBQ:
315 RxHead++;
316 RxHead++;
317 RxHead++;
318 cu->cu_QCode.CtlAdr = cu->cu_Misc->Response[RxHead++];
319 cu->cu_QCode.Track = cu->cu_Misc->Response[RxHead++];
320 cu->cu_QCode.Index = cu->cu_Misc->Response[RxHead++];
321 cu->cu_QCode.Zero = 0;
322 cu->cu_QCode.TrackPosition.MSF.Reserved = 0;
323 cu->cu_QCode.TrackPosition.MSF.Minute = cu->cu_Misc->Response[RxHead++];
324 cu->cu_QCode.TrackPosition.MSF.Second = cu->cu_Misc->Response[RxHead++];
325 cu->cu_QCode.TrackPosition.MSF.Frame = cu->cu_Misc->Response[RxHead++];
326 cu->cu_QCode.DiskPosition.MSF.Reserved = cu->cu_Misc->Response[RxHead++];
327 cu->cu_QCode.DiskPosition.MSF.Minute = cu->cu_Misc->Response[RxHead++];
328 cu->cu_QCode.DiskPosition.MSF.Second = cu->cu_Misc->Response[RxHead++];
329 cu->cu_QCode.DiskPosition.MSF.Frame = cu->cu_Misc->Response[RxHead++];
330 if (!(cu->cu_CDInfo.Status & CDSTSF_TOC))
331 CD32_UpdateTOC(cu);
333 break;
335 default:
336 D(bug("%s: Command mismatch: got 0x%02x, expected 0x%02x\n", __func__, cu->cu_Misc->Response[RxHead], cmd[0]));
337 return CDERR_InvalidState;
340 writeb((RxTail + 1) & 0xff, AKIKO_CDRXCMP);
343 D(bug("%s: Found expected: 0x%02x (%d)\n", __func__, cu->cu_Misc->Response[cu->cu_RxHead], (RxTail + 256 - cu->cu_RxHead) & 0xff));
345 csum = 0;
346 while (cu->cu_RxHead != RxTail) {
347 /* NOTE: We are relying on the fact that cu_RxHead is a
348 * UBYTE, so that it wraps at 0x100
350 UBYTE val = cu->cu_Misc->Response[cu->cu_RxHead++];
351 D(bug("%02x ", val));
352 if (resp_len > 0) {
353 *(resp++) = val;
354 resp_len--;
356 csum += val;
358 D(bug("(%02x)\n", csum));
360 if (csum != 0xff) {
361 D(bug("%s: Checksum failed on RX\n", __func__));
362 return CDERR_NotSpecified;
365 D(bug("%s: -- RXHead %2x --\n", __func__, cu->cu_RxHead));
367 return 0;
370 static VOID CD32_Led(struct CD32Unit *cu, BOOL led_on)
372 UBYTE cmd[2] = { CHCD_LED, (led_on ? 1 : 0) | 0x80 };
373 UBYTE resp[2];
375 CD32_Cmd(cu, cmd, 2, resp, 2);
378 static LONG CD32_CmdRead(struct CD32Unit *cu, LONG sect_start, LONG sectors, void (*copy_sector)(APTR sector, APTR priv), APTR priv)
380 LONG err;
381 UBYTE cmd[12], resp[2];
382 UBYTE cmd_pause[1] = { CHCD_PAUSE };
383 UBYTE cmd_unpause[1] = { CHCD_UNPAUSE };
384 int i;
385 UWORD pbx;
387 while (sectors > 0) {
388 cu->cu_CDInfo.Status &= ~(CDSTSF_PLAYING | CDSTSF_PAUSED | CDSTSF_SEARCH | CDSTSF_DIRECTION);
390 cmd[0] = CHCD_MULTI;
391 sec2msf(sect_start, &cmd[1]);
392 sec2msf(sect_start + 16, &cmd[4]);
393 cmd[7] = 0x80; /* Data read */
394 cmd[8] = (cu->cu_CDInfo.PlaySpeed >= 150) ? 0x40 : 0x00;
395 cmd[8] = 0x00;
396 cmd[9] = 0x00;
397 cmd[10] = 0x04; /* 0x04 to enable the motor */
398 cmd[11] = 0x00;
400 writew(0xffff, AKIKO_CDPBX);
402 /* Start the read */
403 CD32_Led(cu, TRUE);
404 CD32_Cmd(cu, cmd_unpause, 1, resp, sizeof(resp));
405 err = CD32_Cmd(cu, cmd, sizeof(cmd), resp, sizeof(resp));
406 if (err)
407 return err;
409 if ((resp[1] & 2) != 2)
410 return CDERR_SeekError;
413 /* Wait for (most) sectors to come in */
414 CD32_IntEnable(cu, AKIKO_CDINT_PBX);
416 writel(readl(AKIKO_CDFLAG) | AKIKO_CDFLAG_ENABLE, AKIKO_CDFLAG);
417 Wait(SIGF_SINGLE);
419 CD32_Cmd(cu, cmd_pause, 1, resp, sizeof(resp));
420 CD32_Led(cu, FALSE);
421 pbx = readw(AKIKO_CDPBX);
423 if (pbx == 0) {
424 D(bug("%s: Overflow during read\n", __func__));
425 break;
428 for (i = 15; i >= 0; i--) {
429 if (!(pbx & (1 << i))) {
430 D(bug("%s: SECTOR: %d\n", __func__, *(ULONG *)(&cu->cu_Data[i])));
431 copy_sector(&cu->cu_Data[i], priv);
432 sectors--;
433 sect_start++;
434 if (sectors <= 0)
435 return 0;
441 return -1;
444 static UBYTE CD32_Status(struct CD32Unit *cu)
446 UBYTE cmd[1], res[21];
448 cu->cu_Task = FindTask(NULL);
449 D(bug("%s: %p\n", __func__, cu));
450 cmd[0] = CHCD_STATUS;
451 res[1] = 0x80;
452 CD32_Cmd(cu, cmd, 1, res, 20);
453 res[20] = 0;
454 D(bug("%s: Drive \"%s\", state 0x%02x\n", __func__, &res[2], res[1]));
456 if (res[1] & CHERR_BADCOMMAND) {
457 if (cu->cu_CDInfo.Status & CDSTSF_DISK) {
458 cu->cu_ChangeNum++;
459 cu->cu_CDInfo.Status = 0;
461 } else if (res[1] & CHERR_DISKPRESENT) {
462 if (!(cu->cu_CDInfo.Status & CDSTSF_DISK)) {
463 cu->cu_ChangeNum++;
464 cu->cu_CDInfo.Status |= CDSTSF_CLOSED | CDSTSF_DISK | CDSTSF_SPIN;
468 D(bug("%s: %p = 0x%08x\n", __func__, cu, cu->cu_CDInfo.Status));
469 return cu->cu_CDInfo.Status;
472 static VOID CD32_ReadTOC(struct CD32Unit *cu)
474 UBYTE toc_cmd[12] = { CHCD_MULTI, 0, 0, 0, 0, 0, 0, 0x03, 0x40, 0, 0x00, 0 };
475 UBYTE idle_cmd[1] = { CHCD_UNPAUSE };
476 UBYTE resp[2];
477 ULONG timeout = 5 * 1000; /* 5 second timeout */
478 BOOL blink = TRUE;
480 /* Start the read TOC command */
481 if (CD32_Cmd(cu, toc_cmd, 12, resp, 2) != 0)
482 return;
484 /* Until the TOC is read, blink the LED and unpause */
485 D(bug("%s: Waiting for TOC for up to 10 seconds\n", __func__));
486 while (!(cu->cu_CDInfo.Status & CDSTSF_TOC) && timeout > 0) {
487 CD32_Led(cu, blink);
488 blink = !blink;
489 cdDelayMS(cu->cu_CDBase, 100); /* Delay 100ms */
490 CD32_Cmd(cu, idle_cmd, 1, resp, 2);
491 timeout -= 100;
494 if (cu->cu_CDInfo.Status & CDSTSF_CDROM) {
495 if (cu->cu_CDTOC[0].Summary.LastTrack == 1) {
496 cu->cu_TotalSectors = msf2sec(&cu->cu_CDTOC[0].Summary.LeadOut.MSF) -
497 msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF);
498 } else {
499 cu->cu_TotalSectors = msf2sec(&cu->cu_CDTOC[2].Entry.Position.MSF) -
500 msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF);
502 } else {
503 cu->cu_TotalSectors = 0;
506 D(bug("%s: TotalSectors = %d\n", __func__, cu->cu_TotalSectors));
509 static LONG CD32_IsCDROM(struct CD32Unit *cu)
511 if ((cu->cu_CDInfo.Status & CDSTSF_DISK) == 0) {
512 if ((CD32_Status(cu) & CDSTSF_DISK) == 0)
513 return CDERR_NoDisk;
516 if ((cu->cu_CDInfo.Status & CDSTSF_TOC) == 0) {
517 CD32_ReadTOC(cu);
520 if ((cu->cu_CDInfo.Status & CDSTSF_CDROM) == 0) {
521 return CDERR_SeekError;
524 return 0;
527 struct CopyREAD {
528 struct IOStdReq *io;
529 LONG offset;
530 UBYTE *buffer;
531 LONG length;
534 static VOID CD32_CopyREAD(APTR frame, APTR priv)
536 struct CD32Mode1 *mode1 = frame;
537 struct CopyREAD *cr = priv;
538 LONG tocopy;
540 tocopy = 2048 - cr->offset;
541 if (tocopy > cr->length)
542 tocopy = cr->length;
544 D(bug("%s: Copy from 0x%08x to 0x%08x, %d\n", __func__, &mode1->cs_Data[cr->offset], cr->buffer, tocopy));
545 CopyMem(&mode1->cs_Data[cr->offset], cr->buffer, tocopy);
546 if (0) { int i ;
547 for (i = 0; i < tocopy; i++) {
548 int mod = i % 16;
549 if (mod == 0) D(bug("%08x:", cr->buffer - (UBYTE *)cr->io->io_Data + i));
550 D(bug("%c%02x", (mod==8) ? '-' : ' ', cr->buffer[i]));
551 if (mod == 15) D(bug("\n"));
553 D(bug("\n"));
556 cr->offset = 0;
557 cr->length -= tocopy;
558 cr->buffer += tocopy;
559 cr->io->io_Actual += tocopy;
563 static LONG CD32_DoIO(struct IOStdReq *io, APTR priv)
565 struct CD32Unit *cu = priv;
566 LONG err = CDERR_NOCMD;
567 UBYTE cmd[16], res[16];
568 LONG sect_start, sect_end, origin;
569 struct CopyREAD cr;
571 D(bug("%s:%p io_Command=%d\n", __func__, io, io->io_Command));
573 cu->cu_Task = FindTask(NULL);
575 switch (io->io_Command) {
576 case CD_CHANGENUM:
577 io->io_Actual = cu->cu_ChangeNum;
578 err = 0;
579 break;
580 case CD_CHANGESTATE:
581 io->io_Actual = (cu->cu_CDInfo.Status & CDSTSF_TOC) ? 0 : 1;
582 D(bug("CD_CHANGESTATE: %d\n", io->io_Actual));
583 err = 0;
584 break;
585 case CD_CONFIG:
586 D(bug("CD_CONFIG: Data %p, Length %d\n", io->io_Data, io->io_Length));
587 if (io->io_Length != 0) {
588 err = CDERR_BADLENGTH;
589 break;
591 /* Gah. Some bright spark decided to use tags with
592 * the same values as TAG_IGNORE, TAG_MORE, and TAG_SKIP,
593 * so we have to process the TagItem array manually.
595 err = 0;
596 if (io->io_Data != NULL) {
597 struct TagItem *ti = io->io_Data;
598 BOOL done = FALSE;
599 while (!done) {
600 switch (ti->ti_Tag) {
601 case TAGCD_PLAYSPEED:
602 if (((ti->ti_Data % 75) != 0) ||
603 (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) {
604 err = CDERR_InvalidState;
605 done = TRUE;
606 break;
608 cu->cu_CDInfo.PlaySpeed = ti->ti_Data;
609 break;
610 case TAGCD_READSPEED:
611 if (((ti->ti_Data % 75) != 0) ||
612 (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) {
613 err = CDERR_InvalidState;
614 done = TRUE;
615 break;
617 cu->cu_CDInfo.ReadSpeed = ti->ti_Data;
618 break;
620 case TAGCD_READXLSPEED:
621 if (((ti->ti_Data % 75) != 0) ||
622 (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) {
623 err = CDERR_InvalidState;
624 done = TRUE;
625 break;
627 cu->cu_CDInfo.ReadXLSpeed = ti->ti_Data;
628 break;
630 case TAGCD_SECTORSIZE:
631 if (ti->ti_Data != 2048 &&
632 ti->ti_Data != 2328) {
633 err = CDERR_InvalidState;
634 done = TRUE;
635 break;
637 cu->cu_CDInfo.SectorSize = ti->ti_Data;
638 break;
640 case TAGCD_XLECC:
641 cu->cu_CDInfo.XLECC = ti->ti_Data ? 1 : 0;
642 break;
643 case TAGCD_EJECTRESET:
644 cu->cu_CDInfo.EjectReset = ti->ti_Data ? 1 : 0;
645 break;
646 case TAGCD_DONE:
647 done = TRUE;
648 break;
649 default:
650 break;
652 ti++;
653 D(bug("CD_CONFIG: Tag %d, Item %d, err %d, done %d\n", ti->ti_Tag, ti->ti_Data, err, done));
656 break;
657 case CD_TOCMSF:
658 D(bug("CD_TOCMSF: %d\n", io->io_Length));
659 err = CD32_IsCDROM(cu);
660 D(bug("CD_TOCMSF: check %d\n", err));
661 if (io->io_Data != NULL && (cu->cu_CDInfo.Status & CDSTSF_TOC)) {
662 ULONG i, actual = 0;
663 ULONG track = io->io_Offset;
664 APTR buff = io->io_Data;
665 for (i = 0; i < io->io_Length && track <= cu->cu_CDTOC[0].Summary.LastTrack; i++, track++) {
666 CopyMem(&cu->cu_CDTOC[track], buff, sizeof(union CDTOC));
667 actual++;
668 buff+=sizeof(union CDTOC);
670 io->io_Actual = actual;
671 err = 0;
673 D(bug("CD_TOCMSF: err %d\n", err));
674 break;
675 case CD_EJECT:
676 io->io_Actual = 0;
677 err = 0;
678 break;
679 case CD_GETGEOMETRY:
680 err = CD32_IsCDROM(cu);
681 if (err) {
682 D(bug("CD_GETGEOMETRY: Not a data disk\n"));
683 break;
686 if (io->io_Length >= sizeof(struct DriveGeometry)) {
687 struct DriveGeometry *dg = (struct DriveGeometry *)io->io_Data;
688 dg->dg_SectorSize = cu->cu_CDInfo.SectorSize;
689 dg->dg_TotalSectors = cu->cu_TotalSectors;
690 dg->dg_Cylinders = cu->cu_TotalSectors;
691 dg->dg_CylSectors = 1;
692 dg->dg_Heads = 1;
693 dg->dg_TrackSectors = 1;
694 dg->dg_BufMemType = MEMF_PUBLIC;
695 dg->dg_DeviceType = DG_CDROM;
696 dg->dg_Flags = DGF_REMOVABLE;
697 dg->dg_Reserved = 0;
698 io->io_Actual = sizeof(*dg);
699 err = 0;
700 } else {
701 err = CDERR_BADLENGTH;
703 break;
704 case CD_INFO:
705 if (io->io_Length >= sizeof(struct CDInfo)) {
706 CD32_IsCDROM(cu);
707 CopyMem(&cu->cu_CDInfo, io->io_Data, sizeof(struct CDInfo));
708 io->io_Actual = sizeof(struct CDInfo);
709 err = 0;
711 break;
712 case CD_PAUSE:
713 err = 0;
714 io->io_Actual = (cu->cu_CDInfo.Status & CDSTSF_PAUSED) ? 1 : 0;
715 if (cu->cu_CDInfo.Status & CDSTSF_TOC) {
716 UBYTE cmd[1] = { io->io_Length ? CHCD_PAUSE : CHCD_UNPAUSE };
717 UBYTE res[1];
718 err = CD32_Cmd(cu, cmd, 1, res, 1);
719 if (err == 0) {
720 if (io->io_Length)
721 cu->cu_CDInfo.Status |= CDSTSF_PAUSED;
722 else
723 cu->cu_CDInfo.Status &= ~CDSTSF_PAUSED;
726 break;
727 case CD_MOTOR:
728 err = 0;
729 io->io_Actual = (cu->cu_CDInfo.Status & CDSTSF_SPIN);
730 if (io->io_Length == 0) {
731 UBYTE cmd[1] = { CHCD_STOP }, res[1];
732 err = CD32_Cmd(cu, cmd, 1, res, 1);
733 if (err == 0) {
734 cu->cu_CDInfo.Status &= ~CDSTSF_SPIN;
736 } else {
737 cu->cu_CDInfo.Status |= CDSTSF_SPIN;
739 break;
740 case CD_PLAYTRACK:
741 if (io->io_Offset <= cu->cu_CDTOC[0].Summary.LastTrack) {
742 ULONG last = io->io_Offset + io->io_Length;
743 UBYTE cmd[12], res[2];
744 cmd[0] = CHCD_MULTI;
745 cmd[1] = cu->cu_CDTOC[io->io_Offset].Entry.Position.MSF.Minute;
746 cmd[2] = cu->cu_CDTOC[io->io_Offset].Entry.Position.MSF.Second;
747 cmd[3] = cu->cu_CDTOC[io->io_Offset].Entry.Position.MSF.Frame;
748 if (last > cu->cu_CDTOC[0].Summary.LastTrack) {
749 cmd[4] = cu->cu_CDTOC[0].Summary.LeadOut.MSF.Minute;
750 cmd[5] = cu->cu_CDTOC[0].Summary.LeadOut.MSF.Minute;
751 cmd[6] = cu->cu_CDTOC[0].Summary.LeadOut.MSF.Minute;
752 } else {
753 cmd[4] = cu->cu_CDTOC[last].Entry.Position.MSF.Minute;
754 cmd[5] = cu->cu_CDTOC[last].Entry.Position.MSF.Second;
755 cmd[6] = cu->cu_CDTOC[last].Entry.Position.MSF.Frame;
757 cmd[7] = 0x00;
758 cmd[8] = (cu->cu_CDInfo.ReadSpeed >= 150) ? 0x40 : 0x00;
759 cmd[9] = 0x00;
760 cmd[10] = 0x04;
761 cmd[11] = 0x00;
762 err = CD32_Cmd(cu, cmd, 12, res, 2);
763 D(bug("CD_PLAYTRACK: err=%d, res[1]=0x%02x\n", err, res[1]));
764 if (!err && (res[1] & 0x80) == 0) {
765 cu->cu_CDInfo.Status &= ~(CDSTSF_PLAYING | CDSTSF_PAUSED | CDSTSF_SEARCH | CDSTSF_DIRECTION);
766 cu->cu_CDInfo.Status |= CDSTSF_PLAYING;
767 D(bug("CD_PLAYTRACK: Playing tracks %d-%d\n", io->io_Offset, last-1));
768 err = 0;
771 break;
772 case CD_READ:
773 err = CD32_IsCDROM(cu);
774 if (err)
775 break;
777 /* Convert offset and length to MSF */
778 CD32_Led(cu, TRUE);
780 io->io_Error = 0;
781 io->io_Actual = 0;
783 origin = msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF);
784 sect_start = io->io_Offset / cu->cu_CDInfo.SectorSize;
785 sect_end = (io->io_Offset + io->io_Length + cu->cu_CDInfo.SectorSize - 1) / cu->cu_CDInfo.SectorSize;
787 cr.io = io;
788 cr.buffer = io->io_Data;
789 cr.offset = io->io_Offset % cu->cu_CDInfo.SectorSize;
790 cr.length = io->io_Length;
792 err = CD32_CmdRead(cu, sect_start + origin, sect_end - sect_start, CD32_CopyREAD, &cr);
793 break;
794 case CD_RESET:
795 cmd[0] = CHCD_RESET;
796 err = CD32_Cmd(cu, cmd, 1, res, 1);
797 CD32_Status(cu);
798 break;
799 case CD_ATTENUATE:
800 io->io_Actual = cu->cu_Muted ? 0 : 0x7fff;
801 if (io->io_Offset > 0 && io->io_Offset < 0x7fff) {
802 CD32_Mute(cu, io->io_Offset == 0);
804 err = 0;
805 break;
806 default:
807 break;
810 D(bug("%s:%p err=%d (Actual %d)\n", __func__, io, err, io->io_Actual));
811 return err;
814 static VOID CD32_Expunge(APTR priv)
816 struct CD32Unit *cu = priv;
817 RemIntServer(INTB_PORTS, &cu->cu_Interrupt);
818 FreeMem(cu->cu_Misc, sizeof(struct CD32Misc));
819 FreeMem(cu->cu_Data, sizeof(struct CD32Data) * 16);
820 FreeVec(cu);
823 static const struct cdUnitOps CD32Ops = {
824 .uo_Name = "CD32 (Akiko)",
825 .uo_Expunge = CD32_Expunge,
826 .uo_DoIO = CD32_DoIO,
829 static const struct DosEnvec CD32Envec = {
830 .de_TableSize = DE_MASK,
831 .de_SizeBlock = 2048 >> 2,
832 .de_Surfaces = 1,
833 .de_SectorPerBlock = 1,
834 .de_BlocksPerTrack = 1,
835 .de_Reserved = 0,
836 .de_PreAlloc = 0,
837 .de_Interleave = 0,
838 .de_LowCyl = 0,
839 .de_HighCyl = 0,
840 .de_NumBuffers = 32,
841 .de_BufMemType = MEMF_24BITDMA,
842 .de_MaxTransfer = 32 * 2048,
843 .de_Mask = 0x00fffffe,
847 static int CD32_InitLib(LIBBASETYPE *cb)
849 LONG unit;
850 struct CD32Unit *priv;
852 if (readw(AKIKO_ID) != AKIKO_ID_MAGIC) {
853 D(bug("%s: No Akiko detected\n", __func__));
854 return 1;
857 /* Reset the Akiko CD32 interface */
858 writeb(readb(AKIKO_CDRESET) | 0x80, AKIKO_CDRESET);
859 writeb(readb(AKIKO_CDRESET) & ~0x80, AKIKO_CDRESET);
861 /* Disable the CD32 interface for the moment */
862 priv = AllocVec(sizeof(*priv), MEMF_ANY | MEMF_CLEAR);
863 if (priv) {
864 priv->cu_CDBase = cb;
865 priv->cu_Misc = LibAllocAligned(sizeof(struct CD32Misc), MEMF_24BITDMA | MEMF_CLEAR, 1024);
866 if (priv->cu_Misc) {
867 priv->cu_Data = LibAllocAligned(sizeof(struct CD32Data) * 16, MEMF_24BITDMA | MEMF_CLEAR, 4096);
868 if (priv->cu_Data) {
869 priv->cu_Interrupt.is_Node.ln_Pri = 0;
870 priv->cu_Interrupt.is_Node.ln_Name = (STRPTR)CD32Ops.uo_Name;
871 priv->cu_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
872 priv->cu_Interrupt.is_Data = priv;
873 priv->cu_Interrupt.is_Code = (VOID_FUNC)CD32_Interrupt;
874 AddIntServer(INTB_PORTS, &priv->cu_Interrupt);
876 writel((IPTR)priv->cu_Data, AKIKO_CDADRDATA);
877 writel((IPTR)priv->cu_Misc, AKIKO_CDADRMISC);
878 priv->cu_TxHead = readb(AKIKO_CDTXINX);
879 priv->cu_RxHead = readb(AKIKO_CDRXINX);
880 writeb(priv->cu_TxHead, AKIKO_CDTXCMP);
881 writeb(priv->cu_RxHead, AKIKO_CDRXCMP);
883 priv->cu_CDInfo.SectorSize = 2048;
884 priv->cu_CDInfo.MaxSpeed = 150;
885 priv->cu_CDInfo.PlaySpeed = 75;
886 priv->cu_CDInfo.ReadSpeed = 150;
887 priv->cu_CDInfo.ReadXLSpeed = 150;
888 priv->cu_CDInfo.AudioPrecision = 1;
889 CD32_IsCDROM(priv);
891 unit = cdAddUnit(cb, &CD32Ops, priv, &CD32Envec);
892 if (unit >= 0) {
893 D(bug("%s: Akiko as CD Unit %d\n", __func__, unit));
894 return 1;
897 /* Unmute the CDROM */
898 CD32_Mute(priv, 0);
900 RemIntServer(INTB_PORTS, &priv->cu_Interrupt);
902 FreeMem(priv->cu_Data, sizeof(struct CD32Data) * 16);
904 FreeMem(priv->cu_Misc, sizeof(struct CD32Misc));
906 FreeVec(priv);
909 return 0;
912 static int CD32_ExpungeLib(LIBBASETYPE *cb)
914 /* Nothing to do. */
915 return 0;
919 ADD2INITLIB(CD32_InitLib, 32);
920 ADD2EXPUNGELIB(CD32_ExpungeLib, 32);