2 * Copyright (c) 2010, Wei Mingzhi <whistler@openoffice.org>.
5 * Based on: Cdrom for Psemu Pro like Emulators
6 * By: linuzappz <linuzappz@hotmail.com>
8 * Portions based on: cdrdao - write audio CD-Rs in disc-at-once mode
9 * Copyright (C) 2007 Denis Leroy <denis@poolshark.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <http://www.gnu.org/licenses>.
25 #if defined (__linux__) && !defined (USE_LIBCDIO)
29 static int cdHandle = -1;
30 static int ReadMMC = 0, SubQMMC = 0;
32 static int SendMMCCmd(const unsigned char *cmd, int cmdLen, const unsigned char *dataOut,
33 int dataOutLen, unsigned char *dataIn, int dataInLen)
37 memset(&io_hdr, 0, sizeof(io_hdr));
39 io_hdr.interface_id = 'S';
40 io_hdr.cmd_len = cmdLen;
41 io_hdr.cmdp = (unsigned char *)cmd;
42 io_hdr.timeout = 10000;
47 if (dataOut != NULL) {
48 io_hdr.dxferp = (void *)dataOut;
49 io_hdr.dxfer_len = dataOutLen;
50 io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
51 } else if (dataIn != NULL) {
52 io_hdr.dxferp = (void *)dataIn;
53 io_hdr.dxfer_len = dataInLen;
54 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
57 if (ioctl(cdHandle, SG_IO, &io_hdr) < 0) {
64 static int CheckReadMMC() {
66 unsigned char buf[CD_FRAMESIZE_RAW];
68 memset(&cdb, 0, sizeof(cdb));
69 memset(buf, 0xAA, sizeof(buf));
71 cdb.Code = GPCMD_READ_CD;
73 cdb.IncludeUserData = 1;
75 cdb.IncludeSyncData = 1;
76 cdb.TransferBlocks[2] = 1;
78 if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) == 0) {
80 PRINTF("Using MMC for data\n");
81 return 1; // supported
85 return 0; // NOT supported
88 static int CheckSubQMMC() {
90 unsigned char buf[CD_FRAMESIZE_RAW + 96];
92 memset(&cdb, 0, sizeof(cdb));
93 memset(buf, 0xAA, sizeof(buf));
95 cdb.Code = GPCMD_READ_CD;
97 cdb.IncludeUserData = 1;
99 cdb.IncludeSyncData = 1;
100 cdb.SubChannelSelection = 1;
101 cdb.TransferBlocks[2] = 1;
103 if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) == 0) {
104 if (buf[0] != 0xAA && (buf[2352] != 0xAA || buf[2353] != 0xAA)) {
105 PRINTF("Using MMC for subchannel\n");
106 return 1; // supported
110 return 0; // NOT supported
113 int OpenCdHandle(const char *dev) {
116 cdHandle = open(dev, O_RDONLY);
118 if (cdHandle != -1) {
119 ioctl(cdHandle, CDROM_LOCKDOOR, 0);
121 spindown = (char)SpinDown;
122 ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
124 ioctl(cdHandle, CDROM_SELECT_SPEED, CdrSpeed);
126 ReadMMC = CheckReadMMC();
127 SubQMMC = CheckSubQMMC();
135 void CloseCdHandle() {
136 char spindown = SPINDOWN_VENDOR_SPECIFIC;
137 ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
144 int IsCdHandleOpen() {
145 return (cdHandle != -1);
148 long GetTN(unsigned char *buffer) {
149 struct cdrom_tochdr toc;
151 if (ioctl(cdHandle, CDROMREADTOCHDR, &toc) == -1)
154 buffer[0] = toc.cdth_trk0; // start track
155 buffer[1] = toc.cdth_trk1; // end track
160 long GetTD(unsigned char track, unsigned char *buffer) {
161 struct cdrom_tocentry entry;
164 track = 0xAA; // total time (leadout)
165 entry.cdte_track = track;
166 entry.cdte_format = CDROM_MSF;
168 if (ioctl(cdHandle, CDROMREADTOCENTRY, &entry) == -1)
171 buffer[0] = entry.cdte_addr.msf.frame;
172 buffer[1] = entry.cdte_addr.msf.second;
173 buffer[2] = entry.cdte_addr.msf.minute;
178 long GetTE(unsigned char track, unsigned char *m, unsigned char *s, unsigned char *f) {
179 struct cdrom_tocentry entry;
180 unsigned char msf[3];
182 if (GetTN(msf) == -1) return -1;
184 entry.cdte_track = track + 1;
185 if (entry.cdte_track > msf[1]) entry.cdte_track = 0xaa;
187 entry.cdte_format = CDROM_MSF;
189 if (ioctl(cdHandle, CDROMREADTOCENTRY, &entry) == -1)
192 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);
201 long ReadSector(crdata *cr) {
206 memset(&cdb, 0, sizeof(cdb));
208 lba = msf_to_lba(cr->msf.cdmsf_min0, cr->msf.cdmsf_sec0, cr->msf.cdmsf_frame0);
210 cdb.Code = GPCMD_READ_CD;
212 cdb.IncludeUserData = 1;
214 cdb.IncludeSyncData = 1;
215 cdb.SubChannelSelection = 0;
216 cdb.StartingLBA[1] = lba >> 16;
217 cdb.StartingLBA[2] = lba >> 8;
218 cdb.StartingLBA[3] = lba;
219 cdb.TransferBlocks[2] = 1;
221 if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, (unsigned char *)cr, sizeof(*cr)) != 0)
224 if (ioctl(cdHandle, CDROMREADRAW, cr) == -1)
231 long PlayCDDA(unsigned char *sector) {
232 struct cdrom_msf addr;
233 unsigned char ptmp[4];
235 // 0 is the last track of every cdrom, so play up to there
236 if (GetTD(0, ptmp) == -1)
239 addr.cdmsf_min0 = sector[0];
240 addr.cdmsf_sec0 = sector[1];
241 addr.cdmsf_frame0 = sector[2];
242 addr.cdmsf_min1 = ptmp[2];
243 addr.cdmsf_sec1 = ptmp[1];
244 addr.cdmsf_frame1 = ptmp[0];
246 if (ioctl(cdHandle, CDROMPLAYMSF, &addr) == -1)
253 struct cdrom_subchnl sc;
255 sc.cdsc_format = CDROM_MSF;
256 if (ioctl(cdHandle, CDROMSUBCHNL, &sc) == -1)
259 switch (sc.cdsc_audiostatus) {
260 case CDROM_AUDIO_PAUSED:
261 case CDROM_AUDIO_PLAY:
262 ioctl(cdHandle, CDROMSTOP);
269 long GetStatus(int playing, struct CdrStat *stat) {
270 struct cdrom_subchnl sc;
274 memset(stat, 0, sizeof(struct CdrStat));
276 if (playing) { // return Time only if playing
277 sc.cdsc_format = CDROM_MSF;
278 if (ioctl(cdHandle, CDROMSUBCHNL, &sc) != -1)
279 memcpy(stat->Time, &sc.cdsc_absaddr.msf, 3);
282 ret = ioctl(cdHandle, CDROM_DISC_STATUS);
294 ret = ioctl(cdHandle, CDROM_DRIVE_STATUS);
299 stat->Status |= 0x10;
302 spindown = (char)SpinDown;
303 ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
304 ioctl(cdHandle, CDROM_SELECT_SPEED, CdrSpeed);
305 ioctl(cdHandle, CDROM_LOCKDOOR, 0);
309 switch (sc.cdsc_audiostatus) {
310 case CDROM_AUDIO_PLAY:
311 stat->Status |= 0x80;
318 static unsigned char *ReadSubMMC(const unsigned char *time) {
319 static unsigned char buf[CD_FRAMESIZE_RAW + 96];
320 int lba = msf_to_lba(btoi(time[0]), btoi(time[1]), btoi(time[2]));
323 memset(&cdb, 0, sizeof(cdb));
325 cdb.Code = GPCMD_READ_CD;
327 cdb.IncludeUserData = 1;
329 cdb.IncludeSyncData = 1;
330 cdb.StartingLBA[1] = lba >> 16;
331 cdb.StartingLBA[2] = lba >> 8;
332 cdb.StartingLBA[3] = lba;
333 cdb.TransferBlocks[2] = 1;
334 cdb.SubChannelSelection = 1;
336 if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) != 0)
339 DecodeRawSubData(buf + CD_FRAMESIZE_RAW);
340 return buf + CD_FRAMESIZE_RAW;
343 static unsigned char *ReadSubIOCTL(const unsigned char *time) {
344 static struct SubQ subq;
345 struct cdrom_subchnl subchnl;
350 cr.msf.cdmsf_min0 = btoi(time[0]);
351 cr.msf.cdmsf_sec0 = btoi(time[1]);
352 cr.msf.cdmsf_frame0 = btoi(time[2]);
354 if (ioctl(cdHandle, CDROMSEEK, &cr.msf) == -1) {
355 // will be slower, but there's no other way to make it accurate
356 if (ioctl(cdHandle, CDROMREADRAW, &cr) == -1) {
361 subchnl.cdsc_format = CDROM_MSF;
362 r = ioctl(cdHandle, CDROMSUBCHNL, &subchnl);
364 if (r == -1) return NULL;
366 subq.ControlAndADR = 0x41;
367 subq.TrackNumber = subchnl.cdsc_trk;
368 subq.IndexNumber = subchnl.cdsc_ind;
369 subq.TrackRelativeAddress[0] = itob(subchnl.cdsc_reladdr.msf.minute);
370 subq.TrackRelativeAddress[1] = itob(subchnl.cdsc_reladdr.msf.second);
371 subq.TrackRelativeAddress[2] = itob(subchnl.cdsc_reladdr.msf.frame);
372 subq.AbsoluteAddress[0] = itob(subchnl.cdsc_absaddr.msf.minute);
373 subq.AbsoluteAddress[1] = itob(subchnl.cdsc_absaddr.msf.second);
374 subq.AbsoluteAddress[2] = itob(subchnl.cdsc_absaddr.msf.frame);
376 // CRC is not supported with IOCTL, fake it.
377 crc = calcCrc((unsigned char *)&subq + 12, 10);
378 subq.CRC[0] = (crc >> 8);
379 subq.CRC[1] = (crc & 0xFF);
381 r = msf_to_lba(btoi(time[0]), btoi(time[1]), btoi(time[2]));
383 if (GetTE(1, &cr.msf.cdmsf_min0, &cr.msf.cdmsf_sec0, &cr.msf.cdmsf_frame0) == -1) {
384 cr.msf.cdmsf_min0 = 80;
385 cr.msf.cdmsf_sec0 = 0;
386 cr.msf.cdmsf_frame0 = 0;
389 if (msf_to_lba(cr.msf.cdmsf_min0, cr.msf.cdmsf_sec0, cr.msf.cdmsf_frame0) >= r &&
390 (msf_to_lba(subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, subchnl.cdsc_absaddr.msf.frame) != r ||
391 msf_to_lba(subchnl.cdsc_reladdr.msf.minute, subchnl.cdsc_reladdr.msf.second, subchnl.cdsc_reladdr.msf.frame) != r - CD_MSF_OFFSET))
392 subq.CRC[1] ^= 1; // time mismatch; report wrong CRC
394 PRINTF("subq : %x,%x : %x,%x,%x : %x,%x,%x\n",
395 subchnl.cdsc_trk, subchnl.cdsc_ind,
396 itob(subchnl.cdsc_reladdr.msf.minute), itob(subchnl.cdsc_reladdr.msf.second), itob(subchnl.cdsc_reladdr.msf.frame),
397 itob(subchnl.cdsc_absaddr.msf.minute), itob(subchnl.cdsc_absaddr.msf.second), itob(subchnl.cdsc_absaddr.msf.frame));
399 return (unsigned char *)&subq;
402 unsigned char *ReadSub(const unsigned char *time) {
403 if (SubQMMC) return ReadSubMMC(time);
404 else return ReadSubIOCTL(time);