1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 #ifdef SDL_CDROM_QNX
25
26 /* Functions for system-level CD-ROM audio control */
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/ioctl.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sys/cdrom.h>
35 #include <sys/dcmd_cam.h>
36
37 #include "SDL_timer.h"
38 #include "SDL_cdrom.h"
39 #include "../SDL_syscdrom.h"
40
41 /* The maximum number of CD-ROM drives we'll detect */
42 #define MAX_DRIVES 16
43
44 #define QNX_CD_OPENMODE O_RDONLY | O_EXCL
45
46 /* A list of available CD-ROM drives */
47 static char *SDL_cdlist[MAX_DRIVES];
48 static dev_t SDL_cdmode[MAX_DRIVES];
49 static int SDL_cdopen[MAX_DRIVES];
50
51 /* The system-dependent CD control functions */
52 static const char *SDL_SYS_CDName(int drive);
53 static int SDL_SYS_CDOpen(int drive);
54 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
55 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
56 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
57 static int SDL_SYS_CDPause(SDL_CD *cdrom);
58 static int SDL_SYS_CDResume(SDL_CD *cdrom);
59 static int SDL_SYS_CDStop(SDL_CD *cdrom);
60 static int SDL_SYS_CDEject(SDL_CD *cdrom);
61 static void SDL_SYS_CDClose(SDL_CD *cdrom);
62
63 /* Check a drive to see if it is a CD-ROM */
CheckDrive(char * drive,struct stat * stbuf)64 static int CheckDrive(char *drive, struct stat *stbuf)
65 {
66 int is_cd, cdfd;
67 cam_devinfo_t dinfo;
68 int devctlret=0;
69
70 int atapi;
71 int removable;
72 int cdb10;
73
74 /* If it doesn't exist, return -1 */
75 if (stat(drive, stbuf) < 0)
76 {
77 return(-1);
78 }
79
80 /* If it does exist, verify that it's an available CD-ROM */
81 is_cd = 0;
82
83 if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode))
84 {
85 cdfd = open(drive, QNX_CD_OPENMODE);
86 if ( cdfd >= 0 )
87 {
88 devctlret=devctl(cdfd, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
89
90 if (devctlret==EOK)
91 {
92 atapi=dinfo.flags & DEV_ATAPI;
93 removable=dinfo.flags & DEV_REMOVABLE;
94 cdb10=dinfo.flags & DEV_CDB_10; /* I'm not sure about that flag */
95
96 /* in the near future need to add more checks for splitting cdroms from other devices */
97 if ((atapi)&&(removable))
98 {
99 is_cd = 1;
100 }
101 }
102
103 close(cdfd);
104 }
105 }
106 return(is_cd);
107 }
108
109 /* Add a CD-ROM drive to our list of valid drives */
AddDrive(char * drive,struct stat * stbuf)110 static void AddDrive(char *drive, struct stat *stbuf)
111 {
112 int i;
113
114 if (SDL_numcds < MAX_DRIVES)
115 {
116 /* Check to make sure it's not already in our list.
117 This can happen when we see a drive via symbolic link. */
118
119 for (i=0; i<SDL_numcds; ++i)
120 {
121 if (stbuf->st_rdev == SDL_cdmode[i])
122 {
123 return;
124 }
125 }
126
127 /* Add this drive to our list */
128
129 i = SDL_numcds;
130 SDL_cdlist[i] = SDL_strdup(drive);
131 if (SDL_cdlist[i] == NULL)
132 {
133 SDL_OutOfMemory();
134 return;
135 }
136 SDL_cdmode[i] = stbuf->st_rdev;
137 ++SDL_numcds;
138 }
139 }
140
SDL_SYS_CDInit(void)141 int SDL_SYS_CDInit(void)
142 {
143 /* checklist: /dev/cdrom, /dev/cd?, /dev/scd? */
144 static char *checklist[]={"cdrom", "?0 cd?", "?1 cd?", "?0 scd?", NULL};
145
146 char *SDLcdrom;
147 int i, j, exists;
148 char drive[32];
149 struct stat stbuf;
150
151 /* Fill in our driver capabilities */
152 SDL_CDcaps.Name = SDL_SYS_CDName;
153 SDL_CDcaps.Open = SDL_SYS_CDOpen;
154 SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
155 SDL_CDcaps.Status = SDL_SYS_CDStatus;
156 SDL_CDcaps.Play = SDL_SYS_CDPlay;
157 SDL_CDcaps.Pause = SDL_SYS_CDPause;
158 SDL_CDcaps.Resume = SDL_SYS_CDResume;
159 SDL_CDcaps.Stop = SDL_SYS_CDStop;
160 SDL_CDcaps.Eject = SDL_SYS_CDEject;
161 SDL_CDcaps.Close = SDL_SYS_CDClose;
162
163 /* clearing device open status */
164 for (i=0; i<MAX_DRIVES; i++)
165 {
166 SDL_cdopen[i]=0;
167 }
168
169 /* Look in the environment for our CD-ROM drive list */
170 SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
171 if ( SDLcdrom != NULL )
172 {
173 char *cdpath, *delim;
174 size_t len = SDL_strlen(SDLcdrom)+1;
175 cdpath = SDL_stack_alloc(char, len);
176 if (cdpath != NULL)
177 {
178 SDL_strlcpy(cdpath, SDLcdrom, len);
179 SDLcdrom = cdpath;
180 do {
181 delim = SDL_strchr(SDLcdrom, ':');
182 if (delim)
183 {
184 *delim++ = '\0';
185 }
186 if (CheckDrive(SDLcdrom, &stbuf) > 0)
187 {
188 AddDrive(SDLcdrom, &stbuf);
189 }
190 if (delim)
191 {
192 SDLcdrom = delim;
193 }
194 else
195 {
196 SDLcdrom = NULL;
197 }
198 } while (SDLcdrom);
199 SDL_stack_free(cdpath);
200 }
201
202 /* If we found our drives, there's nothing left to do */
203 if (SDL_numcds > 0)
204 {
205 return(0);
206 }
207 }
208
209 /* Scan the system for CD-ROM drives */
210 for ( i=0; checklist[i]; ++i )
211 {
212 if (checklist[i][0] == '?')
213 {
214 char* insert;
215 exists = 1;
216
217 for ( j=checklist[i][1]; exists; ++j )
218 {
219 SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
220 insert = SDL_strchr(drive, '?');
221 if (insert != NULL)
222 {
223 *insert = j;
224 }
225 switch (CheckDrive(drive, &stbuf))
226 {
227 /* Drive exists and is a CD-ROM */
228 case 1:
229 AddDrive(drive, &stbuf);
230 break;
231 /* Drive exists, but isn't a CD-ROM */
232 case 0:
233 break;
234 /* Drive doesn't exist */
235 case -1:
236 exists = 0;
237 break;
238 }
239 }
240 }
241 else
242 {
243 SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
244 if (CheckDrive(drive, &stbuf) > 0)
245 {
246 AddDrive(drive, &stbuf);
247 }
248 }
249 }
250 return(0);
251 }
252
SDL_SYS_CDName(int drive)253 static const char *SDL_SYS_CDName(int drive)
254 {
255 return(SDL_cdlist[drive]);
256 }
257
SDL_SYS_CDOpen(int drive)258 static int SDL_SYS_CDOpen(int drive)
259 {
260 int handle;
261
262 handle=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
263
264 if (handle>0)
265 {
266 SDL_cdopen[drive]=handle;
267 }
268
269 return (handle);
270 }
271
SDL_SYS_CDGetTOC(SDL_CD * cdrom)272 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
273 {
274 cdrom_read_toc_t toc;
275 int i, okay;
276
277 okay = 0;
278 if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL) == 0)
279 {
280 cdrom->numtracks = toc.last_track - toc.first_track + 1;
281 if (cdrom->numtracks > SDL_MAX_TRACKS)
282 {
283 cdrom->numtracks = SDL_MAX_TRACKS;
284 }
285 /* Read all the track TOC entries */
286 for (i=0; i<=cdrom->numtracks; ++i)
287 {
288 if (i == cdrom->numtracks)
289 {
290 cdrom->track[i].id = CDROM_LEADOUT;
291 }
292 else
293 {
294 cdrom->track[i].id = toc.first_track+i;
295 }
296
297 cdrom->track[i].type = toc.toc_entry[i].control_adr & 0x0F;
298 cdrom->track[i].offset = toc.toc_entry[i].addr.lba;
299 cdrom->track[i].length = 0;
300
301 if (i > 0)
302 {
303 cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
304 }
305 }
306 if (i == (cdrom->numtracks+1))
307 {
308 okay = 1;
309 }
310 }
311 return (okay ? 0 : -1);
312 }
313
314 /* Get CD-ROM status */
SDL_SYS_CDStatus(SDL_CD * cdrom,int * position)315 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
316 {
317 CDstatus status;
318
319 cdrom_read_toc_t toc;
320 cdrom_subch_data_t info;
321 cam_devinfo_t dinfo;
322
323 int devctlret=0;
324 int drive=-1;
325 int i;
326 int eagaincnt=0;
327
328 /* check media presence before read subchannel call, some cdroms can lockups */
329 /* if no media, while calling read subchannel functions. */
330 devctlret=devctl(cdrom->id, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
331
332 if (devctlret==EOK)
333 {
334 if ((dinfo.flags & DEV_NO_MEDIA)!=0)
335 {
336 status = CD_TRAYEMPTY;
337 if (position)
338 {
339 *position = 0;
340 }
341 return (status);
342 }
343 }
344
345 /* if media exists, then do other stuff */
346
347 SDL_memset(&info, 0x00, sizeof(info));
348 info.subch_command.data_format = CDROM_SUBCH_CURRENT_POSITION;
349
350 do {
351 devctlret=devctl(cdrom->id, DCMD_CAM_CDROMSUBCHNL, &info, sizeof(info), NULL);
352 if (devctlret==EIO)
353 {
354 /* big workaround for media change, handle is unusable after that,
355 that bug was found in QNX 6.2, 6.2.1 is not released yet. */
356
357 for (i=0; i<MAX_DRIVES; i++)
358 {
359 if (SDL_cdopen[i]==cdrom->id)
360 {
361 drive=i;
362 break;
363 }
364 }
365 if (drive==-1)
366 {
367 /* that cannot happen, but ... */
368 break;
369 }
370 close(cdrom->id);
371 cdrom->id=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
372 devctlret=EAGAIN;
373 }
374 if (devctlret==EAGAIN)
375 {
376 eagaincnt++;
377 }
378 if (eagaincnt==2)
379 {
380 /* workaround for broken cdroms, which can return always EAGAIN when its not ready, */
381 /* that mean errornous media or just no media avail */
382 devctlret=ENXIO;
383 break;
384 }
385 } while ((devctlret==EAGAIN)||(devctlret==ESTALE));
386
387 if (devctlret != 0)
388 {
389 if (devctlret==ENXIO)
390 {
391 status = CD_TRAYEMPTY;
392 }
393 else
394 {
395 status = CD_ERROR;
396 }
397 }
398 else
399 {
400 switch (info.current_position.header.audio_status)
401 {
402 case CDROM_AUDIO_INVALID:
403 case CDROM_AUDIO_NO_STATUS:
404 /* Try to determine if there's a CD available */
405 if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL)==0)
406 status = CD_STOPPED;
407 else
408 status = CD_TRAYEMPTY;
409 break;
410 case CDROM_AUDIO_COMPLETED:
411 status = CD_STOPPED;
412 break;
413 case CDROM_AUDIO_PLAY:
414 status = CD_PLAYING;
415 break;
416 case CDROM_AUDIO_PAUSED:
417 /* Workaround buggy CD-ROM drive */
418 if (info.current_position.data_format == CDROM_LEADOUT)
419 {
420 status = CD_STOPPED;
421 }
422 else
423 {
424 status = CD_PAUSED;
425 }
426 break;
427 default:
428 status = CD_ERROR;
429 break;
430 }
431 }
432
433 if (position)
434 {
435 if (status==CD_PLAYING || (status==CD_PAUSED))
436 {
437 *position = MSF_TO_FRAMES(info.current_position.addr.msf.minute,
438 info.current_position.addr.msf.second,
439 info.current_position.addr.msf.frame);
440 }
441 else
442 {
443 *position = 0;
444 }
445 }
446
447 return (status);
448 }
449
450 /* Start play */
SDL_SYS_CDPlay(SDL_CD * cdrom,int start,int length)451 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
452 {
453 cdrom_playmsf_t playtime;
454
455 FRAMES_TO_MSF(start, &playtime.start_minute, &playtime.start_second, &playtime.start_frame);
456 FRAMES_TO_MSF(start+length, &playtime.end_minute, &playtime.end_second, &playtime.end_frame);
457
458 if (devctl(cdrom->id, DCMD_CAM_CDROMPLAYMSF, &playtime, sizeof(playtime), NULL) != 0)
459 {
460 return -1;
461 }
462 else
463 {
464 return 0;
465 }
466 }
467
468 /* Pause play */
SDL_SYS_CDPause(SDL_CD * cdrom)469 static int SDL_SYS_CDPause(SDL_CD *cdrom)
470 {
471 if (devctl(cdrom->id, DCMD_CAM_CDROMPAUSE, NULL, 0, NULL)!=0)
472 {
473 return -1;
474 }
475 else
476 {
477 return 0;
478 }
479 }
480
481 /* Resume play */
SDL_SYS_CDResume(SDL_CD * cdrom)482 static int SDL_SYS_CDResume(SDL_CD *cdrom)
483 {
484 if (devctl(cdrom->id, DCMD_CAM_CDROMRESUME, NULL, 0, NULL)!=0)
485 {
486 return -1;
487 }
488 else
489 {
490 return 0;
491 }
492 }
493
494 /* Stop play */
SDL_SYS_CDStop(SDL_CD * cdrom)495 static int SDL_SYS_CDStop(SDL_CD *cdrom)
496 {
497 if (devctl(cdrom->id, DCMD_CAM_CDROMSTOP, NULL, 0, NULL)!=0)
498 {
499 return -1;
500 }
501 else
502 {
503 return 0;
504 }
505 }
506
507 /* Eject the CD-ROM */
SDL_SYS_CDEject(SDL_CD * cdrom)508 static int SDL_SYS_CDEject(SDL_CD *cdrom)
509 {
510 if (devctl(cdrom->id, DCMD_CAM_EJECT_MEDIA, NULL, 0, NULL)!=0)
511 {
512 return -1;
513 }
514 else
515 {
516 return 0;
517 }
518 }
519
520 /* Close the CD-ROM handle */
SDL_SYS_CDClose(SDL_CD * cdrom)521 static void SDL_SYS_CDClose(SDL_CD *cdrom)
522 {
523 int i;
524
525 for (i=0; i<MAX_DRIVES; i++)
526 {
527 if (SDL_cdopen[i]==cdrom->id)
528 {
529 SDL_cdopen[i]=0;
530 break;
531 }
532 }
533
534 close(cdrom->id);
535 }
536
SDL_SYS_CDQuit(void)537 void SDL_SYS_CDQuit(void)
538 {
539 int i;
540
541 if (SDL_numcds > 0)
542 {
543 for (i=0; i<SDL_numcds; ++i)
544 {
545 SDL_free(SDL_cdlist[i]);
546 }
547 SDL_numcds = 0;
548 }
549 }
550
551 #endif /* SDL_CDROM_QNX */
552