1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2006 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 (at) 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 */ 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 */ 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 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 253 static const char *SDL_SYS_CDName(int drive) 254 { 255 return(SDL_cdlist[drive]); 256 } 257 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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