/* * Copyright (c) 2010, Wei Mingzhi . * All Rights Reserved. * * Based on: Cdrom for Psemu Pro like Emulators * By: linuzappz * * Portions based on: cdrdao - write audio CD-Rs in disc-at-once mode * Copyright (C) 2007 Denis Leroy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #if defined (__linux__) && !defined (USE_LIBCDIO) #include "cdr.h" static int cdHandle = -1; static int ReadMMC = 0, SubQMMC = 0; static int SendMMCCmd(const unsigned char *cmd, int cmdLen, const unsigned char *dataOut, int dataOutLen, unsigned char *dataIn, int dataInLen) { sg_io_hdr_t io_hdr; memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cmdLen; io_hdr.cmdp = (unsigned char *)cmd; io_hdr.timeout = 10000; io_hdr.sbp = NULL; io_hdr.mx_sb_len = 0; io_hdr.flags = 1; if (dataOut != NULL) { io_hdr.dxferp = (void *)dataOut; io_hdr.dxfer_len = dataOutLen; io_hdr.dxfer_direction = SG_DXFER_TO_DEV; } else if (dataIn != NULL) { io_hdr.dxferp = (void *)dataIn; io_hdr.dxfer_len = dataInLen; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; } if (ioctl(cdHandle, SG_IO, &io_hdr) < 0) { return -1; } return io_hdr.status; } static int CheckReadMMC() { MMC_READ_CD cdb; unsigned char buf[CD_FRAMESIZE_RAW]; memset(&cdb, 0, sizeof(cdb)); memset(buf, 0xAA, sizeof(buf)); cdb.Code = GPCMD_READ_CD; cdb.IncludeEDC = 0; cdb.IncludeUserData = 1; cdb.HeaderCode = 3; cdb.IncludeSyncData = 1; cdb.TransferBlocks[2] = 1; if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) == 0) { if (buf[0] != 0xAA) { PRINTF("Using MMC for data\n"); return 1; // supported } } return 0; // NOT supported } static int CheckSubQMMC() { MMC_READ_CD cdb; unsigned char buf[CD_FRAMESIZE_RAW + 96]; memset(&cdb, 0, sizeof(cdb)); memset(buf, 0xAA, sizeof(buf)); cdb.Code = GPCMD_READ_CD; cdb.IncludeEDC = 1; cdb.IncludeUserData = 1; cdb.HeaderCode = 3; cdb.IncludeSyncData = 1; cdb.SubChannelSelection = 1; cdb.TransferBlocks[2] = 1; if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) == 0) { if (buf[0] != 0xAA && (buf[2352] != 0xAA || buf[2353] != 0xAA)) { PRINTF("Using MMC for subchannel\n"); return 1; // supported } } return 0; // NOT supported } int OpenCdHandle(const char *dev) { char spindown; cdHandle = open(dev, O_RDONLY); if (cdHandle != -1) { ioctl(cdHandle, CDROM_LOCKDOOR, 0); spindown = (char)SpinDown; ioctl(cdHandle, CDROMSETSPINDOWN, &spindown); ioctl(cdHandle, CDROM_SELECT_SPEED, CdrSpeed); ReadMMC = CheckReadMMC(); SubQMMC = CheckSubQMMC(); return 0; } return -1; } void CloseCdHandle() { char spindown = SPINDOWN_VENDOR_SPECIFIC; ioctl(cdHandle, CDROMSETSPINDOWN, &spindown); close(cdHandle); cdHandle = -1; } int IsCdHandleOpen() { return (cdHandle != -1); } long GetTN(unsigned char *buffer) { struct cdrom_tochdr toc; if (ioctl(cdHandle, CDROMREADTOCHDR, &toc) == -1) return -1; buffer[0] = toc.cdth_trk0; // start track buffer[1] = toc.cdth_trk1; // end track return 0; } long GetTD(unsigned char track, unsigned char *buffer) { struct cdrom_tocentry entry; if (track == 0) track = 0xAA; // total time (leadout) entry.cdte_track = track; entry.cdte_format = CDROM_MSF; if (ioctl(cdHandle, CDROMREADTOCENTRY, &entry) == -1) return -1; buffer[0] = entry.cdte_addr.msf.frame; buffer[1] = entry.cdte_addr.msf.second; buffer[2] = entry.cdte_addr.msf.minute; return 0; } long GetTE(unsigned char track, unsigned char *m, unsigned char *s, unsigned char *f) { struct cdrom_tocentry entry; unsigned char msf[3]; if (GetTN(msf) == -1) return -1; entry.cdte_track = track + 1; if (entry.cdte_track > msf[1]) entry.cdte_track = 0xaa; entry.cdte_format = CDROM_MSF; if (ioctl(cdHandle, CDROMREADTOCENTRY, &entry) == -1) return -1; lba_to_msf(msf_to_lba(entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame) - CD_MSF_OFFSET, msf); *m = msf[0]; *s = msf[1]; *f = msf[2]; return 0; } long ReadSector(crdata *cr) { if (ReadMMC) { MMC_READ_CD cdb; int lba; memset(&cdb, 0, sizeof(cdb)); lba = msf_to_lba(cr->msf.cdmsf_min0, cr->msf.cdmsf_sec0, cr->msf.cdmsf_frame0); cdb.Code = GPCMD_READ_CD; cdb.IncludeEDC = 1; cdb.IncludeUserData = 1; cdb.HeaderCode = 3; cdb.IncludeSyncData = 1; cdb.SubChannelSelection = 0; cdb.StartingLBA[1] = lba >> 16; cdb.StartingLBA[2] = lba >> 8; cdb.StartingLBA[3] = lba; cdb.TransferBlocks[2] = 1; if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, (unsigned char *)cr, sizeof(*cr)) != 0) return -1; } else { if (ioctl(cdHandle, CDROMREADRAW, cr) == -1) return -1; } return 0; } long PlayCDDA(unsigned char *sector) { struct cdrom_msf addr; unsigned char ptmp[4]; // 0 is the last track of every cdrom, so play up to there if (GetTD(0, ptmp) == -1) return -1; addr.cdmsf_min0 = sector[0]; addr.cdmsf_sec0 = sector[1]; addr.cdmsf_frame0 = sector[2]; addr.cdmsf_min1 = ptmp[2]; addr.cdmsf_sec1 = ptmp[1]; addr.cdmsf_frame1 = ptmp[0]; if (ioctl(cdHandle, CDROMPLAYMSF, &addr) == -1) return -1; return 0; } long StopCDDA() { struct cdrom_subchnl sc; sc.cdsc_format = CDROM_MSF; if (ioctl(cdHandle, CDROMSUBCHNL, &sc) == -1) return -1; switch (sc.cdsc_audiostatus) { case CDROM_AUDIO_PAUSED: case CDROM_AUDIO_PLAY: ioctl(cdHandle, CDROMSTOP); break; } return 0; } long GetStatus(int playing, struct CdrStat *stat) { struct cdrom_subchnl sc; int ret; char spindown; memset(stat, 0, sizeof(struct CdrStat)); if (playing) { // return Time only if playing sc.cdsc_format = CDROM_MSF; if (ioctl(cdHandle, CDROMSUBCHNL, &sc) != -1) memcpy(stat->Time, &sc.cdsc_absaddr.msf, 3); } ret = ioctl(cdHandle, CDROM_DISC_STATUS); switch (ret) { case CDS_AUDIO: stat->Type = 0x02; break; case CDS_DATA_1: case CDS_DATA_2: case CDS_XA_2_1: case CDS_XA_2_2: stat->Type = 0x01; break; } ret = ioctl(cdHandle, CDROM_DRIVE_STATUS); switch (ret) { case CDS_NO_DISC: case CDS_TRAY_OPEN: stat->Type = 0xff; stat->Status |= 0x10; break; default: spindown = (char)SpinDown; ioctl(cdHandle, CDROMSETSPINDOWN, &spindown); ioctl(cdHandle, CDROM_SELECT_SPEED, CdrSpeed); ioctl(cdHandle, CDROM_LOCKDOOR, 0); break; } switch (sc.cdsc_audiostatus) { case CDROM_AUDIO_PLAY: stat->Status |= 0x80; break; } return 0; } static unsigned char *ReadSubMMC(const unsigned char *time) { static unsigned char buf[CD_FRAMESIZE_RAW + 96]; int lba = msf_to_lba(btoi(time[0]), btoi(time[1]), btoi(time[2])); MMC_READ_CD cdb; memset(&cdb, 0, sizeof(cdb)); cdb.Code = GPCMD_READ_CD; cdb.IncludeEDC = 1; cdb.IncludeUserData = 1; cdb.HeaderCode = 3; cdb.IncludeSyncData = 1; cdb.StartingLBA[1] = lba >> 16; cdb.StartingLBA[2] = lba >> 8; cdb.StartingLBA[3] = lba; cdb.TransferBlocks[2] = 1; cdb.SubChannelSelection = 1; if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) != 0) return NULL; DecodeRawSubData(buf + CD_FRAMESIZE_RAW); return buf + CD_FRAMESIZE_RAW; } static unsigned char *ReadSubIOCTL(const unsigned char *time) { static struct SubQ subq; struct cdrom_subchnl subchnl; int r; crdata cr; unsigned short crc; cr.msf.cdmsf_min0 = btoi(time[0]); cr.msf.cdmsf_sec0 = btoi(time[1]); cr.msf.cdmsf_frame0 = btoi(time[2]); if (ioctl(cdHandle, CDROMSEEK, &cr.msf) == -1) { // will be slower, but there's no other way to make it accurate if (ioctl(cdHandle, CDROMREADRAW, &cr) == -1) { return NULL; } } subchnl.cdsc_format = CDROM_MSF; r = ioctl(cdHandle, CDROMSUBCHNL, &subchnl); if (r == -1) return NULL; subq.ControlAndADR = 0x41; subq.TrackNumber = subchnl.cdsc_trk; subq.IndexNumber = subchnl.cdsc_ind; subq.TrackRelativeAddress[0] = itob(subchnl.cdsc_reladdr.msf.minute); subq.TrackRelativeAddress[1] = itob(subchnl.cdsc_reladdr.msf.second); subq.TrackRelativeAddress[2] = itob(subchnl.cdsc_reladdr.msf.frame); subq.AbsoluteAddress[0] = itob(subchnl.cdsc_absaddr.msf.minute); subq.AbsoluteAddress[1] = itob(subchnl.cdsc_absaddr.msf.second); subq.AbsoluteAddress[2] = itob(subchnl.cdsc_absaddr.msf.frame); // CRC is not supported with IOCTL, fake it. crc = calcCrc((unsigned char *)&subq + 12, 10); subq.CRC[0] = (crc >> 8); subq.CRC[1] = (crc & 0xFF); r = msf_to_lba(btoi(time[0]), btoi(time[1]), btoi(time[2])); if (GetTE(1, &cr.msf.cdmsf_min0, &cr.msf.cdmsf_sec0, &cr.msf.cdmsf_frame0) == -1) { cr.msf.cdmsf_min0 = 80; cr.msf.cdmsf_sec0 = 0; cr.msf.cdmsf_frame0 = 0; } if (msf_to_lba(cr.msf.cdmsf_min0, cr.msf.cdmsf_sec0, cr.msf.cdmsf_frame0) >= r && (msf_to_lba(subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, subchnl.cdsc_absaddr.msf.frame) != r || msf_to_lba(subchnl.cdsc_reladdr.msf.minute, subchnl.cdsc_reladdr.msf.second, subchnl.cdsc_reladdr.msf.frame) != r - CD_MSF_OFFSET)) subq.CRC[1] ^= 1; // time mismatch; report wrong CRC PRINTF("subq : %x,%x : %x,%x,%x : %x,%x,%x\n", subchnl.cdsc_trk, subchnl.cdsc_ind, itob(subchnl.cdsc_reladdr.msf.minute), itob(subchnl.cdsc_reladdr.msf.second), itob(subchnl.cdsc_reladdr.msf.frame), itob(subchnl.cdsc_absaddr.msf.minute), itob(subchnl.cdsc_absaddr.msf.second), itob(subchnl.cdsc_absaddr.msf.frame)); return (unsigned char *)&subq; } unsigned char *ReadSub(const unsigned char *time) { if (SubQMMC) return ReadSubMMC(time); else return ReadSubIOCTL(time); } #endif