cd.device: CD32 CDROM support
[AROS.git] / arch / m68k-amiga / devs / cd / cd32.c
blobb5691454680d721f37eb0634b1e086f15f1bd268
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 union CDTOC cu_CDTOC[100];
88 ULONG cu_IntEnable;
89 ULONG cu_ChangeNum;
92 UBYTE dec2bcd(UBYTE dec)
94 if (dec > 99)
95 return 99;
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
129 ULONG status;
130 UBYTE rxtail;
132 AROS_INTFUNC_INIT
134 status = readl(AKIKO_CDINTREQ);
135 if (!status)
136 return FALSE;
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];
143 if (len == 0) {
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);
147 return TRUE;
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);
158 #if 0
159 if (status & AKIKO_CDINT_OVERFLOW) {
160 CD32_IntDisable(cu, AKIKO_CDINT_OVERFLOW);
161 Signal(cu->cu_Task, SIGF_SINGLE);
163 #endif
165 if (status & AKIKO_CDINT_PBX) {
166 UWORD pbx = readw(AKIKO_CDPBX);
167 writew(0, AKIKO_CDPBX);
168 if (pbx < 0x000f)
169 Signal(cu->cu_Task, SIGF_SINGLE);
172 return FALSE;
174 AROS_INTFUNC_EXIT
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));
187 switch (qc->Index) {
188 case 0xA0:
189 cu->cu_CDTOC[0].Summary.FirstTrack = bcd2dec(qc->DiskPosition.MSF.Minute);
190 break;
191 case 0xA1:
192 cu->cu_CDTOC[0].Summary.LastTrack = bcd2dec(qc->DiskPosition.MSF.Minute);
193 break;
194 case 0xA2:
195 cu->cu_CDTOC[0].Summary.LeadOut = qc->DiskPosition;
196 cu->cu_CDInfo.Status |= CDSTSF_TOC;
197 break;
198 default:
199 if (((qc->Index & 0xf) <= 9) && ((qc->Index & 0xf0) <= 0x90) &&
200 (qc->Index != 0)) {
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;
208 } else {
209 D(bug("%s: Illegal track number 0x%02x ignored\n", __func__, qc->Index));
211 break;
215 static LONG CD32_Cmd(struct CD32Unit *cu, UBYTE *cmd, LONG cmd_len, UBYTE *resp, LONG resp_len)
217 UBYTE csum, RxTail;
218 int i;
220 cu->cu_Sequence++;
221 if (cu->cu_Sequence >= 16)
222 cu->cu_Sequence = 1;
224 cmd[0] = (cmd[0] & 0xf) | (cu->cu_Sequence << 4);
226 csum = 0;
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];
232 csum += 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);
246 for (;;) {
247 UBYTE RxHead = cu->cu_RxHead;
249 D(bug("%s: %02x Wait...\n", __func__, cmd[0]));
250 Wait(SIGF_SINGLE);
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])
257 break;
259 D(bug("%s: Found unexpected: 0x%02x (%d)\n", __func__, cu->cu_Misc->Response[RxHead], (RxTail + 256 - RxHead) & 0xff));
261 csum = 0;
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));
268 csum += val;
270 D(bug(" (%02x)\n", csum));
272 if (csum != 0xff) {
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]) {
281 case CHCD_MEDIA:
282 RxHead++;
283 if (cu->cu_Misc->Response[RxHead] == 0x83) {
284 if (!(cu->cu_CDInfo.Status & CDSTSF_DISK)) {
285 cu->cu_ChangeNum++;
286 cu->cu_CDInfo.Status |= CDSTSF_CLOSED | CDSTSF_DISK | CDSTSF_SPIN;
288 } else {
289 if (cu->cu_CDInfo.Status & CDSTSF_DISK) {
290 cu->cu_ChangeNum++;
291 cu->cu_CDInfo.Status = 0;
294 break;
295 case CHCD_SUBQ:
296 RxHead++;
297 RxHead++;
298 RxHead++;
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))
312 CD32_UpdateTOC(cu);
314 break;
316 default:
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));
326 csum = 0;
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));
333 if (resp_len > 0) {
334 *(resp++) = val;
335 resp_len--;
337 csum += val;
339 D(bug("(%02x)\n", csum));
341 if (csum != 0xff) {
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));
348 return 0;
351 static VOID CD32_Led(struct CD32Unit *cu, BOOL led_on)
353 UBYTE cmd[2] = { CHCD_LED, (led_on ? 1 : 0) | 0x80 };
354 UBYTE resp[2];
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)
361 LONG err;
362 UBYTE cmd[12], resp[2];
363 UBYTE cmd_pause[1] = { CHCD_PAUSE };
364 UBYTE cmd_unpause[1] = { CHCD_UNPAUSE };
365 int i;
366 UWORD pbx;
368 while (sectors > 0) {
369 cu->cu_CDInfo.Status &= ~(CDSTSF_PLAYING | CDSTSF_PAUSED | CDSTSF_SEARCH | CDSTSF_DIRECTION);
371 cmd[0] = CHCD_MULTI;
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;
376 cmd[8] = 0x00;
377 cmd[9] = 0x00;
378 cmd[10] = 0x04; /* 0x04 to enable the motor */
379 cmd[11] = 0x00;
381 writew(0xffff, AKIKO_CDPBX);
383 /* Start the read */
384 CD32_Led(cu, TRUE);
385 CD32_Cmd(cu, cmd_unpause, 1, resp, sizeof(resp));
386 err = CD32_Cmd(cu, cmd, sizeof(cmd), resp, sizeof(resp));
387 if (err)
388 return err;
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);
398 Wait(SIGF_SINGLE);
400 CD32_Cmd(cu, cmd_pause, 1, resp, sizeof(resp));
401 CD32_Led(cu, FALSE);
402 pbx = readw(AKIKO_CDPBX);
404 if (pbx == 0) {
405 D(bug("%s: Overflow during read\n", __func__));
406 break;
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);
413 sectors--;
414 sect_start++;
415 if (sectors <= 0)
416 return 0;
422 return -1;
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;
432 res[1] = 0x80;
433 CD32_Cmd(cu, cmd, 1, res, 20);
434 res[20] = 0;
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) {
439 cu->cu_ChangeNum++;
440 cu->cu_CDInfo.Status = 0;
442 } else if (res[1] & CHERR_DISKPRESENT) {
443 if (!(cu->cu_CDInfo.Status & CDSTSF_DISK)) {
444 cu->cu_ChangeNum++;
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 };
457 UBYTE resp[2];
458 ULONG timeout = 5 * 1000; /* 5 second timeout */
459 BOOL blink = TRUE;
461 /* Start the read TOC command */
462 if (CD32_Cmd(cu, toc_cmd, 12, resp, 2) != 0)
463 return;
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) {
468 CD32_Led(cu, blink);
469 blink = !blink;
470 cdDelayMS(cu->cu_CDBase, 100); /* Delay 100ms */
471 CD32_Cmd(cu, idle_cmd, 1, resp, 2);
472 timeout -= 100;
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);
479 } else {
480 cu->cu_TotalSectors = msf2sec(&cu->cu_CDTOC[2].Entry.Position.MSF) -
481 msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF);
483 } else {
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)
494 return CDERR_NoDisk;
497 if ((cu->cu_CDInfo.Status & CDSTSF_TOC) == 0) {
498 CD32_ReadTOC(cu);
501 if ((cu->cu_CDInfo.Status & CDSTSF_CDROM) == 0) {
502 return CDERR_SeekError;
505 return 0;
508 struct CopyREAD {
509 struct IOStdReq *io;
510 LONG offset;
511 UBYTE *buffer;
512 LONG length;
515 static VOID CD32_CopyREAD(APTR frame, APTR priv)
517 struct CD32Mode1 *mode1 = frame;
518 struct CopyREAD *cr = priv;
519 LONG tocopy;
521 tocopy = 2048 - cr->offset;
522 if (tocopy > cr->length)
523 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);
527 if (0) { int i ;
528 for (i = 0; i < tocopy; i++) {
529 int mod = i % 16;
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"));
534 D(bug("\n"));
537 cr->offset = 0;
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;
550 struct CopyREAD cr;
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) {
557 case CD_CHANGENUM:
558 io->io_Actual = cu->cu_ChangeNum;
559 err = 0;
560 break;
561 case CD_CHANGESTATE:
562 io->io_Actual = (cu->cu_CDInfo.Status & CDSTSF_TOC) ? 0 : 1;
563 D(bug("CD_CHANGESTATE: %d\n", io->io_Actual));
564 err = 0;
565 break;
566 case CD_CONFIG:
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;
570 break;
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.
576 err = 0;
577 if (io->io_Data != NULL) {
578 struct TagItem *ti = io->io_Data;
579 BOOL done = FALSE;
580 while (!done) {
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;
586 done = TRUE;
587 break;
589 cu->cu_CDInfo.PlaySpeed = ti->ti_Data;
590 break;
591 case TAGCD_READSPEED:
592 if (((ti->ti_Data % 75) != 0) ||
593 (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) {
594 err = CDERR_InvalidState;
595 done = TRUE;
596 break;
598 cu->cu_CDInfo.ReadSpeed = ti->ti_Data;
599 break;
601 case TAGCD_READXLSPEED:
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.ReadXLSpeed = ti->ti_Data;
609 break;
611 case TAGCD_SECTORSIZE:
612 if (ti->ti_Data != 2048 &&
613 ti->ti_Data != 2328) {
614 err = CDERR_InvalidState;
615 done = TRUE;
616 break;
618 cu->cu_CDInfo.SectorSize = ti->ti_Data;
619 break;
621 case TAGCD_XLECC:
622 cu->cu_CDInfo.XLECC = ti->ti_Data ? 1 : 0;
623 break;
624 case TAGCD_EJECTRESET:
625 cu->cu_CDInfo.EjectReset = ti->ti_Data ? 1 : 0;
626 break;
627 case TAGCD_DONE:
628 done = TRUE;
629 break;
630 default:
631 break;
633 ti++;
634 D(bug("CD_CONFIG: Tag %d, Item %d, err %d, done %d\n", ti->ti_Tag, ti->ti_Data, err, done));
637 break;
638 case CD_TOCMSF:
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)) {
643 ULONG i, actual = 0;
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));
648 actual++;
649 buff+=sizeof(union CDTOC);
651 io->io_Actual = actual;
652 err = 0;
654 D(bug("CD_TOCMSF: err %d\n", err));
655 break;
656 case CD_EJECT:
657 io->io_Actual = 0;
658 err = 0;
659 break;
660 case CD_GETGEOMETRY:
661 err = CD32_IsCDROM(cu);
662 if (err) {
663 D(bug("CD_GETGEOMETRY: Not a data disk\n"));
664 break;
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;
673 dg->dg_Heads = 1;
674 dg->dg_TrackSectors = 1;
675 dg->dg_BufMemType = MEMF_PUBLIC;
676 dg->dg_DeviceType = DG_CDROM;
677 dg->dg_Flags = DGF_REMOVABLE;
678 dg->dg_Reserved = 0;
679 io->io_Actual = sizeof(*dg);
680 err = 0;
681 } else {
682 err = CDERR_BADLENGTH;
684 break;
685 case CD_INFO:
686 if (io->io_Length >= sizeof(struct CDInfo)) {
687 CD32_IsCDROM(cu);
688 CopyMem(&cu->cu_CDInfo, io->io_Data, sizeof(struct CDInfo));
689 io->io_Actual = sizeof(struct CDInfo);
690 err = 0;
692 break;
693 case CD_MOTOR:
694 /* FIXME: Should this be a pause/unpause command?
695 * Or just ignored?
697 io->io_Actual = 1;
698 err = 0;
699 break;
700 case CD_PLAYTRACK:
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];
704 cmd[0] = CHCD_MULTI;
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;
712 } else {
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;
717 cmd[7] = 0x00;
718 cmd[8] = (cu->cu_CDInfo.ReadSpeed >= 150) ? 0x40 : 0x00;
719 cmd[9] = 0x00;
720 cmd[10] = 0x04;
721 cmd[11] = 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]));
724 if (!err &&
725 (res[1] & 0x80) == 0 &&
726 (res[1] & 0x08)) {
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));
730 err = 0;
733 break;
734 case CD_READ:
735 err = CD32_IsCDROM(cu);
736 if (err)
737 break;
739 /* Convert offset and length to MSF */
740 CD32_Led(cu, TRUE);
742 io->io_Error = 0;
743 io->io_Actual = 0;
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;
749 cr.io = io;
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);
755 break;
756 case CD_RESET:
757 cmd[0] = CHCD_RESET;
758 err = CD32_Cmd(cu, cmd, 1, res, 1);
759 break;
760 default:
761 break;
764 D(bug("%s:%p err=%d (Actual %d)\n", __func__, io, err, io->io_Actual));
765 return err;
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);
774 FreeVec(cu);
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,
786 .de_Surfaces = 1,
787 .de_SectorPerBlock = 1,
788 .de_BlocksPerTrack = 1,
789 .de_Reserved = 0,
790 .de_PreAlloc = 0,
791 .de_Interleave = 0,
792 .de_LowCyl = 0,
793 .de_HighCyl = 0,
794 .de_NumBuffers = 32,
795 .de_BufMemType = MEMF_24BITDMA,
796 .de_MaxTransfer = 32 * 2048,
797 .de_Mask = 0x00fffffe,
801 static int CD32_InitLib(LIBBASETYPE *cb)
803 LONG unit;
804 struct CD32Unit *priv;
806 if (readw(AKIKO_ID) != AKIKO_ID_MAGIC) {
807 D(bug("%s: No Akiko detected\n", __func__));
808 return 1;
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);
817 if (priv) {
818 priv->cu_CDBase = cb;
819 priv->cu_Misc = LibAllocAligned(sizeof(struct CD32Misc), MEMF_24BITDMA | MEMF_CLEAR, 1024);
820 if (priv->cu_Misc) {
821 priv->cu_Data = LibAllocAligned(sizeof(struct CD32Data) * 16, MEMF_24BITDMA | MEMF_CLEAR, 4096);
822 if (priv->cu_Data) {
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;
842 CD32_IsCDROM(priv);
844 unit = cdAddUnit(cb, &CD32Ops, priv, &CD32Envec);
845 if (unit >= 0) {
846 D(bug("%s: Akiko as CD Unit %d\n", __func__, unit));
847 return 1;
850 RemIntServer(INTB_PORTS, &priv->cu_Interrupt);
852 FreeMem(priv->cu_Data, sizeof(struct CD32Data) * 16);
854 FreeMem(priv->cu_Misc, sizeof(struct CD32Misc));
856 FreeVec(priv);
859 return 0;
862 static int CD32_ExpungeLib(LIBBASETYPE *cb)
864 /* Nothing to do. */
865 return 0;
869 ADD2INITLIB(CD32_InitLib, 32);
870 ADD2EXPUNGELIB(CD32_ExpungeLib, 32);