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 Carsten Griwodz 20 griff (at) kom.tu-darmstadt.de 21 22 based on linux/SDL_syscdrom.c by Sam Lantinga 23 */ 24 #include "SDL_config.h" 25 26 #ifdef SDL_CDROM_AIX 27 28 /* Functions for system-level CD-ROM audio control */ 29 30 /*#define DEBUG_CDROM 1*/ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <unistd.h> 37 38 #include <sys/ioctl.h> 39 #include <sys/devinfo.h> 40 #include <sys/mntctl.h> 41 #include <sys/statfs.h> 42 #include <sys/vmount.h> 43 #include <fstab.h> 44 #include <sys/scdisk.h> 45 46 #include "SDL_cdrom.h" 47 #include "../SDL_syscdrom.h" 48 49 /* The maximum number of CD-ROM drives we'll detect */ 50 #define MAX_DRIVES 16 51 52 /* A list of available CD-ROM drives */ 53 static char *SDL_cdlist[MAX_DRIVES]; 54 static dev_t SDL_cdmode[MAX_DRIVES]; 55 56 /* The system-dependent CD control functions */ 57 static const char *SDL_SYS_CDName(int drive); 58 static int SDL_SYS_CDOpen(int drive); 59 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); 60 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); 61 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); 62 static int SDL_SYS_CDPause(SDL_CD *cdrom); 63 static int SDL_SYS_CDResume(SDL_CD *cdrom); 64 static int SDL_SYS_CDStop(SDL_CD *cdrom); 65 static int SDL_SYS_CDEject(SDL_CD *cdrom); 66 static void SDL_SYS_CDClose(SDL_CD *cdrom); 67 static int SDL_SYS_CDioctl(int id, int command, void *arg); 68 69 /* Check a drive to see if it is a CD-ROM */ 70 static int CheckDrive(char *drive, struct stat *stbuf) 71 { 72 int is_cd; 73 int cdfd; 74 int ret; 75 struct devinfo info; 76 77 /* If it doesn't exist, return -1 */ 78 if ( stat(drive, stbuf) < 0 ) { 79 return -1; 80 } 81 82 /* If it does exist, verify that it's an available CD-ROM */ 83 is_cd = 0; 84 if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) { 85 cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); 86 if ( cdfd >= 0 ) { 87 ret = SDL_SYS_CDioctl( cdfd, IOCINFO, &info ); 88 if ( ret < 0 ) { 89 /* Some kind of error */ 90 is_cd = 0; 91 } else { 92 if ( info.devtype == DD_CDROM ) { 93 is_cd = 1; 94 } else { 95 is_cd = 0; 96 } 97 } 98 close(cdfd); 99 } 100 #ifdef DEBUG_CDROM 101 else 102 { 103 fprintf(stderr, "Could not open drive %s (%s)\n", drive, strerror(errno)); 104 } 105 #endif 106 } 107 return is_cd; 108 } 109 110 /* Add a CD-ROM drive to our list of valid drives */ 111 static void AddDrive(char *drive, struct stat *stbuf) 112 { 113 int i; 114 115 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. 118 */ 119 for ( i=0; i<SDL_numcds; ++i ) { 120 if ( stbuf->st_rdev == SDL_cdmode[i] ) { 121 #ifdef DEBUG_CDROM 122 fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); 123 #endif 124 return; 125 } 126 } 127 128 /* Add this drive to our list */ 129 i = SDL_numcds; 130 SDL_cdlist[i] = SDL_strdup(drive); 131 if ( SDL_cdlist[i] == NULL ) { 132 SDL_OutOfMemory(); 133 return; 134 } 135 SDL_cdmode[i] = stbuf->st_rdev; 136 ++SDL_numcds; 137 #ifdef DEBUG_CDROM 138 fprintf(stderr, "Added CD-ROM drive: %s\n", drive); 139 #endif 140 } 141 } 142 143 static void CheckMounts() 144 { 145 char* buffer; 146 int bufsz; 147 struct vmount* ptr; 148 int ret; 149 150 buffer = (char*)SDL_malloc(10); 151 bufsz = 10; 152 if ( buffer==NULL ) 153 { 154 fprintf(stderr, "Could not allocate 10 bytes in aix/SDL_syscdrom.c:CheckMounts\n" ); 155 exit ( -10 ); 156 } 157 158 do 159 { 160 /* mntctrl() returns an array of all mounted filesystems */ 161 ret = mntctl ( MCTL_QUERY, bufsz, buffer ); 162 if ( ret == 0 ) 163 { 164 /* Buffer was too small, realloc. */ 165 bufsz = *(int*)buffer; /* Required size is in first word. */ 166 /* (whatever a word is in AIX 4.3.3) */ 167 /* int seems to be OK in 32bit mode. */ 168 SDL_free(buffer); 169 buffer = (char*)SDL_malloc(bufsz); 170 if ( buffer==NULL ) 171 { 172 fprintf(stderr, 173 "Could not allocate %d bytes in aix/SDL_syscdrom.c:CheckMounts\n", 174 bufsz ); 175 exit ( -10 ); 176 } 177 } 178 else if ( ret < 0 ) 179 { 180 #ifdef DEBUG_CDROM 181 fprintf(stderr, "Error reading vmount structures\n"); 182 #endif 183 return; 184 } 185 } 186 while ( ret == 0 ); 187 188 #ifdef DEBUG_CDROM 189 fprintf ( stderr, "Read %d vmount structures\n",ret ); 190 #endif 191 ptr = (struct vmount*)buffer; 192 do 193 { 194 switch(ptr->vmt_gfstype) 195 { 196 case MNT_CDROM : 197 { 198 struct stat stbuf; 199 char* text; 200 201 text = (char*)ptr + ptr->vmt_data[VMT_OBJECT].vmt_off; 202 #ifdef DEBUG_CDROM 203 fprintf(stderr, "Checking mount path: %s mounted on %s\n", 204 text, (char*)ptr + ptr->vmt_data[VMT_STUB].vmt_off ); 205 #endif 206 if ( CheckDrive( text, &stbuf) > 0) 207 { 208 AddDrive( text, &stbuf); 209 } 210 } 211 break; 212 default : 213 break; 214 } 215 ptr = (struct vmount*)((char*)ptr + ptr->vmt_length); 216 ret--; 217 } 218 while ( ret > 0 ); 219 220 free ( buffer ); 221 } 222 223 static int CheckNonmounts() 224 { 225 #ifdef _THREAD_SAFE 226 AFILE_t fsFile = NULL; 227 int passNo = 0; 228 int ret; 229 struct fstab entry; 230 struct stat stbuf; 231 232 ret = setfsent_r( &fsFile, &passNo ); 233 if ( ret != 0 ) return -1; 234 do 235 { 236 ret = getfsent_r ( &entry, &fsFile, &passNo ); 237 if ( ret == 0 ) { 238 char* l = SDL_strrchr(entry.fs_spec,'/'); 239 if ( l != NULL ) { 240 if ( !SDL_strncmp("cd",++l,2) ) { 241 #ifdef DEBUG_CDROM 242 fprintf(stderr, 243 "Found unmounted CD ROM drive with device name %s\n", 244 entry.fs_spec); 245 #endif 246 if ( CheckDrive( entry.fs_spec, &stbuf) > 0) 247 { 248 AddDrive( entry.fs_spec, &stbuf); 249 } 250 } 251 } 252 } 253 } 254 while ( ret == 0 ); 255 ret = endfsent_r ( &fsFile ); 256 if ( ret != 0 ) return -1; 257 return 0; 258 #else 259 struct fstab* entry; 260 struct stat stbuf; 261 262 setfsent(); 263 do 264 { 265 entry = getfsent(); 266 if ( entry != NULL ) { 267 char* l = SDL_strrchr(entry->fs_spec,'/'); 268 if ( l != NULL ) { 269 if ( !SDL_strncmp("cd",++l,2) ) { 270 #ifdef DEBUG_CDROM 271 fprintf(stderr,"Found unmounted CD ROM drive with device name %s", entry->fs_spec); 272 #endif 273 if ( CheckDrive( entry->fs_spec, &stbuf) > 0) 274 { 275 AddDrive( entry->fs_spec, &stbuf); 276 } 277 } 278 } 279 } 280 } 281 while ( entry != NULL ); 282 endfsent(); 283 #endif 284 } 285 286 int SDL_SYS_CDInit(void) 287 { 288 char *SDLcdrom; 289 struct stat stbuf; 290 291 /* Fill in our driver capabilities */ 292 SDL_CDcaps.Name = SDL_SYS_CDName; 293 SDL_CDcaps.Open = SDL_SYS_CDOpen; 294 SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; 295 SDL_CDcaps.Status = SDL_SYS_CDStatus; 296 SDL_CDcaps.Play = SDL_SYS_CDPlay; 297 SDL_CDcaps.Pause = SDL_SYS_CDPause; 298 SDL_CDcaps.Resume = SDL_SYS_CDResume; 299 SDL_CDcaps.Stop = SDL_SYS_CDStop; 300 SDL_CDcaps.Eject = SDL_SYS_CDEject; 301 SDL_CDcaps.Close = SDL_SYS_CDClose; 302 303 /* Look in the environment for our CD-ROM drive list */ 304 SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ 305 if ( SDLcdrom != NULL ) { 306 char *cdpath, *delim; 307 size_t len = SDL_strlen(SDLcdrom)+1; 308 cdpath = SDL_stack_alloc(char, len); 309 if ( cdpath != NULL ) { 310 SDL_strlcpy(cdpath, SDLcdrom, len); 311 SDLcdrom = cdpath; 312 do { 313 delim = SDL_strchr(SDLcdrom, ':'); 314 if ( delim ) { 315 *delim++ = '\0'; 316 } 317 #ifdef DEBUG_CDROM 318 fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom); 319 #endif 320 if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { 321 AddDrive(SDLcdrom, &stbuf); 322 } 323 if ( delim ) { 324 SDLcdrom = delim; 325 } else { 326 SDLcdrom = NULL; 327 } 328 } while ( SDLcdrom ); 329 SDL_stack_free(cdpath); 330 } 331 332 /* If we found our drives, there's nothing left to do */ 333 if ( SDL_numcds > 0 ) { 334 return(0); 335 } 336 } 337 338 CheckMounts(); 339 CheckNonmounts(); 340 341 return 0; 342 } 343 344 /* General ioctl() CD-ROM command function */ 345 static int SDL_SYS_CDioctl(int id, int command, void *arg) 346 { 347 int retval; 348 349 retval = ioctl(id, command, arg); 350 if ( retval < 0 ) { 351 SDL_SetError("ioctl() error: %s", strerror(errno)); 352 } 353 return retval; 354 } 355 356 static const char *SDL_SYS_CDName(int drive) 357 { 358 return(SDL_cdlist[drive]); 359 } 360 361 static int SDL_SYS_CDOpen(int drive) 362 { 363 int fd; 364 char* lastsl; 365 char* cdromname; 366 size_t len; 367 368 /* 369 * We found /dev/cd? drives and that is in our list. But we can 370 * open only the /dev/rcd? versions of those devices for Audio CD. 371 */ 372 len = SDL_strlen(SDL_cdlist[drive])+2; 373 cdromname = (char*)SDL_malloc(len); 374 SDL_strlcpy(cdromname,SDL_cdlist[drive],len); 375 lastsl = SDL_strrchr(cdromname,'/'); 376 if (lastsl) { 377 *lastsl = 0; 378 SDL_strlcat(cdromname,"/r",len); 379 lastsl = SDL_strrchr(SDL_cdlist[drive],'/'); 380 if (lastsl) { 381 lastsl++; 382 SDL_strlcat(cdromname,lastsl,len); 383 } 384 } 385 386 #ifdef DEBUG_CDROM 387 fprintf(stderr, "Should open drive %s, opening %s\n", SDL_cdlist[drive], cdromname); 388 #endif 389 390 /* 391 * Use exclusive access. Don't use SC_DIAGNOSTICS as xmcd does because they 392 * require root priviledges, and we don't want that. SC_SINGLE provides 393 * exclusive access with less trouble. 394 */ 395 fd = openx(cdromname, O_RDONLY, NULL, SC_SINGLE); 396 if ( fd < 0 ) 397 { 398 #ifdef DEBUG_CDROM 399 fprintf(stderr, "Could not open drive %s (%s)\n", cdromname, strerror(errno)); 400 #endif 401 } 402 else 403 { 404 struct mode_form_op cdMode; 405 int ret; 406 #ifdef DEBUG_CDROM 407 cdMode.action = CD_GET_MODE; 408 ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode); 409 if ( ret < 0 ) { 410 fprintf(stderr, 411 "Could not get drive mode for %s (%s)\n", 412 cdromname, strerror(errno)); 413 } else { 414 switch(cdMode.cd_mode_form) { 415 case CD_MODE1 : 416 fprintf(stderr, 417 "Drive mode for %s is %s\n", 418 cdromname, "CD-ROM Data Mode 1"); 419 break; 420 case CD_MODE2_FORM1 : 421 fprintf(stderr, 422 "Drive mode for %s is %s\n", 423 cdromname, "CD-ROM XA Data Mode 2 Form 1"); 424 break; 425 case CD_MODE2_FORM2 : 426 fprintf(stderr, 427 "Drive mode for %s is %s\n", 428 cdromname, "CD-ROM XA Data Mode 2 Form 2"); 429 break; 430 case CD_DA : 431 fprintf(stderr, 432 "Drive mode for %s is %s\n", 433 cdromname, "CD-DA"); 434 break; 435 default : 436 fprintf(stderr, 437 "Drive mode for %s is %s\n", 438 cdromname, "unknown"); 439 break; 440 } 441 } 442 #endif 443 444 cdMode.action = CD_CHG_MODE; 445 cdMode.cd_mode_form = CD_DA; 446 ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode); 447 if ( ret < 0 ) { 448 #ifdef DEBUG_CDROM 449 fprintf(stderr, 450 "Could not set drive mode for %s (%s)\n", 451 cdromname, strerror(errno)); 452 #endif 453 SDL_SetError("ioctl() error: Could not set CD drive mode, %s", 454 strerror(errno)); 455 } else { 456 #ifdef DEBUG_CDROM 457 fprintf(stderr, 458 "Drive mode for %s set to CD_DA\n", 459 cdromname); 460 #endif 461 } 462 } 463 SDL_free(cdromname); 464 return fd; 465 } 466 467 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) 468 { 469 struct cd_audio_cmd cmd; 470 struct cd_audio_cmd entry; 471 int i; 472 int okay; 473 474 cmd.audio_cmds = CD_TRK_INFO_AUDIO; 475 cmd.msf_flag = FALSE; 476 if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) { 477 return -1; 478 } 479 480 okay = 0; 481 cdrom->numtracks = cmd.indexing.track_index.last_track 482 - cmd.indexing.track_index.first_track+1; 483 if ( cdrom->numtracks > SDL_MAX_TRACKS ) { 484 cdrom->numtracks = SDL_MAX_TRACKS; 485 } 486 487 /* Read all the track TOC entries */ 488 for ( i=0; i<=cdrom->numtracks; ++i ) { 489 if ( i == cdrom->numtracks ) { 490 cdrom->track[i].id = 0xAA;; 491 } else { 492 cdrom->track[i].id = cmd.indexing.track_index.first_track+i; 493 } 494 entry.audio_cmds = CD_GET_TRK_MSF; 495 entry.indexing.track_msf.track = cdrom->track[i].id; 496 if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &entry) < 0 ) { 497 break; 498 } else { 499 cdrom->track[i].type = 0; /* don't know how to detect 0x04 data track */ 500 cdrom->track[i].offset = MSF_TO_FRAMES( 501 entry.indexing.track_msf.mins, 502 entry.indexing.track_msf.secs, 503 entry.indexing.track_msf.frames); 504 cdrom->track[i].length = 0; 505 if ( i > 0 ) { 506 cdrom->track[i-1].length = cdrom->track[i].offset 507 - cdrom->track[i-1].offset; 508 } 509 } 510 } 511 if ( i == (cdrom->numtracks+1) ) { 512 okay = 1; 513 } 514 return(okay ? 0 : -1); 515 } 516 517 /* Get CD-ROM status */ 518 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) 519 { 520 CDstatus status; 521 struct cd_audio_cmd cmd; 522 cmd.audio_cmds = CD_INFO_AUDIO; 523 524 if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) { 525 #ifdef DEBUG_CDROM 526 fprintf(stderr, "ioctl failed in SDL_SYS_CDStatus (%s)\n", SDL_GetError()); 527 #endif 528 status = CD_ERROR; 529 } else { 530 switch (cmd.status) { 531 case CD_NO_AUDIO: 532 case CD_COMPLETED: 533 status = CD_STOPPED; 534 break; 535 case CD_PLAY_AUDIO: 536 status = CD_PLAYING; 537 break; 538 case CD_PAUSE_AUDIO: 539 status = CD_PAUSED; 540 break; 541 case CD_NOT_VALID: 542 #ifdef DEBUG_CDROM 543 fprintf(stderr, "cdStatus failed with CD_NOT_VALID\n"); 544 #endif 545 status = CD_ERROR; 546 break; 547 case CD_STATUS_ERROR: 548 #ifdef DEBUG_CDROM 549 fprintf(stderr, "cdStatus failed with CD_STATUS_ERROR\n"); 550 #endif 551 status = CD_ERROR; 552 break; 553 default: 554 #ifdef DEBUG_CDROM 555 fprintf(stderr, "cdStatus failed with unknown error\n"); 556 #endif 557 status = CD_ERROR; 558 break; 559 } 560 } 561 if ( position ) { 562 if ( status == CD_PLAYING || (status == CD_PAUSED) ) { 563 *position = MSF_TO_FRAMES( cmd.indexing.info_audio.current_mins, 564 cmd.indexing.info_audio.current_secs, 565 cmd.indexing.info_audio.current_frames); 566 } else { 567 *position = 0; 568 } 569 } 570 return status; 571 } 572 573 /* Start play */ 574 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) 575 { 576 struct cd_audio_cmd cmd; 577 578 /* 579 * My CD Rom is muted by default. I think I read that this is new with 580 * AIX 4.3. SDL does not change the volume, so I need a kludge. Maybe 581 * its better to do this elsewhere? 582 */ 583 cmd.audio_cmds = CD_PLAY_AUDIO | CD_SET_VOLUME; 584 cmd.msf_flag = TRUE; 585 FRAMES_TO_MSF(start, 586 &cmd.indexing.msf.first_mins, 587 &cmd.indexing.msf.first_secs, 588 &cmd.indexing.msf.first_frames); 589 FRAMES_TO_MSF(start+length, 590 &cmd.indexing.msf.last_mins, 591 &cmd.indexing.msf.last_secs, 592 &cmd.indexing.msf.last_frames); 593 cmd.volume_type = CD_VOLUME_ALL; 594 cmd.all_channel_vol = 255; /* This is a uchar. What is a good value? No docu! */ 595 cmd.out_port_0_sel = CD_AUDIO_CHNL_0; 596 cmd.out_port_1_sel = CD_AUDIO_CHNL_1; 597 cmd.out_port_2_sel = CD_AUDIO_CHNL_2; 598 cmd.out_port_3_sel = CD_AUDIO_CHNL_3; 599 600 #ifdef DEBUG_CDROM 601 fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", 602 cmd.indexing.msf.first_mins, 603 cmd.indexing.msf.first_secs, 604 cmd.indexing.msf.first_frames, 605 cmd.indexing.msf.last_mins, 606 cmd.indexing.msf.last_secs, 607 cmd.indexing.msf.last_frames); 608 #endif 609 return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); 610 } 611 612 /* Pause play */ 613 static int SDL_SYS_CDPause(SDL_CD *cdrom) 614 { 615 struct cd_audio_cmd cmd; 616 cmd.audio_cmds = CD_PAUSE_AUDIO; 617 return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); 618 } 619 620 /* Resume play */ 621 static int SDL_SYS_CDResume(SDL_CD *cdrom) 622 { 623 struct cd_audio_cmd cmd; 624 cmd.audio_cmds = CD_RESUME_AUDIO; 625 return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); 626 } 627 628 /* Stop play */ 629 static int SDL_SYS_CDStop(SDL_CD *cdrom) 630 { 631 struct cd_audio_cmd cmd; 632 cmd.audio_cmds = CD_STOP_AUDIO; 633 return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); 634 } 635 636 /* Eject the CD-ROM */ 637 static int SDL_SYS_CDEject(SDL_CD *cdrom) 638 { 639 return(SDL_SYS_CDioctl(cdrom->id, DKEJECT, 0)); 640 } 641 642 /* Close the CD-ROM handle */ 643 static void SDL_SYS_CDClose(SDL_CD *cdrom) 644 { 645 close(cdrom->id); 646 } 647 648 void SDL_SYS_CDQuit(void) 649 { 650 int i; 651 652 if ( SDL_numcds > 0 ) { 653 for ( i=0; i<SDL_numcds; ++i ) { 654 SDL_free(SDL_cdlist[i]); 655 } 656 SDL_numcds = 0; 657 } 658 } 659 660 #endif /* SDL_CDROM_AIX */ 661