PCem
view src/cdrom-ioctl-linux.c @ 154:d0d530adce12
Initial port to Linux (using Allegro).
64-bit fixes.
Some changes to aid portability.
A few other tweaks.
| author | TomW |
|---|---|
| date | Thu Sep 04 21:07:24 2014 +0100 |
| parents | |
| children |
line source
1 /*Linux CD-ROM support via IOCTL*/
3 #include <linux/cdrom.h>
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6 #include "ibm.h"
7 #include "ide.h"
8 #include "cdrom-ioctl.h"
10 static ATAPI ioctl_atapi;
12 static uint32_t last_block = 0;
13 static int ioctl_inited = 0;
14 static char ioctl_path[8];
15 static void ioctl_close(void);
16 static int tocvalid = 0;
17 static struct cdrom_tocentry toc[100];
18 static int first_track, last_track;
20 #define MSFtoLBA(m,s,f) (((((m*60)+s)*75)+f)-150)
22 enum
23 {
24 CD_STOPPED = 0,
25 CD_PLAYING,
26 CD_PAUSED
27 };
29 static int ioctl_cd_state = CD_STOPPED;
30 static uint32_t ioctl_cd_pos = 0, ioctl_cd_end = 0;
32 #define BUF_SIZE 32768
33 static int16_t cd_buffer[BUF_SIZE];
34 static int cd_buflen = 0;
35 void ioctl_audio_callback(int16_t *output, int len)
36 {
37 int fd;
38 struct cdrom_read_audio read_audio;
40 // pclog("Audio callback %08X %08X %i %i %i %04X %i\n", ioctl_cd_pos, ioctl_cd_end, ioctl_cd_state, cd_buflen, len, cd_buffer[4], GetTickCount());
41 if (ioctl_cd_state != CD_PLAYING)
42 {
43 memset(output, 0, len * 2);
44 return;
45 }
46 fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
48 if (fd <= 0)
49 {
50 memset(output, 0, len * 2);
51 return;
52 }
54 while (cd_buflen < len)
55 {
56 if (ioctl_cd_pos < ioctl_cd_end)
57 {
58 read_audio.addr.lba = ioctl_cd_pos;
59 read_audio.addr_format = CDROM_LBA;
60 read_audio.nframes = 1;
61 read_audio.buf = (__u8 *)&cd_buffer[cd_buflen];
63 if (ioctl(fd, CDROMREADAUDIO, &read_audio) < 0)
64 {
65 // pclog("DeviceIoControl returned false\n");
66 memset(&cd_buffer[cd_buflen], 0, (BUF_SIZE - cd_buflen) * 2);
67 ioctl_cd_state = CD_STOPPED;
68 cd_buflen = len;
69 }
70 else
71 {
72 // pclog("DeviceIoControl returned true\n");
73 ioctl_cd_pos++;
74 cd_buflen += (2352 / 2);
75 }
76 }
77 else
78 {
79 memset(&cd_buffer[cd_buflen], 0, (BUF_SIZE - cd_buflen) * 2);
80 ioctl_cd_state = CD_STOPPED;
81 cd_buflen = len;
82 }
83 }
84 close(fd);
85 memcpy(output, cd_buffer, len * 2);
86 // for (c = 0; c < BUF_SIZE - len; c++)
87 // cd_buffer[c] = cd_buffer[c + cd_buflen];
88 memcpy(&cd_buffer[0], &cd_buffer[len], (BUF_SIZE - len) * 2);
89 cd_buflen -= len;
90 // pclog("Done %i\n", GetTickCount());
91 }
93 void ioctl_audio_stop()
94 {
95 ioctl_cd_state = CD_STOPPED;
96 }
98 static int get_track_nr(uint32_t pos)
99 {
100 int c;
101 int track = 0;
103 if (!tocvalid)
104 return 0;
106 for (c = first_track; c < last_track; c++)
107 {
108 uint32_t track_address = toc[c].cdte_addr.msf.frame +
109 (toc[c].cdte_addr.msf.second * 75) +
110 (toc[c].cdte_addr.msf.minute * 75 * 60);
111 //pclog("get_track_nr: track=%i pos=%x track_address=%x\n", c, pos, track_address);
112 if (track_address <= pos)
113 track = c;
114 }
115 return track;
116 }
118 static void ioctl_playaudio(uint32_t pos, uint32_t len, int ismsf)
119 {
120 // pclog("Play audio - %08X %08X %i\n", pos, len, ismsf);
121 if (ismsf)
122 {
123 pos = (pos & 0xff) + (((pos >> 8) & 0xff) * 75) + (((pos >> 16) & 0xff) * 75 * 60);
124 len = (len & 0xff) + (((len >> 8) & 0xff) * 75) + (((len >> 16) & 0xff) * 75 * 60);
125 // pclog("MSF - pos = %08X len = %08X\n", pos, len);
126 }
127 else
128 len += pos;
129 ioctl_cd_pos = pos;// + 150;
130 ioctl_cd_end = pos+len;// + 150;
131 ioctl_cd_state = CD_PLAYING;
132 // pclog("Audio start %08X %08X %i %i %i\n", ioctl_cd_pos, ioctl_cd_end, ioctl_cd_state, 0, len);
133 }
135 static void ioctl_pause(void)
136 {
137 if (ioctl_cd_state == CD_PLAYING)
138 ioctl_cd_state = CD_PAUSED;
139 }
141 static void ioctl_resume(void)
142 {
143 if (ioctl_cd_state == CD_PAUSED)
144 ioctl_cd_state = CD_PLAYING;
145 }
147 static void ioctl_stop(void)
148 {
149 ioctl_cd_state = CD_STOPPED;
150 }
152 static void ioctl_seek(uint32_t pos)
153 {
154 // pclog("Seek %08X\n", pos);
155 ioctl_cd_pos = pos;
156 ioctl_cd_state = CD_STOPPED;
157 }
159 static int read_toc(int fd)
160 {
161 struct cdrom_tochdr toc_hdr;
162 int track, err;
163 //pclog("read_toc\n");
164 err = ioctl(fd, CDROMREADTOCHDR, &toc_hdr);
165 if (err == -1)
166 {
167 pclog("read_toc: CDROMREADTOCHDR failed\n");
168 return 0;
169 }
171 first_track = toc_hdr.cdth_trk0;
172 last_track = toc_hdr.cdth_trk1;
173 //pclog("read_toc: first_track=%i last_track=%i\n", first_track, last_track);
174 memset(toc, 0, sizeof(toc));
176 for (track = toc_hdr.cdth_trk0; track <= toc_hdr.cdth_trk1; track++)
177 {
178 toc[track].cdte_track = track;
179 toc[track].cdte_format = CDROM_MSF;
180 err = ioctl(fd, CDROMREADTOCENTRY, &toc[track]);
181 if (err == -1)
182 {
183 // pclog("read_toc: CDROMREADTOCENTRY failed on track %i\n", track);
184 return 0;
185 }
186 // pclog("read_toc: Track %02X - number %02X control %02X adr %02X address %02X %02X %02X %02X\n", track, toc[track].cdte_track, toc[track].cdte_ctrl, toc[track].cdte_adr, 0, toc[track].cdte_addr.msf.minute, toc[track].cdte_addr.msf.second, toc[track].cdte_addr.msf.frame);
187 }
188 return 1;
189 }
191 static int ioctl_ready(void)
192 {
193 long size;
194 int temp;
195 struct cdrom_tochdr toc_hdr;
196 struct cdrom_tocentry toc_entry;
197 int err;
198 int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
200 if (fd <= 0)
201 return 0;
203 err = ioctl(fd, CDROMREADTOCHDR, &toc_hdr);
204 if (err == -1)
205 {
206 close(fd);
207 return 0;
208 }
209 // pclog("CDROMREADTOCHDR: start track=%i end track=%i\n", toc_hdr.cdth_trk0, toc_hdr.cdth_trk1);
210 toc_entry.cdte_track = toc_hdr.cdth_trk1;
211 toc_entry.cdte_format = CDROM_MSF;
212 err = ioctl(fd, CDROMREADTOCENTRY, &toc_entry);
213 if (err == -1)
214 {
215 close(fd);
216 return 0;
217 }
218 // pclog("CDROMREADTOCENTRY: addr=%02i:%02i:%02i\n", toc_entry.cdte_addr.msf.minute, toc_entry.cdte_addr.msf.second, toc_entry.cdte_addr.msf.frame);
219 if ((toc_entry.cdte_addr.msf.minute != toc[toc_hdr.cdth_trk1].cdte_addr.msf.minute) ||
220 (toc_entry.cdte_addr.msf.second != toc[toc_hdr.cdth_trk1].cdte_addr.msf.second) ||
221 (toc_entry.cdte_addr.msf.frame != toc[toc_hdr.cdth_trk1].cdte_addr.msf.frame ) ||
222 !tocvalid)
223 {
224 int track;
225 ioctl_cd_state = CD_STOPPED;
227 tocvalid = read_toc(fd);
228 close(fd);
229 return 0;
230 }
231 close(fd);
232 return 1;
233 }
235 static uint8_t ioctl_getcurrentsubchannel(uint8_t *b, int msf)
236 {
237 struct cdrom_subchnl sub;
238 uint32_t cdpos = ioctl_cd_pos;
239 int track = get_track_nr(cdpos);
240 uint32_t track_address = toc[track].cdte_addr.msf.frame +
241 (toc[track].cdte_addr.msf.second * 75) +
242 (toc[track].cdte_addr.msf.minute * 75 * 60);
243 long size;
244 int pos=0;
245 int err;
246 uint8_t ret;
247 //pclog("ioctl_getsubchannel: cdpos=%x track_address=%x track=%i\n", cdpos, track_address, track);
248 if (ioctl_cd_state == CD_PLAYING)
249 ret = 0x11;
250 else if (ioctl_cd_state == CD_PAUSED)
251 ret = 0x12;
252 else
253 ret = 0x13;
255 b[pos++] = (toc[track].cdte_adr << 4) | toc[track].cdte_ctrl;
256 b[pos++] = track;
257 b[pos++] = 0;
259 if (msf)
260 {
261 uint32_t dat = cdpos;
262 b[pos + 3] = (uint8_t)(dat % 75); dat /= 75;
263 b[pos + 2] = (uint8_t)(dat % 60); dat /= 60;
264 b[pos + 1] = (uint8_t)dat;
265 b[pos] = 0;
266 pos += 4;
267 dat = cdpos - track_address;
268 b[pos + 3] = (uint8_t)(dat % 75); dat /= 75;
269 b[pos + 2] = (uint8_t)(dat % 60); dat /= 60;
270 b[pos + 1] = (uint8_t)dat;
271 b[pos] = 0;
272 pos += 4;
273 }
274 else
275 {
276 b[pos++] = (cdpos >> 24) & 0xff;
277 b[pos++] = (cdpos >> 16) & 0xff;
278 b[pos++] = (cdpos >> 8) & 0xff;
279 b[pos++] = cdpos & 0xff;
280 cdpos -= track_address;
281 b[pos++] = (cdpos >> 24) & 0xff;
282 b[pos++] = (cdpos >> 16) & 0xff;
283 b[pos++] = (cdpos >> 8) & 0xff;
284 b[pos++] = cdpos & 0xff;
285 }
287 return ret;
288 }
290 static void ioctl_eject(void)
291 {
292 int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
294 if (fd <= 0)
295 return;
297 ioctl(fd, CDROMEJECT);
299 close(fd);
300 }
302 static void ioctl_load(void)
303 {
304 int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
306 if (fd <= 0)
307 return;
309 ioctl(fd, CDROMEJECT);
311 close(fd);
312 }
314 static void ioctl_readsector(uint8_t *b, int sector)
315 {
316 int cdrom = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
317 if (cdrom <= 0)
318 return;
319 lseek(cdrom, sector*2048, SEEK_SET);
320 read(cdrom, b, 2048);
321 close(cdrom);
322 }
324 static int ioctl_readtoc(unsigned char *b, unsigned char starttrack, int msf, int maxlen, int single)
325 {
326 int len=4;
327 long size;
328 int c,d;
329 uint32_t temp;
330 int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
332 if (fd <= 0)
333 return 0;
335 ioctl_cd_state = CD_STOPPED;
337 tocvalid = read_toc(fd);
339 close(fd);
341 if (!tocvalid)
342 return 4;
344 // pclog("Read TOC done! %i\n",single);
345 b[2] = first_track;
346 b[3] = last_track;
347 d = 0;
348 //pclog("Read TOC starttrack=%i\n", starttrack);
349 for (c = 1; c <= last_track; c++)
350 {
351 if (toc[c].cdte_track >= starttrack)
352 {
353 d = c;
354 break;
355 }
356 }
357 b[2] = toc[c].cdte_track;
358 last_block = 0;
359 for (c = d; c <= last_track; c++)
360 {
361 uint32_t address;
362 if ((len + 8) > maxlen)
363 break;
364 // pclog("Len %i max %i Track %02X - %02X %02X %02i:%02i:%02i %08X\n",len,maxlen,toc[c].cdte_track,toc[c].cdte_adr,toc[c].cdte_ctrl,toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame,MSFtoLBA(toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame));
365 b[len++] = 0; /*Reserved*/
366 b[len++] = (toc[c].cdte_adr << 4) | toc[c].cdte_ctrl;
367 b[len++] = toc[c].cdte_track;
368 b[len++] = 0; /*Reserved*/
369 address = MSFtoLBA(toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame);
370 if (address > last_block)
371 last_block = address;
373 if (msf)
374 {
375 b[len++] = 0;
376 b[len++] = toc[c].cdte_addr.msf.minute;
377 b[len++] = toc[c].cdte_addr.msf.second;
378 b[len++] = toc[c].cdte_addr.msf.frame;
379 }
380 else
381 {
382 temp = MSFtoLBA(toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame);
383 b[len++] = temp >> 24;
384 b[len++] = temp >> 16;
385 b[len++] = temp >> 8;
386 b[len++] = temp;
387 }
388 if (single)
389 break;
390 }
391 b[0] = (uint8_t)(((len-2) >> 8) & 0xff);
392 b[1] = (uint8_t)((len-2) & 0xff);
393 /* pclog("Table of Contents (%i bytes) : \n", size);
394 pclog("First track - %02X\n", first_track);
395 pclog("Last track - %02X\n", last_track);
396 for (c = 0; c <= last_track; c++)
397 pclog("Track %02X - number %02X control %02X adr %02X address %02X %02X %02X %02X\n", c, toc[c].cdte_track, toc[c].cdte_ctrl, toc[c].cdte_adr, 0, toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame);
398 for (c = 0;c <= last_track; c++)
399 pclog("Track %02X - number %02X control %02X adr %02X address %06X\n", c, toc[c].cdte_track, toc[c].cdte_ctrl, toc[c].cdte_adr, MSFtoLBA(toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame));*/
400 return len;
401 }
403 static void ioctl_readtoc_session(unsigned char *b, int msf, int maxlen)
404 {
405 struct cdrom_multisession session;
406 int len = 4;
407 int err;
408 int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
410 if (fd <= 0)
411 return;
413 session.addr_format = CDROM_MSF;
414 err = ioctl(fd, CDROMMULTISESSION, &session);
416 if (err == -1)
417 {
418 close(fd);
419 return;
420 }
422 b[2] = 0;
423 b[3] = 0;
424 b[len++] = 0; /*Reserved*/
425 b[len++] = (toc[0].cdte_adr << 4) | toc[0].cdte_ctrl;
426 b[len++] = toc[0].cdte_track;
427 b[len++] = 0; /*Reserved*/
428 if (msf)
429 {
430 b[len++] = 0;
431 b[len++] = session.addr.msf.minute;
432 b[len++] = session.addr.msf.second;
433 b[len++] = session.addr.msf.frame;
434 }
435 else
436 {
437 uint32_t temp = MSFtoLBA(session.addr.msf.minute, session.addr.msf.second, session.addr.msf.frame);
438 b[len++] = temp >> 24;
439 b[len++] = temp >> 16;
440 b[len++] = temp >> 8;
441 b[len++] = temp;
442 }
443 }
445 static uint32_t ioctl_size()
446 {
447 unsigned char b[4096];
449 atapi->readtoc(b, 0, 0, 4096, 0);
451 return last_block;
452 }
454 void ioctl_reset()
455 {
456 int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK);
457 //pclog("ioctl_reset: fd=%i\n", fd);
458 tocvalid = 0;
460 if (fd <= 0)
461 return;
463 tocvalid = read_toc(fd);
465 close(fd);
466 }
468 int ioctl_open(char d)
469 {
470 atapi=&ioctl_atapi;
471 return 0;
472 }
474 static void ioctl_close(void)
475 {
476 }
478 static void ioctl_exit(void)
479 {
480 ioctl_stop();
481 ioctl_inited = 0;
482 tocvalid=0;
483 }
485 static ATAPI ioctl_atapi=
486 {
487 ioctl_ready,
488 ioctl_readtoc,
489 ioctl_readtoc_session,
490 ioctl_getcurrentsubchannel,
491 ioctl_readsector,
492 ioctl_playaudio,
493 ioctl_seek,
494 ioctl_load,
495 ioctl_eject,
496 ioctl_pause,
497 ioctl_resume,
498 ioctl_size,
499 ioctl_stop,
500 ioctl_exit
501 };
