2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
26 /* Functions for system-level CD-ROM audio control */
28 #include <sys/types.h>
30 #include <sys/ioctl.h>
34 #include <sys/cdrom.h>
35 #include <sys/dcmd_cam.h>
37 #include "SDL_timer.h"
38 #include "SDL_cdrom.h"
39 #include "../SDL_syscdrom.h"
41 /* The maximum number of CD-ROM drives we'll detect */
44 #define QNX_CD_OPENMODE O_RDONLY | O_EXCL
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];
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);
63 /* Check a drive to see if it is a CD-ROM */
64 static int CheckDrive(char *drive, struct stat *stbuf)
74 /* If it doesn't exist, return -1 */
75 if (stat(drive, stbuf) < 0)
80 /* If it does exist, verify that it's an available CD-ROM */
83 if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode))
85 cdfd = open(drive, QNX_CD_OPENMODE);
88 devctlret=devctl(cdfd, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
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 */
96 /* in the near future need to add more checks for splitting cdroms from other devices */
97 if ((atapi)&&(removable))
109 /* Add a CD-ROM drive to our list of valid drives */
110 static void AddDrive(char *drive, struct stat *stbuf)
114 if (SDL_numcds < MAX_DRIVES)
116 /* Check to make sure it's not already in our list.
117 This can happen when we see a drive via symbolic link. */
119 for (i=0; i<SDL_numcds; ++i)
121 if (stbuf->st_rdev == SDL_cdmode[i])
127 /* Add this drive to our list */
130 SDL_cdlist[i] = SDL_strdup(drive);
131 if (SDL_cdlist[i] == NULL)
136 SDL_cdmode[i] = stbuf->st_rdev;
141 int SDL_SYS_CDInit(void)
143 /* checklist: /dev/cdrom, /dev/cd?, /dev/scd? */
144 static char *checklist[]={"cdrom", "?0 cd?", "?1 cd?", "?0 scd?", NULL};
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;
163 /* clearing device open status */
164 for (i=0; i<MAX_DRIVES; i++)
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 )
173 char *cdpath, *delim;
174 size_t len = SDL_strlen(SDLcdrom)+1;
175 cdpath = SDL_stack_alloc(char, len);
178 SDL_strlcpy(cdpath, SDLcdrom, len);
181 delim = SDL_strchr(SDLcdrom, ':');
186 if (CheckDrive(SDLcdrom, &stbuf) > 0)
188 AddDrive(SDLcdrom, &stbuf);
199 SDL_stack_free(cdpath);
202 /* If we found our drives, there's nothing left to do */
209 /* Scan the system for CD-ROM drives */
210 for ( i=0; checklist[i]; ++i )
212 if (checklist[i][0] == '?')
217 for ( j=checklist[i][1]; exists; ++j )
219 SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
220 insert = SDL_strchr(drive, '?');
225 switch (CheckDrive(drive, &stbuf))
227 /* Drive exists and is a CD-ROM */
229 AddDrive(drive, &stbuf);
231 /* Drive exists, but isn't a CD-ROM */
234 /* Drive doesn't exist */
243 SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
244 if (CheckDrive(drive, &stbuf) > 0)
246 AddDrive(drive, &stbuf);
253 static const char *SDL_SYS_CDName(int drive)
255 return(SDL_cdlist[drive]);
258 static int SDL_SYS_CDOpen(int drive)
262 handle=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
266 SDL_cdopen[drive]=handle;
272 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
274 cdrom_read_toc_t toc;
278 if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL) == 0)
280 cdrom->numtracks = toc.last_track - toc.first_track + 1;
281 if (cdrom->numtracks > SDL_MAX_TRACKS)
283 cdrom->numtracks = SDL_MAX_TRACKS;
285 /* Read all the track TOC entries */
286 for (i=0; i<=cdrom->numtracks; ++i)
288 if (i == cdrom->numtracks)
290 cdrom->track[i].id = CDROM_LEADOUT;
294 cdrom->track[i].id = toc.first_track+i;
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;
303 cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
306 if (i == (cdrom->numtracks+1))
311 return (okay ? 0 : -1);
314 /* Get CD-ROM status */
315 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
319 cdrom_read_toc_t toc;
320 cdrom_subch_data_t info;
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);
334 if ((dinfo.flags & DEV_NO_MEDIA)!=0)
336 status = CD_TRAYEMPTY;
345 /* if media exists, then do other stuff */
347 SDL_memset(&info, 0x00, sizeof(info));
348 info.subch_command.data_format = CDROM_SUBCH_CURRENT_POSITION;
351 devctlret=devctl(cdrom->id, DCMD_CAM_CDROMSUBCHNL, &info, sizeof(info), NULL);
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. */
357 for (i=0; i<MAX_DRIVES; i++)
359 if (SDL_cdopen[i]==cdrom->id)
367 /* that cannot happen, but ... */
371 cdrom->id=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
374 if (devctlret==EAGAIN)
380 /* workaround for broken cdroms, which can return always EAGAIN when its not ready, */
381 /* that mean errornous media or just no media avail */
385 } while ((devctlret==EAGAIN)||(devctlret==ESTALE));
389 if (devctlret==ENXIO)
391 status = CD_TRAYEMPTY;
400 switch (info.current_position.header.audio_status)
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)
408 status = CD_TRAYEMPTY;
410 case CDROM_AUDIO_COMPLETED:
413 case CDROM_AUDIO_PLAY:
416 case CDROM_AUDIO_PAUSED:
417 /* Workaround buggy CD-ROM drive */
418 if (info.current_position.data_format == CDROM_LEADOUT)
435 if (status==CD_PLAYING || (status==CD_PAUSED))
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);
451 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
453 cdrom_playmsf_t playtime;
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);
458 if (devctl(cdrom->id, DCMD_CAM_CDROMPLAYMSF, &playtime, sizeof(playtime), NULL) != 0)
469 static int SDL_SYS_CDPause(SDL_CD *cdrom)
471 if (devctl(cdrom->id, DCMD_CAM_CDROMPAUSE, NULL, 0, NULL)!=0)
482 static int SDL_SYS_CDResume(SDL_CD *cdrom)
484 if (devctl(cdrom->id, DCMD_CAM_CDROMRESUME, NULL, 0, NULL)!=0)
495 static int SDL_SYS_CDStop(SDL_CD *cdrom)
497 if (devctl(cdrom->id, DCMD_CAM_CDROMSTOP, NULL, 0, NULL)!=0)
507 /* Eject the CD-ROM */
508 static int SDL_SYS_CDEject(SDL_CD *cdrom)
510 if (devctl(cdrom->id, DCMD_CAM_EJECT_MEDIA, NULL, 0, NULL)!=0)
520 /* Close the CD-ROM handle */
521 static void SDL_SYS_CDClose(SDL_CD *cdrom)
525 for (i=0; i<MAX_DRIVES; i++)
527 if (SDL_cdopen[i]==cdrom->id)
537 void SDL_SYS_CDQuit(void)
543 for (i=0; i<SDL_numcds; ++i)
545 SDL_free(SDL_cdlist[i]);
551 #endif /* SDL_CDROM_QNX */