From bc101c0904a1c75d712d89b2e48772885009f787 Mon Sep 17 00:00:00 2001 From: jmcmullan Date: Sat, 2 Feb 2013 19:17:57 +0000 Subject: [PATCH] cd.device: CD32 CDROM support The Amiga CD32's Akiko interface to the proprietary Chinon 365 CDROM drive is presented via the cd.device. Hopefully this will end up as a common wrapper around the Akiko, ATAPI, USB-CDROM, and SCSI cdrom devices. For now, however, the focus is on CD32 support. Please note that this device uses the CDFS handler, not CDVDFS. Signed-off-by: Jason S. McMullan git-svn-id: https://svn.aros.org/svn/aros/trunk/AROS@46456 fb15a70f-31f2-0310-bbcc-cdcc74a49acc --- arch/m68k-amiga/devs/cd/cd.c | 273 +++++++++++ arch/m68k-amiga/devs/cd/cd.conf | 19 + arch/m68k-amiga/devs/cd/cd32.c | 870 ++++++++++++++++++++++++++++++++++ arch/m68k-amiga/devs/cd/cd32.h | 62 +++ arch/m68k-amiga/devs/cd/cd_intern.h | 43 ++ arch/m68k-amiga/devs/cd/chinon.h | 149 ++++++ arch/m68k-amiga/devs/cd/mmakefile.src | 18 + compiler/include/devices/cd.h | 185 ++++++++ 8 files changed, 1619 insertions(+) create mode 100644 arch/m68k-amiga/devs/cd/cd.c create mode 100644 arch/m68k-amiga/devs/cd/cd.conf create mode 100644 arch/m68k-amiga/devs/cd/cd32.c create mode 100644 arch/m68k-amiga/devs/cd/cd32.h create mode 100644 arch/m68k-amiga/devs/cd/cd_intern.h create mode 100644 arch/m68k-amiga/devs/cd/chinon.h create mode 100644 arch/m68k-amiga/devs/cd/mmakefile.src diff --git a/arch/m68k-amiga/devs/cd/cd.c b/arch/m68k-amiga/devs/cd/cd.c new file mode 100644 index 0000000000..987718ff09 --- /dev/null +++ b/arch/m68k-amiga/devs/cd/cd.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2013, The AROS Development Team + * All right reserved. + * Author: Jason S. McMullan + * + * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 + */ + +#include +#include +#include + +#include + +#include + +#include + +#include "cd_intern.h" + +struct cdUnit { + struct MinNode cu_Node; + LONG cu_Unit; + ULONG cu_Usage; + APTR cu_Private; + const struct cdUnitOps *cu_UnitOps; + struct Task *cu_Task; + struct MsgPort *cu_MsgPort; +}; + +/* We have a synchonous task for dispatching IO + * to each cd.device unit. + */ +static VOID cdTask(IPTR base, IPTR unit) +{ + struct cdUnit *cu = (APTR)unit; + struct IOStdReq *io; + + D(bug("%s.%d Task, Port %p\n", cu->cu_UnitOps->uo_Name, cu->cu_Unit, cu->cu_MsgPort)); + do { + WaitPort(cu->cu_MsgPort); + io = (struct IOStdReq *)GetMsg(cu->cu_MsgPort); + + D(bug("%s: Processing %p\n", __func__, io)); + + if (io->io_Flags & IOF_ABORT) { + io->io_Error = CDERR_ABORTED; + } else if (io->io_Unit == (struct Unit *)cu && + cu->cu_UnitOps->uo_DoIO != NULL) { + io->io_Error = cu->cu_UnitOps->uo_DoIO(io, cu->cu_Private); + } else { + io->io_Error = CDERR_NOCMD; + } + + D(bug("%s: Reply %p\n", __func__, io)); + ReplyMsg(&io->io_Message); + + } while (io->io_Unit != NULL); + + /* Terminate by fallthough */ +} + +/* Add a bootnode using expansion.library */ +static BOOL cdRegisterVolume(struct cdUnit *unit, const struct DosEnvec *de) +{ + struct ExpansionBase *ExpansionBase; + struct DeviceNode *devnode; + TEXT dosdevname[4] = "CD0"; + + ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library", + 40L); + + if (ExpansionBase) + { + IPTR pp[24]; + + CopyMem((IPTR *)de, &pp[4], sizeof(IPTR)*de->de_TableSize); + + /* This should be dealt with using some sort of volume manager or such. */ + if (unit->cu_Unit < 10) + dosdevname[2] += unit->cu_Unit % 10; + else + dosdevname[2] = 'A' - 10 + unit->cu_Unit; + + pp[0] = (IPTR)dosdevname; + pp[1] = (IPTR)MOD_NAME_STRING; + pp[2] = unit->cu_Unit; + pp[DE_TABLESIZE + 4] = DE_BOOTBLOCKS; + pp[DE_BOOTPRI + 4] = -10; + pp[DE_DOSTYPE + 4] = AROS_MAKE_ID('C','D','F','S'); + pp[DE_CONTROL + 4] = 0; + pp[DE_BOOTBLOCKS + 4] = 0; + + devnode = MakeDosNode(pp); + + if (devnode) + { + AddBootNode(pp[DE_BOOTPRI + 4], ADNF_STARTPROC, devnode, NULL); + + return TRUE; + } + + CloseLibrary((struct Library *)ExpansionBase); + } + + return FALSE; +} + +VOID cdDelayMS(struct cdBase *cb, ULONG ms) +{ + cb->cb_TimerPort.mp_SigTask = FindTask(NULL); + cb->cb_TimerRequest.tr_node.io_Command = TR_ADDREQUEST; + cb->cb_TimerRequest.tr_time.tv_secs = ms / 1000; + cb->cb_TimerRequest.tr_time.tv_micro = (ms * 1000) % 1000000; + + DoIO((struct IORequest *)&cb->cb_TimerRequest); +} + + +LONG cdAddUnit(struct cdBase *cb, const struct cdUnitOps *ops, APTR priv, const struct DosEnvec *de) +{ + struct cdUnit *cu; + + cu = AllocVec(sizeof(*cu), MEMF_CLEAR | MEMF_ANY); + if (cu) { + cu->cu_Private = priv; + cu->cu_UnitOps = ops; + cu->cu_Task = NewCreateTask(TASKTAG_PC, cdTask, + TASKTAG_NAME, ops->uo_Name, + TASKTAG_ARG1, cb, + TASKTAG_ARG2, cu, + TASKTAG_TASKMSGPORT, &cu->cu_MsgPort, + TAG_END); + if (cu->cu_Task) { + cu->cu_Unit = cb->cb_MaxUnit++; + ObtainSemaphore(&cb->cb_UnitsLock); + ADDTAIL(&cb->cb_Units, &cu->cu_Node); + ReleaseSemaphore(&cb->cb_UnitsLock); + cdRegisterVolume(cu, de); + return cu->cu_Unit; + } + FreeVec(cu); + } + + return -1; +} + +static int cd_Init(LIBBASETYPE *cb) +{ + NEWLIST(&cb->cb_Units); + InitSemaphore(&cb->cb_UnitsLock); + + /* Hand-hacked port for timer responses */ + cb->cb_TimerPort.mp_SigBit = SIGB_SINGLE; + cb->cb_TimerPort.mp_Flags = PA_SIGNAL; + cb->cb_TimerPort.mp_SigTask = FindTask(NULL); + cb->cb_TimerPort.mp_Node.ln_Type = NT_MSGPORT; + + cb->cb_TimerRequest.tr_node.io_Message.mn_ReplyPort = &cb->cb_TimerPort; + cb->cb_TimerRequest.tr_node.io_Message.mn_Length = sizeof(cb->cb_TimerRequest); + + return (0 == OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)&cb->cb_TimerRequest, 0)); +} + +static int cd_Expunge(LIBBASETYPE *cb) +{ + struct cdUnit *cu; + struct IORequest io; + struct MsgPort *mp = CreateMsgPort(); + + CloseDevice((struct IORequest *)&cb->cb_TimerPort); + + while ((cu = (APTR)REMOVE(&cb->cb_Units)) != NULL) { + D(bug("%s: Remove Unit %d\n", __func__, cu->cu_Unit)); + + /* Shut down the unit's task */ + io.io_Device = (struct Device *)cb; + io.io_Unit = NULL; + io.io_Command = CMD_INVALID; + io.io_Message.mn_ReplyPort = mp; + io.io_Message.mn_Length = sizeof(io); + PutMsg(cu->cu_MsgPort, &io.io_Message); + WaitPort(mp); + GetMsg(mp); + + if (cu->cu_UnitOps->uo_Expunge) + cu->cu_UnitOps->uo_Expunge(cu->cu_Private); + FreeVec(cu); + } + + DeleteMsgPort(mp); + + return 1; +} + +ADD2INITLIB(cd_Init, 0); +ADD2EXPUNGELIB(cd_Expunge, 0); + +AROS_LH1(void, BeginIO, + AROS_LHA(struct IORequest *, io, A1), + LIBBASETYPEPTR, LIBBASE, 5, cd) +{ + AROS_LIBFUNC_INIT + + struct IOStdReq *iostd = (struct IOStdReq *)io; + struct cdUnit *cu = (struct cdUnit *)(io->io_Unit); + + D(bug("%s.%d: %p\n" + "io_Command: %d\n" + "io_Length: %d\n" + "io_Data: %p\n" + "io_Offset: %d\n", + __func__, cu->cu_Unit, iostd, + iostd->io_Command, + iostd->io_Length, iostd->io_Data, iostd->io_Offset)); + + io->io_Error = CDERR_NOCMD; + + io->io_Flags &= ~IOF_QUICK; + PutMsg(cu->cu_MsgPort, &iostd->io_Message); + + return; + + AROS_LIBFUNC_EXIT +} + +AROS_LH1(LONG, AbortIO, + AROS_LHA(struct IORequest *, io, A1), + LIBBASETYPEPTR, LIBBASE, 6, cd) +{ + AROS_LIBFUNC_INIT + + D(bug("%s.%d: %p\n", __func__, ((struct cdUnit *)(io->io_Unit))->cu_Unit, io)); + Forbid(); + io->io_Flags |= IOF_ABORT; + Permit(); + + return TRUE; + + AROS_LIBFUNC_EXIT +} + +static int GM_UNIQUENAME(Open)(LIBBASETYPEPTR cdBase, struct IOStdReq *ioreq, ULONG unitnum, ULONG flags) +{ + struct cdUnit *cu = NULL; + + ObtainSemaphore(&cdBase->cb_UnitsLock); + ForeachNode(&cdBase->cb_Units, cu) { + if (cu->cu_Unit == unitnum) { + cu->cu_Usage++; + ioreq->io_Unit = (struct Unit *)cu; + ReleaseSemaphore(&cdBase->cb_UnitsLock); + return TRUE; + } + } + ReleaseSemaphore(&cdBase->cb_UnitsLock); + + return FALSE; +} + +static int GM_UNIQUENAME(Close)(LIBBASETYPEPTR cdBase, struct IORequest *ioreq) +{ + struct cdUnit *cu = (APTR)ioreq->io_Unit; + + ObtainSemaphore(&cdBase->cb_UnitsLock); + cu->cu_Usage--; + ReleaseSemaphore(&cdBase->cb_UnitsLock); + + return TRUE; +} + +ADD2OPENDEV(GM_UNIQUENAME(Open),0) +ADD2CLOSEDEV(GM_UNIQUENAME(Close),0) diff --git a/arch/m68k-amiga/devs/cd/cd.conf b/arch/m68k-amiga/devs/cd/cd.conf new file mode 100644 index 0000000000..375d3e7dc0 --- /dev/null +++ b/arch/m68k-amiga/devs/cd/cd.conf @@ -0,0 +1,19 @@ +##begin config +basename cd +version 40.0 +libbasetype struct cdBase +residentpri 5 +beginio_func BeginIO +abortio_func AbortIO +options noexpunge +##end config + +##begin cdefprivate +#include "cd_intern.h" +##end cdefprivate + +##begin cdef +#include +#include +#include +##end cdef diff --git a/arch/m68k-amiga/devs/cd/cd32.c b/arch/m68k-amiga/devs/cd/cd32.c new file mode 100644 index 0000000000..b569145468 --- /dev/null +++ b/arch/m68k-amiga/devs/cd/cd32.c @@ -0,0 +1,870 @@ +/* + * Copyright (C) 2013, The AROS Development Team + * All right reserved. + * Author: Jason S. McMullan + * + * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 + */ + +#include + +#include + +#include + +#include +#include + +#include + +#include "chinon.h" +#include "cd32.h" + +#include "cd_intern.h" + +static inline ULONG readl(ULONG addr) +{ + return *((volatile ULONG *)addr); +} + +static inline VOID writel(ULONG value, ULONG addr) +{ + *((volatile ULONG *)addr) = value; +} + +static inline UWORD readw(ULONG addr) +{ + return *((volatile UWORD *)addr); +} + +static inline VOID writew(UWORD value, ULONG addr) +{ + *((volatile UWORD *)addr) = value; +} + +static inline UBYTE readb(ULONG addr) +{ + return *((volatile UBYTE *)addr); +} + +static inline VOID writeb(UBYTE value, ULONG addr) +{ + *((volatile UBYTE *)addr) = value; +} + +struct CD32Mode1 { + ULONG cs_Sector; + UBYTE cs_Reserved[8]; + UBYTE cs_MinuteBCD; + UBYTE cs_SecondBCD; + UBYTE cs_FrameBCD; + UBYTE cs_Mode; + UBYTE cs_Data[2048]; + UBYTE cs_EDC[4]; + UBYTE cs_Reserved2[8]; + UBYTE cs_ECC[276]; +}; + +struct CD32Unit { + LIBBASETYPE *cu_CDBase; + struct CD32Data { + UBYTE Data[0xc00]; + UBYTE Error[0x400]; + } *cu_Data; /* x16 of these */ + struct CD32Misc { + UBYTE Response[0x100]; + UBYTE Subcode[0x100]; + UBYTE Command[0x100]; + UBYTE Reserved[0x100]; + } *cu_Misc; + struct Interrupt cu_Interrupt; + UBYTE cu_TxHead, cu_RxHead; + struct Task *cu_Task; + struct CDInfo cu_CDInfo; + struct QCode cu_QCode; + ULONG cu_TotalSectors; + UBYTE cu_Sequence; + union CDTOC cu_CDTOC[100]; + ULONG cu_IntEnable; + ULONG cu_ChangeNum; +}; + +UBYTE dec2bcd(UBYTE dec) +{ + if (dec > 99) + return 99; + return ((dec / 10) << 4) | (dec % 10); +} + +static void sec2msf(LONG sec, UBYTE *msf) +{ + msf[0] = dec2bcd(sec / (60 * 75)); + sec = sec % (60 * 75); + msf[1] = dec2bcd(sec / 75); + msf[2] = dec2bcd(sec % 75); +} + +static ULONG msf2sec(struct RMSF *msf) +{ + return ((msf->Minute * 60) + msf->Second) * 75 + msf->Frame; +} + +static VOID CD32_IntEnable(struct CD32Unit *cu, ULONG mask) +{ + cu->cu_IntEnable |= mask; + writel(cu->cu_IntEnable, AKIKO_CDINTENA); +} + +static VOID CD32_IntDisable(struct CD32Unit *cu, ULONG mask) +{ + cu->cu_IntEnable &= ~mask; + writel(cu->cu_IntEnable, AKIKO_CDINTENA); +} + +static AROS_INTH1(CD32_Interrupt, struct CD32Unit *, cu) +{ + const UBYTE resp_len[16] = { + 1, 2, 2, 2, 2, 2, 15, 20, 0, 0, 2, 0, 0, 0, 0, 0 + }; + ULONG status; + UBYTE rxtail; + + AROS_INTFUNC_INIT + + status = readl(AKIKO_CDINTREQ); + if (!status) + return FALSE; + + if (status & AKIKO_CDINT_RXDMA) { + rxtail = readb(AKIKO_CDRXINX); + if (((cu->cu_RxHead+1)&0xff) == rxtail) { + /* Add the correct length for the full response */ + UBYTE len = resp_len[cu->cu_Misc->Response[cu->cu_RxHead] & 0xf]; + if (len == 0) { + D(bug("%s: Insane response byte 0x%02x\n", cu->cu_Misc->Response[cu->cu_RxHead])); + } + writeb(cu->cu_RxHead+len+1, AKIKO_CDRXCMP); + return TRUE; + } + CD32_IntDisable(cu, AKIKO_CDINT_RXDMA); + D(bug("%s: Signal %p\n", __func__, cu->cu_Task)); + Signal(cu->cu_Task, SIGF_SINGLE); + } + + if (status & AKIKO_CDINT_TXDMA) { + CD32_IntDisable(cu, AKIKO_CDINT_TXDMA); + } + +#if 0 + if (status & AKIKO_CDINT_OVERFLOW) { + CD32_IntDisable(cu, AKIKO_CDINT_OVERFLOW); + Signal(cu->cu_Task, SIGF_SINGLE); + } +#endif + + if (status & AKIKO_CDINT_PBX) { + UWORD pbx = readw(AKIKO_CDPBX); + writew(0, AKIKO_CDPBX); + if (pbx < 0x000f) + Signal(cu->cu_Task, SIGF_SINGLE); + } + + return FALSE; + + AROS_INTFUNC_EXIT +} + +static UBYTE bcd2dec(UBYTE bcd) +{ + return (bcd & 0xf) + ((bcd >> 4) & 0xf) * 10; +} + +static VOID CD32_UpdateTOC(struct CD32Unit *cu) +{ + struct QCode *qc = &cu->cu_QCode; + + D(bug("%s: Index 0x%02x\n", __func__, qc->Index)); + switch (qc->Index) { + case 0xA0: + cu->cu_CDTOC[0].Summary.FirstTrack = bcd2dec(qc->DiskPosition.MSF.Minute); + break; + case 0xA1: + cu->cu_CDTOC[0].Summary.LastTrack = bcd2dec(qc->DiskPosition.MSF.Minute); + break; + case 0xA2: + cu->cu_CDTOC[0].Summary.LeadOut = qc->DiskPosition; + cu->cu_CDInfo.Status |= CDSTSF_TOC; + break; + default: + if (((qc->Index & 0xf) <= 9) && ((qc->Index & 0xf0) <= 0x90) && + (qc->Index != 0)) { + int track = bcd2dec(qc->Index); + cu->cu_CDTOC[track].Entry.CtlAdr = qc->CtlAdr; + cu->cu_CDTOC[track].Entry.Track = track; + cu->cu_CDTOC[track].Entry.Position = qc->DiskPosition; + if (track == 1 && qc->CtlAdr == 0x41) { + cu->cu_CDInfo.Status |= CDSTSF_CDROM; + } + } else { + D(bug("%s: Illegal track number 0x%02x ignored\n", __func__, qc->Index)); + } + break; + } +} + +static LONG CD32_Cmd(struct CD32Unit *cu, UBYTE *cmd, LONG cmd_len, UBYTE *resp, LONG resp_len) +{ + UBYTE csum, RxTail; + int i; + + cu->cu_Sequence++; + if (cu->cu_Sequence >= 16) + cu->cu_Sequence = 1; + + cmd[0] = (cmd[0] & 0xf) | (cu->cu_Sequence << 4); + + csum = 0; + for (i = 0; i < cmd_len; i++) { + /* NOTE: We are relying on the fact that cu_TxHead is a + * UBYTE, so that it wraps at 0x100 + */ + cu->cu_Misc->Command[cu->cu_TxHead++] = cmd[i]; + csum += cmd[i]; + } + + cu->cu_Misc->Command[cu->cu_TxHead++] = ~csum; + + /* Just wait for the RX to complete of the status */ + CD32_IntEnable(cu, AKIKO_CDINT_RXDMA | AKIKO_CDINT_TXDMA); + writel(AKIKO_CDFLAG_TXD | AKIKO_CDFLAG_RXD | AKIKO_CDFLAG_CAS | AKIKO_CDFLAG_PBX | AKIKO_CDFLAG_MSB, AKIKO_CDFLAG); + + /* Trigger the command by updating AKIKO_CDTXCMP */ + SetSignal(0, SIGF_SINGLE); + writeb((cu->cu_RxHead + 1) & 0xff, AKIKO_CDRXCMP); + writeb(cu->cu_TxHead, AKIKO_CDTXCMP); + + for (;;) { + UBYTE RxHead = cu->cu_RxHead; + + D(bug("%s: %02x Wait...\n", __func__, cmd[0])); + Wait(SIGF_SINGLE); + D(bug("%s: %02x Waited.\n", __func__, cmd[0])); + + RxTail = readb(AKIKO_CDRXINX); + D(bug("%s: RxHead=%02x RxTail=%02x\n", __func__, RxHead, RxTail)); + + if (cu->cu_Misc->Response[RxHead] == cmd[0]) + break; + + D(bug("%s: Found unexpected: 0x%02x (%d)\n", __func__, cu->cu_Misc->Response[RxHead], (RxTail + 256 - RxHead) & 0xff)); + + csum = 0; + while (RxHead != RxTail) { + /* NOTE: We are relying on the fact that cu_RxHead is a + * UBYTE, so that it wraps at 0x100 + */ + UBYTE val = cu->cu_Misc->Response[RxHead++]; + D(bug("%02x ", val)); + csum += val; + } + D(bug(" (%02x)\n", csum)); + + if (csum != 0xff) { + D(bug("%s: Checksum failed on RX\n", __func__)); + return CDERR_NotSpecified; + } + + RxHead = cu->cu_RxHead; + cu->cu_RxHead = RxTail; + + switch (cu->cu_Misc->Response[RxHead]) { + case CHCD_MEDIA: + RxHead++; + if (cu->cu_Misc->Response[RxHead] == 0x83) { + if (!(cu->cu_CDInfo.Status & CDSTSF_DISK)) { + cu->cu_ChangeNum++; + cu->cu_CDInfo.Status |= CDSTSF_CLOSED | CDSTSF_DISK | CDSTSF_SPIN; + } + } else { + if (cu->cu_CDInfo.Status & CDSTSF_DISK) { + cu->cu_ChangeNum++; + cu->cu_CDInfo.Status = 0; + } + } + break; + case CHCD_SUBQ: + RxHead++; + RxHead++; + RxHead++; + cu->cu_QCode.CtlAdr = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.Track = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.Index = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.Zero = 0; + cu->cu_QCode.TrackPosition.MSF.Reserved = 0; + cu->cu_QCode.TrackPosition.MSF.Minute = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.TrackPosition.MSF.Second = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.TrackPosition.MSF.Frame = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.DiskPosition.MSF.Reserved = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.DiskPosition.MSF.Minute = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.DiskPosition.MSF.Second = cu->cu_Misc->Response[RxHead++]; + cu->cu_QCode.DiskPosition.MSF.Frame = cu->cu_Misc->Response[RxHead++]; + if (!(cu->cu_CDInfo.Status & CDSTSF_TOC)) + CD32_UpdateTOC(cu); + + break; + + default: + D(bug("%s: Command mismatch: got 0x%02x, expected 0x%02x\n", __func__, cu->cu_Misc->Response[RxHead], cmd[0])); + return CDERR_InvalidState; + } + + writeb((RxTail + 1) & 0xff, AKIKO_CDRXCMP); + } + + D(bug("%s: Found expected: 0x%02x (%d)\n", __func__, cu->cu_Misc->Response[cu->cu_RxHead], (RxTail + 256 - cu->cu_RxHead) & 0xff)); + + csum = 0; + while (cu->cu_RxHead != RxTail) { + /* NOTE: We are relying on the fact that cu_RxHead is a + * UBYTE, so that it wraps at 0x100 + */ + UBYTE val = cu->cu_Misc->Response[cu->cu_RxHead++]; + D(bug("%02x ", val)); + if (resp_len > 0) { + *(resp++) = val; + resp_len--; + } + csum += val; + } + D(bug("(%02x)\n", csum)); + + if (csum != 0xff) { + D(bug("%s: Checksum failed on RX\n", __func__)); + return CDERR_NotSpecified; + } + + D(bug("%s: -- RXHead %2x --\n", __func__, cu->cu_RxHead)); + + return 0; +} + +static VOID CD32_Led(struct CD32Unit *cu, BOOL led_on) +{ + UBYTE cmd[2] = { CHCD_LED, (led_on ? 1 : 0) | 0x80 }; + UBYTE resp[2]; + + CD32_Cmd(cu, cmd, 2, resp, 2); +} + +static LONG CD32_CmdRead(struct CD32Unit *cu, LONG sect_start, LONG sectors, void (*copy_sector)(APTR sector, APTR priv), APTR priv) +{ + LONG err; + UBYTE cmd[12], resp[2]; + UBYTE cmd_pause[1] = { CHCD_PAUSE }; + UBYTE cmd_unpause[1] = { CHCD_UNPAUSE }; + int i; + UWORD pbx; + + while (sectors > 0) { + cu->cu_CDInfo.Status &= ~(CDSTSF_PLAYING | CDSTSF_PAUSED | CDSTSF_SEARCH | CDSTSF_DIRECTION); + + cmd[0] = CHCD_MULTI; + sec2msf(sect_start, &cmd[1]); + sec2msf(sect_start + 16, &cmd[4]); + cmd[7] = 0x80; /* Data read */ + cmd[8] = (cu->cu_CDInfo.PlaySpeed >= 150) ? 0x40 : 0x00; + cmd[8] = 0x00; + cmd[9] = 0x00; + cmd[10] = 0x04; /* 0x04 to enable the motor */ + cmd[11] = 0x00; + + writew(0xffff, AKIKO_CDPBX); + + /* Start the read */ + CD32_Led(cu, TRUE); + CD32_Cmd(cu, cmd_unpause, 1, resp, sizeof(resp)); + err = CD32_Cmd(cu, cmd, sizeof(cmd), resp, sizeof(resp)); + if (err) + return err; + + if ((resp[1] & 2) != 2) + return CDERR_SeekError; + + + /* Wait for (most) sectors to come in */ + CD32_IntEnable(cu, AKIKO_CDINT_PBX); + + writel(readl(AKIKO_CDFLAG) | AKIKO_CDFLAG_ENABLE, AKIKO_CDFLAG); + Wait(SIGF_SINGLE); + + CD32_Cmd(cu, cmd_pause, 1, resp, sizeof(resp)); + CD32_Led(cu, FALSE); + pbx = readw(AKIKO_CDPBX); + + if (pbx == 0) { + D(bug("%s: Overflow during read\n", __func__)); + break; + } + + for (i = 15; i >= 0; i--) { + if (!(pbx & (1 << i))) { + D(bug("%s: SECTOR: %d\n", __func__, *(ULONG *)(&cu->cu_Data[i]))); + copy_sector(&cu->cu_Data[i], priv); + sectors--; + sect_start++; + if (sectors <= 0) + return 0; + } + } + + } + + return -1; +} + +static UBYTE CD32_Status(struct CD32Unit *cu) +{ + UBYTE cmd[1], res[21]; + + cu->cu_Task = FindTask(NULL); + D(bug("%s: %p\n", __func__, cu)); + cmd[0] = CHCD_STATUS; + res[1] = 0x80; + CD32_Cmd(cu, cmd, 1, res, 20); + res[20] = 0; + D(bug("%s: Drive \"%s\", state 0x%02x\n", __func__, &res[2], res[1])); + + if (res[1] & CHERR_BADCOMMAND) { + if (cu->cu_CDInfo.Status & CDSTSF_DISK) { + cu->cu_ChangeNum++; + cu->cu_CDInfo.Status = 0; + } + } else if (res[1] & CHERR_DISKPRESENT) { + if (!(cu->cu_CDInfo.Status & CDSTSF_DISK)) { + cu->cu_ChangeNum++; + cu->cu_CDInfo.Status |= CDSTSF_CLOSED | CDSTSF_DISK | CDSTSF_SPIN; + } + } + + D(bug("%s: %p = 0x%08x\n", __func__, cu, cu->cu_CDInfo.Status)); + return cu->cu_CDInfo.Status; +} + +static VOID CD32_ReadTOC(struct CD32Unit *cu) +{ + UBYTE toc_cmd[12] = { CHCD_MULTI, 0, 0, 0, 0, 0, 0, 0x03, 0x40, 0, 0x00, 0 }; + UBYTE idle_cmd[1] = { CHCD_UNPAUSE }; + UBYTE resp[2]; + ULONG timeout = 5 * 1000; /* 5 second timeout */ + BOOL blink = TRUE; + + /* Start the read TOC command */ + if (CD32_Cmd(cu, toc_cmd, 12, resp, 2) != 0) + return; + + /* Until the TOC is read, blink the LED and unpause */ + D(bug("%s: Waiting for TOC for up to 10 seconds\n", __func__)); + while (!(cu->cu_CDInfo.Status & CDSTSF_TOC) && timeout > 0) { + CD32_Led(cu, blink); + blink = !blink; + cdDelayMS(cu->cu_CDBase, 100); /* Delay 100ms */ + CD32_Cmd(cu, idle_cmd, 1, resp, 2); + timeout -= 100; + } + + if (cu->cu_CDInfo.Status & CDSTSF_CDROM) { + if (cu->cu_CDTOC[0].Summary.LastTrack == 1) { + cu->cu_TotalSectors = msf2sec(&cu->cu_CDTOC[0].Summary.LeadOut.MSF) - + msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF); + } else { + cu->cu_TotalSectors = msf2sec(&cu->cu_CDTOC[2].Entry.Position.MSF) - + msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF); + } + } else { + cu->cu_TotalSectors = 0; + } + + D(bug("%s: TotalSectors = %d\n", __func__, cu->cu_TotalSectors)); +} + +static LONG CD32_IsCDROM(struct CD32Unit *cu) +{ + if ((cu->cu_CDInfo.Status & CDSTSF_DISK) == 0) { + if ((CD32_Status(cu) & CDSTSF_DISK) == 0) + return CDERR_NoDisk; + } + + if ((cu->cu_CDInfo.Status & CDSTSF_TOC) == 0) { + CD32_ReadTOC(cu); + } + + if ((cu->cu_CDInfo.Status & CDSTSF_CDROM) == 0) { + return CDERR_SeekError; + } + + return 0; +} + +struct CopyREAD { + struct IOStdReq *io; + LONG offset; + UBYTE *buffer; + LONG length; +}; + +static VOID CD32_CopyREAD(APTR frame, APTR priv) +{ + struct CD32Mode1 *mode1 = frame; + struct CopyREAD *cr = priv; + LONG tocopy; + + tocopy = 2048 - cr->offset; + if (tocopy > cr->length) + tocopy = cr->length; + + D(bug("%s: Copy from 0x%08x to 0x%08x, %d\n", __func__, &mode1->cs_Data[cr->offset], cr->buffer, tocopy)); + CopyMem(&mode1->cs_Data[cr->offset], cr->buffer, tocopy); + if (0) { int i ; + for (i = 0; i < tocopy; i++) { + int mod = i % 16; + if (mod == 0) D(bug("%08x:", cr->buffer - (UBYTE *)cr->io->io_Data + i)); + D(bug("%c%02x", (mod==8) ? '-' : ' ', cr->buffer[i])); + if (mod == 15) D(bug("\n")); + } + D(bug("\n")); + } + + cr->offset = 0; + cr->length -= tocopy; + cr->buffer += tocopy; + cr->io->io_Actual += tocopy; +} + + +static LONG CD32_DoIO(struct IOStdReq *io, APTR priv) +{ + struct CD32Unit *cu = priv; + LONG err = CDERR_NOCMD; + UBYTE cmd[16], res[16]; + LONG sect_start, sect_end, origin; + struct CopyREAD cr; + + D(bug("%s:%p io_Command=%d\n", __func__, io, io->io_Command)); + + cu->cu_Task = FindTask(NULL); + + switch (io->io_Command) { + case CD_CHANGENUM: + io->io_Actual = cu->cu_ChangeNum; + err = 0; + break; + case CD_CHANGESTATE: + io->io_Actual = (cu->cu_CDInfo.Status & CDSTSF_TOC) ? 0 : 1; + D(bug("CD_CHANGESTATE: %d\n", io->io_Actual)); + err = 0; + break; + case CD_CONFIG: + D(bug("CD_CONFIG: Data %p, Length %d\n", io->io_Data, io->io_Length)); + if (io->io_Length != 0) { + err = CDERR_BADLENGTH; + break; + } + /* Gah. Some bright spark decided to use tags with + * the same values as TAG_IGNORE, TAG_MORE, and TAG_SKIP, + * so we have to process the TagItem array manually. + */ + err = 0; + if (io->io_Data != NULL) { + struct TagItem *ti = io->io_Data; + BOOL done = FALSE; + while (!done) { + switch (ti->ti_Tag) { + case TAGCD_PLAYSPEED: + if (((ti->ti_Data % 75) != 0) || + (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) { + err = CDERR_InvalidState; + done = TRUE; + break; + } + cu->cu_CDInfo.PlaySpeed = ti->ti_Data; + break; + case TAGCD_READSPEED: + if (((ti->ti_Data % 75) != 0) || + (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) { + err = CDERR_InvalidState; + done = TRUE; + break; + } + cu->cu_CDInfo.ReadSpeed = ti->ti_Data; + break; + + case TAGCD_READXLSPEED: + if (((ti->ti_Data % 75) != 0) || + (ti->ti_Data > cu->cu_CDInfo.MaxSpeed)) { + err = CDERR_InvalidState; + done = TRUE; + break; + } + cu->cu_CDInfo.ReadXLSpeed = ti->ti_Data; + break; + + case TAGCD_SECTORSIZE: + if (ti->ti_Data != 2048 && + ti->ti_Data != 2328) { + err = CDERR_InvalidState; + done = TRUE; + break; + } + cu->cu_CDInfo.SectorSize = ti->ti_Data; + break; + + case TAGCD_XLECC: + cu->cu_CDInfo.XLECC = ti->ti_Data ? 1 : 0; + break; + case TAGCD_EJECTRESET: + cu->cu_CDInfo.EjectReset = ti->ti_Data ? 1 : 0; + break; + case TAGCD_DONE: + done = TRUE; + break; + default: + break; + } + ti++; + D(bug("CD_CONFIG: Tag %d, Item %d, err %d, done %d\n", ti->ti_Tag, ti->ti_Data, err, done)); + } + } + break; + case CD_TOCMSF: + D(bug("CD_TOCMSF: %d\n", io->io_Length)); + err = CD32_IsCDROM(cu); + D(bug("CD_TOCMSF: check %d\n", err)); + if (io->io_Data != NULL && (cu->cu_CDInfo.Status & CDSTSF_TOC)) { + ULONG i, actual = 0; + ULONG track = io->io_Offset; + APTR buff = io->io_Data; + for (i = 0; i < io->io_Length && track <= cu->cu_CDTOC[0].Summary.LastTrack; i++, track++) { + CopyMem(&cu->cu_CDTOC[track], buff, sizeof(union CDTOC)); + actual++; + buff+=sizeof(union CDTOC); + } + io->io_Actual = actual; + err = 0; + } + D(bug("CD_TOCMSF: err %d\n", err)); + break; + case CD_EJECT: + io->io_Actual = 0; + err = 0; + break; + case CD_GETGEOMETRY: + err = CD32_IsCDROM(cu); + if (err) { + D(bug("CD_GETGEOMETRY: Not a data disk\n")); + break; + } + + if (io->io_Length >= sizeof(struct DriveGeometry)) { + struct DriveGeometry *dg = (struct DriveGeometry *)io->io_Data; + dg->dg_SectorSize = cu->cu_CDInfo.SectorSize; + dg->dg_TotalSectors = cu->cu_TotalSectors; + dg->dg_Cylinders = cu->cu_TotalSectors; + dg->dg_CylSectors = 1; + dg->dg_Heads = 1; + dg->dg_TrackSectors = 1; + dg->dg_BufMemType = MEMF_PUBLIC; + dg->dg_DeviceType = DG_CDROM; + dg->dg_Flags = DGF_REMOVABLE; + dg->dg_Reserved = 0; + io->io_Actual = sizeof(*dg); + err = 0; + } else { + err = CDERR_BADLENGTH; + } + break; + case CD_INFO: + if (io->io_Length >= sizeof(struct CDInfo)) { + CD32_IsCDROM(cu); + CopyMem(&cu->cu_CDInfo, io->io_Data, sizeof(struct CDInfo)); + io->io_Actual = sizeof(struct CDInfo); + err = 0; + } + break; + case CD_MOTOR: + /* FIXME: Should this be a pause/unpause command? + * Or just ignored? + */ + io->io_Actual = 1; + err = 0; + break; + case CD_PLAYTRACK: + if (io->io_Offset <= cu->cu_CDTOC[0].Summary.LastTrack) { + ULONG last = io->io_Offset + io->io_Length; + UBYTE cmd[12], res[2]; + cmd[0] = CHCD_MULTI; + cmd[1] = cu->cu_CDTOC[io->io_Offset].Entry.Position.MSF.Minute; + cmd[2] = cu->cu_CDTOC[io->io_Offset].Entry.Position.MSF.Second; + cmd[3] = cu->cu_CDTOC[io->io_Offset].Entry.Position.MSF.Frame; + if (last > cu->cu_CDTOC[0].Summary.LastTrack) { + cmd[4] = cu->cu_CDTOC[0].Summary.LeadOut.MSF.Minute; + cmd[5] = cu->cu_CDTOC[0].Summary.LeadOut.MSF.Minute; + cmd[6] = cu->cu_CDTOC[0].Summary.LeadOut.MSF.Minute; + } else { + cmd[4] = cu->cu_CDTOC[last].Entry.Position.MSF.Minute; + cmd[5] = cu->cu_CDTOC[last].Entry.Position.MSF.Second; + cmd[6] = cu->cu_CDTOC[last].Entry.Position.MSF.Frame; + } + cmd[7] = 0x00; + cmd[8] = (cu->cu_CDInfo.ReadSpeed >= 150) ? 0x40 : 0x00; + cmd[9] = 0x00; + cmd[10] = 0x04; + cmd[11] = 0x00; + err = CD32_Cmd(cu, cmd, 12, res, 2); + D(bug("CD_PLAYTRACK: err=%d, res[1]=0x%02x\n", err, res[1])); + if (!err && + (res[1] & 0x80) == 0 && + (res[1] & 0x08)) { + cu->cu_CDInfo.Status &= ~(CDSTSF_PLAYING | CDSTSF_PAUSED | CDSTSF_SEARCH | CDSTSF_DIRECTION); + cu->cu_CDInfo.Status |= CDSTSF_PLAYING; + D(bug("CD_PLAYTRACK: Playing tracks %d-%d\n", io->io_Offset, last-1)); + err = 0; + } + } + break; + case CD_READ: + err = CD32_IsCDROM(cu); + if (err) + break; + + /* Convert offset and length to MSF */ + CD32_Led(cu, TRUE); + + io->io_Error = 0; + io->io_Actual = 0; + + origin = msf2sec(&cu->cu_CDTOC[1].Entry.Position.MSF); + sect_start = io->io_Offset / cu->cu_CDInfo.SectorSize; + sect_end = (io->io_Offset + io->io_Length + cu->cu_CDInfo.SectorSize - 1) / cu->cu_CDInfo.SectorSize; + + cr.io = io; + cr.buffer = io->io_Data; + cr.offset = io->io_Offset % cu->cu_CDInfo.SectorSize; + cr.length = io->io_Length; + + err = CD32_CmdRead(cu, sect_start + origin, sect_end - sect_start, CD32_CopyREAD, &cr); + break; + case CD_RESET: + cmd[0] = CHCD_RESET; + err = CD32_Cmd(cu, cmd, 1, res, 1); + break; + default: + break; + } + + D(bug("%s:%p err=%d (Actual %d)\n", __func__, io, err, io->io_Actual)); + return err; +} + +static VOID CD32_Expunge(APTR priv) +{ + struct CD32Unit *cu = priv; + RemIntServer(INTB_PORTS, &cu->cu_Interrupt); + FreeMem(cu->cu_Misc, sizeof(struct CD32Misc)); + FreeMem(cu->cu_Data, sizeof(struct CD32Data) * 16); + FreeVec(cu); +} + +static const struct cdUnitOps CD32Ops = { + .uo_Name = "CD32 (Akiko)", + .uo_Expunge = CD32_Expunge, + .uo_DoIO = CD32_DoIO, +}; + +static const struct DosEnvec CD32Envec = { + .de_TableSize = DE_MASK, + .de_SizeBlock = 2048 >> 2, + .de_Surfaces = 1, + .de_SectorPerBlock = 1, + .de_BlocksPerTrack = 1, + .de_Reserved = 0, + .de_PreAlloc = 0, + .de_Interleave = 0, + .de_LowCyl = 0, + .de_HighCyl = 0, + .de_NumBuffers = 32, + .de_BufMemType = MEMF_24BITDMA, + .de_MaxTransfer = 32 * 2048, + .de_Mask = 0x00fffffe, +}; + + +static int CD32_InitLib(LIBBASETYPE *cb) +{ + LONG unit; + struct CD32Unit *priv; + + if (readw(AKIKO_ID) != AKIKO_ID_MAGIC) { + D(bug("%s: No Akiko detected\n", __func__)); + return 1; + } + + /* Reset the Akiko CD32 interface */ + writeb(readb(AKIKO_CDRESET) | 0x80, AKIKO_CDRESET); + writeb(readb(AKIKO_CDRESET) & ~0x80, AKIKO_CDRESET); + + /* Disable the CD32 interface for the moment */ + priv = AllocVec(sizeof(*priv), MEMF_ANY | MEMF_CLEAR); + if (priv) { + priv->cu_CDBase = cb; + priv->cu_Misc = LibAllocAligned(sizeof(struct CD32Misc), MEMF_24BITDMA | MEMF_CLEAR, 1024); + if (priv->cu_Misc) { + priv->cu_Data = LibAllocAligned(sizeof(struct CD32Data) * 16, MEMF_24BITDMA | MEMF_CLEAR, 4096); + if (priv->cu_Data) { + priv->cu_Interrupt.is_Node.ln_Pri = 0; + priv->cu_Interrupt.is_Node.ln_Name = (STRPTR)CD32Ops.uo_Name; + priv->cu_Interrupt.is_Node.ln_Type = NT_INTERRUPT; + priv->cu_Interrupt.is_Data = priv; + priv->cu_Interrupt.is_Code = (VOID_FUNC)CD32_Interrupt; + AddIntServer(INTB_PORTS, &priv->cu_Interrupt); + + writel((IPTR)priv->cu_Data, AKIKO_CDADRDATA); + writel((IPTR)priv->cu_Misc, AKIKO_CDADRMISC); + priv->cu_TxHead = readb(AKIKO_CDTXINX); + priv->cu_RxHead = readb(AKIKO_CDRXINX); + writeb(priv->cu_TxHead, AKIKO_CDTXCMP); + writeb(priv->cu_RxHead, AKIKO_CDRXCMP); + + priv->cu_CDInfo.SectorSize = 2048; + priv->cu_CDInfo.MaxSpeed = 150; + priv->cu_CDInfo.PlaySpeed = 75; + priv->cu_CDInfo.ReadSpeed = 150; + priv->cu_CDInfo.ReadXLSpeed = 150; + CD32_IsCDROM(priv); + + unit = cdAddUnit(cb, &CD32Ops, priv, &CD32Envec); + if (unit >= 0) { + D(bug("%s: Akiko as CD Unit %d\n", __func__, unit)); + return 1; + } + + RemIntServer(INTB_PORTS, &priv->cu_Interrupt); + + FreeMem(priv->cu_Data, sizeof(struct CD32Data) * 16); + } + FreeMem(priv->cu_Misc, sizeof(struct CD32Misc)); + } + FreeVec(priv); + } + + return 0; +} + +static int CD32_ExpungeLib(LIBBASETYPE *cb) +{ + /* Nothing to do. */ + return 0; +} + + +ADD2INITLIB(CD32_InitLib, 32); +ADD2EXPUNGELIB(CD32_ExpungeLib, 32); diff --git a/arch/m68k-amiga/devs/cd/cd32.h b/arch/m68k-amiga/devs/cd/cd32.h new file mode 100644 index 0000000000..0cf1159b9a --- /dev/null +++ b/arch/m68k-amiga/devs/cd/cd32.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013, The AROS Development Team + * All right reserved. + * Author: Jason S. McMullan + * + * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 + */ + +#ifndef CD32_H +#define CD32_H + +/* CD32 Akiko registers */ + +#define AKIKO_ID 0xb80002 /* u16 */ +#define AKIKO_ID_MAGIC 0xCAFE + +#define AKIKO_CDINTREQ 0xb80004 /* u32 */ +#define AKIKO_CDINTENA 0xb80008 /* u32 */ +#define AKIKO_CDINT_SUBCODE (1 << 31) +#define AKIKO_CDINT_DRIVEXMIT (1 << 30) +#define AKIKO_CDINT_DRIVERECV (1 << 29) +#define AKIKO_CDINT_RXDMA (1 << 28) +#define AKIKO_CDINT_TXDMA (1 << 27) +#define AKIKO_CDINT_PBX (1 << 26) +#define AKIKO_CDINT_OVERFLOW (1 << 25) + +#define AKIKO_CDADRDATA 0xb80010 /* u32 */ + /* Address must be on a 4K boundary, in the first 16M of address + * Sector data is aligned on 4K boundaries, regardless of + * the actual CD sector size. + * Address +0x000..0xbff Sector DMA index (u32), followed by sector data + * +0xc00..0xfff Sector error status + */ +#define AKIKO_CDADRMISC 0xb80014 /* u32 Address for misc data */ + /* Address must be on a 1K boundary, in the first 16M of address + * Address +0x000..0x0ff is Result buffer + * +0x100..0x1ff is Subcode data + * +0x200..0x2ff is Command buffer + */ +#define AKIKO_CDSUBINX 0xb80018 /* u8 Bytes of subcode recieved */ +#define AKIKO_CDTXINX 0xb80019 /* u8 Bytes of Command processed */ +#define AKIKO_CDRXINX 0xb8001a /* u8 Bytes of Result processed */ +#define AKIKO_CDTXCMP 0xb8001d /* u8 Bytes of Command expected */ +#define AKIKO_CDRXCMP 0xb8001f /* u8 Bytes of Result expected */ +#define AKIKO_CDPBX 0xb80020 /* u16 Sector mask */ + +#define AKIKO_CDFLAG 0xb80024 /* u32 */ +#define AKIKO_CDFLAG_SUBCODE (1 << 31) +#define AKIKO_CDFLAG_TXD (1 << 30) +#define AKIKO_CDFLAG_RXD (1 << 29) +#define AKIKO_CDFLAG_CAS (1 << 28) +#define AKIKO_CDFLAG_PBX (1 << 27) +#define AKIKO_CDFLAG_ENABLE (1 << 26) +#define AKIKO_CDFLAG_RAW (1 << 25) +#define AKIKO_CDFLAG_MSB (1 << 24) +#define AKIKO_CDRESET 0xb80025 /* u8 0x80 = reset, 0x00 = normal */ + +#define AKIKO_NVRAM 0xb80030 /* u32 */ +#define AKIKO_C2P 0xb80038 /* u32 */ + + +#endif /* CD32_H */ diff --git a/arch/m68k-amiga/devs/cd/cd_intern.h b/arch/m68k-amiga/devs/cd/cd_intern.h new file mode 100644 index 0000000000..0343bc294e --- /dev/null +++ b/arch/m68k-amiga/devs/cd/cd_intern.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013, The AROS Development Team + * All right reserved. + * Author: Jason S. McMullan + * + * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 + */ + +#ifndef CD_INTERN_H +#define CD_INTERN_H + +#include +#include +#include + +#include + +#include + +#include LC_LIBDEFS_FILE + +struct cdBase { + struct Device cb_Device; + struct List cb_Units; + struct SignalSemaphore cb_UnitsLock; + ULONG cb_MaxUnit; + + struct MsgPort cb_TimerPort; + struct timerequest cb_TimerRequest; +}; + +struct cdUnitOps { + CONST_STRPTR uo_Name; + LONG (*uo_DoIO)(struct IOStdReq *io, APTR priv); + VOID (*uo_Expunge)(APTR priv); +}; + +LONG cdAddUnit(LIBBASETYPE *cb, const struct cdUnitOps *ops, APTR priv, const struct DosEnvec *de); +VOID cdDelayMS(LIBBASETYPE *cb, ULONG timeout_ms); + +#define IOF_ABORT (1 << 7) + +#endif /* CD_INTERN_H */ diff --git a/arch/m68k-amiga/devs/cd/chinon.h b/arch/m68k-amiga/devs/cd/chinon.h new file mode 100644 index 0000000000..781b59008b --- /dev/null +++ b/arch/m68k-amiga/devs/cd/chinon.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013, The AROS Development Team + * All right reserved. + * Author: Jason S. McMullan + * + * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 + */ + +#ifndef CHINON_H +#define CHINON_H + +#include + +/* Chinon O-658-2 24 CD Commands + * 'msf' is [minutes seconds frame] + * ^- These are in BCD --^ + */ + +#define CHCD_RESET 0 /* Cmd = [ 0x00 ] + Result = [ 0x00 ] */ +#define CHCD_STOP 1 /* Cmd = [ 0x01 0x00 ] + Result = [ 0x01 errcode ] */ +#define CHCD_PAUSE 2 /* Cmd = [ 0x02 ] + Result = [ 0x02 errcode ] */ +#define CHCD_UNPAUSE 3 /* Cmd = [ 0x03 ] + Result = [ 0x03 errcode ] */ +#define CHCD_MULTI 4 /* Cmd = [ 0x04 start_msf end_msf subcmd flags ?? audio ?? ] + subcmd: 0x80 - Data read + 0x03 - TOC read + flags: 0x40 - Speed (0 = x1, 0x40 = x2) + audio: 0x04 - Play audio + Result = [ 0x04 0x01 ] // No Disk + [ 0x04 0x02 ] // Read in progress + [ 0x04 0x08 ] // Audio playing + [ 0x04 0x80 ] // Error + [ 0x04 0x00 ] // IO complete + */ +#define CHCD_LED 5 /* Cmd = [ 0x05 led ] + led: 0x80 - Result wanted + 0x01 - LED on/off + Result = [ 0x05 led ] (if led & 0x80) + */ +#define CHCD_SUBQ 6 /* Cmd = [ 0x06 ] + Result = [ 0x06 0x00 0x00 CtlAdr Track Index TrackPos_u24 DiskPos_u32 CurrPos_msf ] */ +#define CHCD_STATUS 7 /* Cmd = [ 0x07 ] + Result = [ 0x07 code FirmwareString[18] ] + code: 0x01 - Door closed, disk present + 0x80 - Door open + */ +#define CHCD_CMD8 8 /* Cmd = [ 0x08 ?? ?? ?? ] + Result = [ 0x08 ?? ?? ?? ] + */ +#define CHCD_CMD9 9 /* Cmd = [ 0x09 ] + Result = [ 0x09 ?? ?? ?? ] + */ +/* Received when there is a media change */ +#define CHCD_MEDIA 10 /* Result = [ 0x0a status ] + * status: 0x83 - Disk present + * 0x80 - Disk absent + */ + +#define CHERR_OK 0x00 +#define CHERR_DISKPRESENT 0x01 +#define CHERR_READING 0x02 +#define CHERR_PLAYING 0x08 +#define CHERR_BADCOMMAND 0x80 +#define CHERR_CHECKSUM 0x88 +#define CHERR_DRAWERSTUCK 0x90 +#define CHERR_DISKUNREADABLE 0x98 +#define CHERR_INVALIDADDRESS 0xa0 +#define CHERR_WRONGDATA 0xa8 +#define CHERR_FOCUS 0xc8 +#define CHERR_SPINDLE 0xd0 +#define CHERR_TRACKING 0xd8 +#define CHERR_SLED 0xe0 +#define CHERR_TRACKJUMP 0xe8 +#define CHERR_ABNORMALSEEK 0xf0 +#define CHERR_NODISK 0xf8 + +struct chcd_msf { + BYTE minute; /* In BCD (up to 99) */ + BYTE second; /* In BCD (up to 59) */ + BYTE frame; /* In BCD (up to 74) */ +}; + +/* NOTE: There is a checksum following all commands, + * regardless of length. The checksum is the inverse + * of the sum of all the bytes of the command. + */ +struct chcd_cmd { + BYTE cmd; + union { + struct {} noop; + struct { + BYTE resv; + } stop; + struct {} pause; + struct {} unpause; + struct { + struct chcd_msf start; + struct chcd_msf end; + BYTE mode; /* 0x80 for data read */ + BYTE flags; /* 0x40 for 150 frames/sec, 0x00 for 75 frames/sec */ + BYTE resv_1; + BYTE audio; /* 0x04 to play audio */ + BYTE resv_2; + } multi; + struct { + BYTE state; /* 0x01 to turn on, 0x00 to turn off */ + /* 0x80 to force a result code */ + } led; + struct { } subq; + struct { } status; + }; +}; + + +/* NOTE: There is a checksum following all results, + * regardless of length. The checksum is the inverse + * of the sum of all the bytes of the result. + */ +struct chcd_result { + BYTE cmd; + BYTE error; + union { + struct {} noop; + struct {} stop; + struct {} pause; + struct {} unpause; + struct {} multi; + struct {} led; + struct { + BYTE resv; + BYTE ctladr; + BYTE track; + BYTE index; + struct chcd_msf track_position; + BYTE resv_2; + struct chcd_msf disk_position; + BYTE resv_3; + BYTE resv_4; + } subq; + struct { + BYTE firmware[18]; + } status; + }; +}; + +#endif /* CHINON_H */ diff --git a/arch/m68k-amiga/devs/cd/mmakefile.src b/arch/m68k-amiga/devs/cd/mmakefile.src new file mode 100644 index 0000000000..07942efcd7 --- /dev/null +++ b/arch/m68k-amiga/devs/cd/mmakefile.src @@ -0,0 +1,18 @@ +# +# Copyright (C) 2013, The AROS Development Team +# All right reserved. +# Author: Jason S. McMullan +# +# Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 +# +include $(TOP)/config/make.cfg + +FILES := cd cd32 + +#USER_CFLAGS += -DDEBUG=1 + +%build_module mmake=kernel-amiga-m68k-cd \ + modname=cd modtype=device \ + files="$(FILES)" + +%common diff --git a/compiler/include/devices/cd.h b/compiler/include/devices/cd.h index 7f387931bb..a20cad4853 100644 --- a/compiler/include/devices/cd.h +++ b/compiler/include/devices/cd.h @@ -11,6 +11,13 @@ /* CD device error codes */ +#ifndef EXEC_IO_H +#include +#endif +#ifndef DEVICES_TRACKDISK_H +#include +#endif + #define CDERR_OPENFAIL (-1) #define CDERR_ABORTED (-2) #define CDERR_NOCMD (-3) @@ -41,4 +48,182 @@ #define CDERR_Phase 42 #define CDERR_NoBoard 50 +#define CD_RESET CMD_RESET +#define CD_READ CMD_READ +#define CD_WRITE CMD_WRITE +#define CD_UPDATE CMD_UPDATE +#define CD_CLEAR CMD_CLEAR +#define CD_STOP CMD_STOP +#define CD_START CMD_START +#define CD_FLUSH CMD_FLUSH +#define CD_MOTOR TD_MOTOR +#define CD_SEEK TD_SEEK +#define CD_FORMAT TD_FORMAT +#define CD_REMOVE TD_REMOVE +#define CD_CHANGENUM TD_CHANGENUM +#define CD_CHANGESTATE TD_CHANGESTATE +#define CD_PROTSTATUS TD_PROTSTATUS +#define CD_GETDRIVETYPE TD_GETDRIVETYPE +#define CD_GETNUMTRACKS TD_GETNUMTRACKS +#define CD_ADDCHANGEINT TD_ADDCHANGEINT +#define CD_REMCHANGEINT TD_REMCHANGEINT +#define CD_GETGEOMETRY TD_GETGEOMETRY +#define CD_EJECT TD_EJECT + +#define CD_INFO 32 /* io_Data - Pointer to struct CDInfo + * io_Length - sizeof(struct CDInfo) + */ + +struct CDInfo { + UWORD PlaySpeed; /* In frames/second (75 = x1, 150 = x2, etc) */ + UWORD ReadSpeed; /* In frames/second */ + UWORD ReadXLSpeed; /* In frames/second */ + UWORD SectorSize; /* 2048 or 2328 */ + UWORD XLECC; /* 1 = CDXL ECC enabled, 0 = CDXL ECC disabled */ + UWORD EjectReset; /* 1 = Reset on eject */ + UWORD Reserved1[4]; + UWORD MaxSpeed; /* Maximum frames/second for the drive */ + UWORD AudioPrecision; /* 0 = no atten., 1 = mute only, N = levels */ + UWORD Status; /* See CDSTSB_* flags */ + UWORD Reserved2[4]; +}; + +#define CDSTSB_CLOSED 0 /* Drive closed */ +#define CDSTSB_DISK 1 /* Disk present */ +#define CDSTSB_SPIN 2 /* Disk spinning */ +#define CDSTSB_TOC 3 /* TOC read */ +#define CDSTSB_CDROM 4 /* Track 1 is data Mode 1 */ +#define CDSTSB_PLAYING 5 /* Playing audio track */ +#define CDSTSB_PAUSED 6 /* Playing paused */ +#define CDSTSB_SEARCH 7 /* Playing fast forward/fast reverse */ +#define CDSTSB_DIRECTION 8 /* Playing forward (0) or reverse (1) */ + +#define CDSTSF_CLOSED (1 << CDSTSB_CLOSED) +#define CDSTSF_DISK (1 << CDSTSB_DISK) +#define CDSTSF_SPIN (1 << CDSTSB_SPIN) +#define CDSTSF_TOC (1 << CDSTSB_TOC) +#define CDSTSF_CDROM (1 << CDSTSB_CDROM) +#define CDSTSF_PLAYING (1 << CDSTSB_PLAYING) +#define CDSTSF_PAUSED (1 << CDSTSB_PAUSED) +#define CDSTSF_SEARCH (1 << CDSTSB_SEARCH) +#define CDSTSF_DIRECTION (1 << CDSTSB_DIRECTION) + +#define CD_CONFIG 33 /* io_Data - Pointer to TagItem array + * io_Length - 0 + */ +/* NOTE: TAG_IGNORE, TAG_MORE, and TAG_SKIP are illegal in this command! */ +#define TAGCD_DONE 0 /* Last tag */ +#define TAGCD_PLAYSPEED 1 /* in frames/second */ +#define TAGCD_READSPEED 2 /* in frames/second */ +#define TAGCD_READXLSPEED 3 /* in frames/second */ +#define TAGCD_SECTORSIZE 4 /* 2048 or 2328 */ +#define TAGCD_XLECC 5 /* 0 or 1 */ +#define TAGCD_EJECTRESET 6 /* 0 or 1 */ + +#define CD_TOCMSF 34 +#define CD_TOCLSN 35 /* io_Data - union CDTOC array + * io_Length - # of CDTOC array entries. + * io_Offset - Starting entry (0 for summary) + */ + +union LSNMSF { + struct RMSF { + UBYTE Reserved; /* Always 0 */ + UBYTE Minute; /* Minutes (0..99) */ + UBYTE Second; /* Seconds (0..59) */ + UBYTE Frame; /* Frame (0..74) */ + } MSF; + ULONG LSN; /* Logical sector number */ +}; + +union CDTOC { + struct TOCSummary { /* Entry 0 */ + UBYTE FirstTrack; /* ID of first track (almost always 1) */ + UBYTE LastTrack; /* Last track on the disk */ + union LSNMSF LeadOut; /* Beginning of lead-out */ + } Summary; + struct TOCEntry { /* All other entries */ + UBYTE CtlAdr; /* Q-Code */ + UBYTE Track; /* Track Number */ + union LSNMSF Position; + } Entry; +}; + +#define CD_READXL 36 /* io_Data - Pointer to CDXL node's List + * io_Length - Maximum transfer length (or 0) + * io_Offset - Byte offset into Mode 1 Track 1 + */ + +#define CD_READXL_INTP(n, cdxl, data) \ + AROS_UFP3(VOID, n, \ + AROS_UFPA(struct CDXL *, cdxl, A2), \ + AROS_UFPA(APTR, data, A4), \ + AROS_UFPA(struct ExecBase *, __cxdl_SysBase, A6)) + +#define CD_READXL_INTC(n, cdxl, data) \ + AROS_UFC3(VOID, n, \ + AROS_UFCA(struct CDXL *, cdxl, A2), \ + AROS_UFCA(APTR, data, A4), \ + AROS_UFCA(struct ExecBase *, SysBase, A6)) + +#define CD_READXL_INTH(n, cdxl, data) \ + AROS_UFH3(VOID, n, \ + AROS_UFHA(struct CDXL *, cdxl, A2), \ + AROS_UFHA(APTR, data, A4), \ + AROS_UFHA(struct ExecBase *, SysBase, A6)) + +#define CD_READXL_INTFUNC_INIT AROS_USERFUNC_INIT +#define CD_READXL_INTFUNC_EXIT AROS_USERFUNC_EXIT + +struct CDXL { + struct MinNode Node; + BYTE *Buffer; + LONG Length; + LONG Actual; + APTR IntData; /* 'data' parameter */ + APTR IntCode; /* CD_READXL_INTH() function */ +}; + +#define CD_PLAYTRACK 37 /* io_Length - Number of track to play + * io_Offset - Starting track + */ +#define CD_PLAYMSF 38 /* io_Length - MSF length to play + * io_Offset - MSF start + */ +#define CD_PLAYLSN 39 /* io_Length - Frames to play + * io_Offset - Starting frame + */ +#define CD_PAUSE 40 /* io_Length - 0 = unpause, 1 = pause */ + +#define CD_SEARCH 41 /* io_Length - 0 (normal), 1 (FFW), 2 (FREV) */ + +/* Modes for CD_SEARCH's io_Length parameter: */ +#define CDMODE_NORMAL 0 /* Play normally */ +#define CDMODE_FFWD 1 /* Play fast forward */ +#define CDMODE_FREV 2 /* Play fast reverse */ + +#define CD_QCODEMSF 42 +#define CD_QCODELSN 43 /* io_Data - Pointer to struct QCode + * io_Length - sizeof(struct QCode) + */ +struct QCode { + UBYTE CtlAdr; + UBYTE Track; + UBYTE Index; + UBYTE Zero; + union LSNMSF TrackPosition; + union LSNMSF DiskPosition; +}; + +#define CD_ATTENUATE 44 /* io_Length - Duration of fade in frames + * io_Offset - Target volume (0 - 0x7FFF, or -1) + * -> io_Actual - Current volume + */ + +#define CD_ADDFRAMEINT 45 /* io_Data - struct Interrupt + * io_Length - sizeof(struct Interrupt) + */ +#define CD_REMFRAMEINT 46 + + #endif /* DEVICES_CD_H */ -- 2.11.4.GIT