pcsxr-1.9.92
[pcsx_rearmed.git] / plugins / dfcdrom / cdr-linux.c
1 /*
2  * Copyright (c) 2010, Wei Mingzhi <whistler@openoffice.org>.
3  * All Rights Reserved.
4  *
5  * Based on: Cdrom for Psemu Pro like Emulators
6  * By: linuzappz <linuzappz@hotmail.com>
7  *
8  * Portions based on: cdrdao - write audio CD-Rs in disc-at-once mode
9  * Copyright (C) 2007 Denis Leroy <denis@poolshark.org>
10  *
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.
15  *
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.
20  *
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>.
23  */
24
25 #if defined (__linux__) && !defined (USE_LIBCDIO)
26
27 #include "cdr.h"
28
29 static int cdHandle = -1;
30 static int ReadMMC = 0, SubQMMC = 0;
31
32 static int SendMMCCmd(const unsigned char *cmd, int cmdLen, const unsigned char *dataOut,
33         int dataOutLen, unsigned char *dataIn, int dataInLen)
34 {
35         sg_io_hdr_t io_hdr;
36
37         memset(&io_hdr, 0, sizeof(io_hdr));
38
39         io_hdr.interface_id = 'S';
40         io_hdr.cmd_len = cmdLen;
41         io_hdr.cmdp = (unsigned char *)cmd;
42         io_hdr.timeout = 10000;
43         io_hdr.sbp = NULL;
44         io_hdr.mx_sb_len = 0;
45         io_hdr.flags = 1;
46
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;
55         }
56
57         if (ioctl(cdHandle, SG_IO, &io_hdr) < 0) {
58                 return -1;
59         }
60
61         return io_hdr.status;
62 }
63
64 static int CheckReadMMC() {
65         MMC_READ_CD                     cdb;
66         unsigned char           buf[CD_FRAMESIZE_RAW];
67
68         memset(&cdb, 0, sizeof(cdb));
69         memset(buf, 0xAA, sizeof(buf));
70
71         cdb.Code = GPCMD_READ_CD;
72         cdb.IncludeEDC = 0;
73         cdb.IncludeUserData = 1;
74         cdb.HeaderCode = 3;
75         cdb.IncludeSyncData = 1;
76         cdb.TransferBlocks[2] = 1;
77
78         if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) == 0) {
79                 if (buf[0] != 0xAA) {
80                         PRINTF("Using MMC for data\n");
81                         return 1; // supported
82                 }
83         }
84
85         return 0; // NOT supported
86 }
87
88 static int CheckSubQMMC() {
89         MMC_READ_CD                     cdb;
90         unsigned char           buf[CD_FRAMESIZE_RAW + 96];
91
92         memset(&cdb, 0, sizeof(cdb));
93         memset(buf, 0xAA, sizeof(buf));
94
95         cdb.Code = GPCMD_READ_CD;
96         cdb.IncludeEDC = 1;
97         cdb.IncludeUserData = 1;
98         cdb.HeaderCode = 3;
99         cdb.IncludeSyncData = 1;
100         cdb.SubChannelSelection = 1;
101         cdb.TransferBlocks[2] = 1;
102
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
107                 }
108         }
109
110         return 0; // NOT supported
111 }
112
113 int OpenCdHandle(const char *dev) {
114         char spindown;
115
116         cdHandle = open(dev, O_RDONLY);
117
118         if (cdHandle != -1) {
119                 ioctl(cdHandle, CDROM_LOCKDOOR, 0);
120
121                 spindown = (char)SpinDown;
122                 ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
123
124                 ioctl(cdHandle, CDROM_SELECT_SPEED, CdrSpeed);
125
126                 ReadMMC = CheckReadMMC();
127                 SubQMMC = CheckSubQMMC();
128
129                 return 0;
130         }
131
132         return -1;
133 }
134
135 void CloseCdHandle() {
136         char spindown = SPINDOWN_VENDOR_SPECIFIC;
137         ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
138
139         close(cdHandle);
140
141         cdHandle = -1;
142 }
143
144 int IsCdHandleOpen() {
145         return (cdHandle != -1);
146 }
147
148 long GetTN(unsigned char *buffer) {
149         struct cdrom_tochdr toc;
150
151         if (ioctl(cdHandle, CDROMREADTOCHDR, &toc) == -1)
152                 return -1;
153
154         buffer[0] = toc.cdth_trk0;      // start track
155         buffer[1] = toc.cdth_trk1;      // end track
156
157         return 0;
158 }
159
160 long GetTD(unsigned char track, unsigned char *buffer) {
161         struct cdrom_tocentry entry;
162
163         if (track == 0)
164                 track = 0xAA; // total time (leadout)
165         entry.cdte_track = track;
166         entry.cdte_format = CDROM_MSF;
167
168         if (ioctl(cdHandle, CDROMREADTOCENTRY, &entry) == -1)
169                 return -1;
170
171         buffer[0] = entry.cdte_addr.msf.frame;
172         buffer[1] = entry.cdte_addr.msf.second;
173         buffer[2] = entry.cdte_addr.msf.minute;
174
175         return 0;
176 }
177
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];
181
182         if (GetTN(msf) == -1) return -1;
183
184         entry.cdte_track = track + 1;
185         if (entry.cdte_track > msf[1]) entry.cdte_track = 0xaa;
186
187         entry.cdte_format = CDROM_MSF;
188
189         if (ioctl(cdHandle, CDROMREADTOCENTRY, &entry) == -1)
190                 return -1;
191
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);
193
194         *m = msf[0];
195         *s = msf[1];
196         *f = msf[2];
197
198         return 0;
199 }
200
201 long ReadSector(crdata *cr) {
202         if (ReadMMC) {
203                 MMC_READ_CD                     cdb;
204                 int                                     lba;
205
206                 memset(&cdb, 0, sizeof(cdb));
207
208                 lba = msf_to_lba(cr->msf.cdmsf_min0, cr->msf.cdmsf_sec0, cr->msf.cdmsf_frame0);
209
210                 cdb.Code = GPCMD_READ_CD;
211                 cdb.IncludeEDC = 1;
212                 cdb.IncludeUserData = 1;
213                 cdb.HeaderCode = 3;
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;
220
221                 if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, (unsigned char *)cr, sizeof(*cr)) != 0)
222                         return -1;
223         } else {
224                 if (ioctl(cdHandle, CDROMREADRAW, cr) == -1)
225                         return -1;
226         }
227
228         return 0;
229 }
230
231 long PlayCDDA(unsigned char *sector) {
232         struct cdrom_msf addr;
233         unsigned char ptmp[4];
234
235         // 0 is the last track of every cdrom, so play up to there
236         if (GetTD(0, ptmp) == -1)
237                 return -1;
238
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];
245
246         if (ioctl(cdHandle, CDROMPLAYMSF, &addr) == -1)
247                 return -1;
248
249         return 0;
250 }
251
252 long StopCDDA() {
253         struct cdrom_subchnl sc;
254
255         sc.cdsc_format = CDROM_MSF;
256         if (ioctl(cdHandle, CDROMSUBCHNL, &sc) == -1)
257                 return -1;
258
259         switch (sc.cdsc_audiostatus) {
260                 case CDROM_AUDIO_PAUSED:
261                 case CDROM_AUDIO_PLAY:
262                         ioctl(cdHandle, CDROMSTOP);
263                         break;
264         }
265
266         return 0;
267 }
268
269 long GetStatus(int playing, struct CdrStat *stat) {
270         struct cdrom_subchnl sc;
271         int ret;
272         char spindown;
273
274         memset(stat, 0, sizeof(struct CdrStat));
275
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);
280         }
281
282         ret = ioctl(cdHandle, CDROM_DISC_STATUS);
283         switch (ret) {
284                 case CDS_AUDIO:
285                         stat->Type = 0x02;
286                         break;
287                 case CDS_DATA_1:
288                 case CDS_DATA_2:
289                 case CDS_XA_2_1:
290                 case CDS_XA_2_2:
291                         stat->Type = 0x01;
292                         break;
293         }
294         ret = ioctl(cdHandle, CDROM_DRIVE_STATUS);
295         switch (ret) {
296                 case CDS_NO_DISC:
297                 case CDS_TRAY_OPEN:
298                         stat->Type = 0xff;
299                         stat->Status |= 0x10;
300                         break;
301                 default:
302                         spindown = (char)SpinDown;
303                         ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
304                         ioctl(cdHandle, CDROM_SELECT_SPEED, CdrSpeed);
305                         ioctl(cdHandle, CDROM_LOCKDOOR, 0);
306                         break;
307         }
308
309         switch (sc.cdsc_audiostatus) {
310                 case CDROM_AUDIO_PLAY:
311                         stat->Status |= 0x80;
312                         break;
313         }
314
315         return 0;
316 }
317
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]));
321         MMC_READ_CD cdb;
322
323         memset(&cdb, 0, sizeof(cdb));
324
325         cdb.Code = GPCMD_READ_CD;
326         cdb.IncludeEDC = 1;
327         cdb.IncludeUserData = 1;
328         cdb.HeaderCode = 3;
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;
335
336         if (SendMMCCmd((unsigned char *)&cdb, sizeof(cdb), NULL, 0, buf, sizeof(buf)) != 0)
337                 return NULL;
338
339         DecodeRawSubData(buf + CD_FRAMESIZE_RAW);
340         return buf + CD_FRAMESIZE_RAW;
341 }
342
343 static unsigned char *ReadSubIOCTL(const unsigned char *time) {
344         static struct SubQ subq;
345         struct cdrom_subchnl subchnl;
346         int r;
347         crdata cr;
348         unsigned short crc;
349
350         cr.msf.cdmsf_min0 = btoi(time[0]);
351         cr.msf.cdmsf_sec0 = btoi(time[1]);
352         cr.msf.cdmsf_frame0 = btoi(time[2]);
353
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) {
357                         return NULL;
358                 }
359         }
360
361         subchnl.cdsc_format = CDROM_MSF;
362         r = ioctl(cdHandle, CDROMSUBCHNL, &subchnl);
363
364         if (r == -1) return NULL;
365
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);
375
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);
380
381         r = msf_to_lba(btoi(time[0]), btoi(time[1]), btoi(time[2]));
382
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;
387         }
388
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
393
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));
398
399         return (unsigned char *)&subq;
400 }
401
402 unsigned char *ReadSub(const unsigned char *time) {
403         if (SubQMMC) return ReadSubMMC(time);
404         else return ReadSubIOCTL(time);
405 }
406
407 #endif