pcsxr-1.9.92
[pcsx_rearmed.git] / plugins / dfcdrom / cdr-linux.c
CommitLineData
ef79bbde
P
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
29static int cdHandle = -1;
30static int ReadMMC = 0, SubQMMC = 0;
31
32static 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
64static 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
88static 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
113int 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
135void CloseCdHandle() {
136 char spindown = SPINDOWN_VENDOR_SPECIFIC;
137 ioctl(cdHandle, CDROMSETSPINDOWN, &spindown);
138
139 close(cdHandle);
140
141 cdHandle = -1;
142}
143
144int IsCdHandleOpen() {
145 return (cdHandle != -1);
146}
147
148long 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
160long 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
178long 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
201long 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
231long 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
252long 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
269long 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
318static 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
343static 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
402unsigned char *ReadSub(const unsigned char *time) {
403 if (SubQMMC) return ReadSubMMC(time);
404 else return ReadSubIOCTL(time);
405}
406
407#endif