src/cdrom/linux/SDL_syscdrom.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. ERRNO_TRAYEMPTY
  2. CheckDrive
  3. AddDrive
  4. CheckMounts
  5. SDL_SYS_CDInit
  6. SDL_SYS_CDioctl
  7. SDL_SYS_CDName
  8. SDL_SYS_CDOpen
  9. SDL_SYS_CDGetTOC
  10. SDL_SYS_CDStatus
  11. SDL_SYS_CDPlay
  12. SDL_SYS_CDPause
  13. SDL_SYS_CDResume
  14. SDL_SYS_CDStop
  15. SDL_SYS_CDEject
  16. SDL_SYS_CDClose
  17. SDL_SYS_CDQuit

   1 /*
   2     SDL - Simple DirectMedia Layer
   3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
   4 
   5     This library is free software; you can redistribute it and/or
   6     modify it under the terms of the GNU Library General Public
   7     License as published by the Free Software Foundation; either
   8     version 2 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     Library General Public License for more details.
  14 
  15     You should have received a copy of the GNU Library General Public
  16     License along with this library; if not, write to the Free
  17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18 
  19     Sam Lantinga
  20     slouken@devolution.com
  21 */
  22 
  23 #ifdef SAVE_RCSID
  24 static char rcsid =
  25  "@(#) $Id: SDL_syscdrom.c,v 1.2.2.8 2001/02/28 12:06:11 hercules Exp $";
  26 #endif
  27 
  28 /* Functions for system-level CD-ROM audio control */
  29 
  30 #include <sys/types.h>
  31 #include <stdlib.h>
  32 #include <sys/stat.h>
  33 #include <sys/ioctl.h>
  34 #include <fcntl.h>
  35 #include <stdio.h>
  36 #include <string.h>
  37 #include <errno.h>
  38 #include <unistd.h>
  39 #ifdef __linux__
  40 #include <linux/cdrom.h>
  41 #endif
  42 #ifdef __SVR4
  43 #include <sys/cdio.h>
  44 #endif
  45 
  46 /* Define this to use the alternative getmntent() code */
  47 #ifndef __SVR4
  48 #define USE_MNTENT
  49 #endif
  50 
  51 #ifdef USE_MNTENT
  52 #if defined(__USLC__)
  53 #include <sys/mntent.h>
  54 #else
  55 #include <mntent.h>
  56 #endif
  57 
  58 #ifndef _PATH_MNTTAB
  59 #ifdef MNTTAB
  60 #define _PATH_MNTTAB    MNTTAB
  61 #else
  62 #define _PATH_MNTTAB    "/etc/fstab"
  63 #endif
  64 #endif /* !_PATH_MNTTAB */
  65 
  66 #ifndef _PATH_MOUNTED
  67 #define _PATH_MOUNTED   "/etc/mtab"
  68 #endif /* !_PATH_MOUNTED */
  69 
  70 #ifndef MNTTYPE_CDROM
  71 #define MNTTYPE_CDROM   "iso9660"
  72 #endif
  73 #ifndef MNTTYPE_SUPER
  74 #define MNTTYPE_SUPER   "supermount"
  75 #endif
  76 #endif /* USE_MNTENT */
  77 
  78 #include "SDL_error.h"
  79 #include "SDL_cdrom.h"
  80 #include "SDL_syscdrom.h"
  81 
  82 
  83 /* The maximum number of CD-ROM drives we'll detect */
  84 #define MAX_DRIVES      16      
  85 
  86 /* A list of available CD-ROM drives */
  87 static char *SDL_cdlist[MAX_DRIVES];
  88 static dev_t SDL_cdmode[MAX_DRIVES];
  89 
  90 /* The system-dependent CD control functions */
  91 static const char *SDL_SYS_CDName(int drive);
  92 static int SDL_SYS_CDOpen(int drive);
  93 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
  94 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
  95 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
  96 static int SDL_SYS_CDPause(SDL_CD *cdrom);
  97 static int SDL_SYS_CDResume(SDL_CD *cdrom);
  98 static int SDL_SYS_CDStop(SDL_CD *cdrom);
  99 static int SDL_SYS_CDEject(SDL_CD *cdrom);
 100 static void SDL_SYS_CDClose(SDL_CD *cdrom);
 101 
 102 /* Some ioctl() errno values which occur when the tray is empty */
 103 #define ERRNO_TRAYEMPTY(errno)  \
     /* [<][>][^][v][top][bottom][index][help] */
 104         ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL))
 105 
 106 /* Check a drive to see if it is a CD-ROM */
 107 static int CheckDrive(char *drive, char *mnttype, struct stat *stbuf)
     /* [<][>][^][v][top][bottom][index][help] */
 108 {
 109         int is_cd, cdfd;
 110         struct cdrom_subchnl info;
 111 
 112         /* If it doesn't exist, return -1 */
 113         if ( stat(drive, stbuf) < 0 ) {
 114                 return(-1);
 115         }
 116 
 117         /* If it does exist, verify that it's an available CD-ROM */
 118         is_cd = 0;
 119         if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
 120                 cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
 121                 if ( cdfd >= 0 ) {
 122                         info.cdsc_format = CDROM_MSF;
 123                         /* Under Linux, EIO occurs when a disk is not present.
 124                          */
 125                         if ( (ioctl(cdfd, CDROMSUBCHNL, &info) == 0) ||
 126                                                 ERRNO_TRAYEMPTY(errno) ) {
 127                                 is_cd = 1;
 128                         }
 129                         close(cdfd);
 130                 }
 131 #ifdef USE_MNTENT
 132                 /* Even if we can't read it, it might be mounted */
 133                 else if ( mnttype && (strcmp(mnttype, MNTTYPE_CDROM) == 0) ) {
 134                         is_cd = 1;
 135                 }
 136 #endif
 137         }
 138         return(is_cd);
 139 }
 140 
 141 /* Add a CD-ROM drive to our list of valid drives */
 142 static void AddDrive(char *drive, struct stat *stbuf)
     /* [<][>][^][v][top][bottom][index][help] */
 143 {
 144         int i;
 145 
 146         if ( SDL_numcds < MAX_DRIVES ) {
 147                 /* Check to make sure it's not already in our list.
 148                    This can happen when we see a drive via symbolic link.
 149                  */
 150                 for ( i=0; i<SDL_numcds; ++i ) {
 151                         if ( stbuf->st_rdev == SDL_cdmode[i] ) {
 152 #ifdef DEBUG_CDROM
 153   fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
 154 #endif
 155                                 return;
 156                         }
 157                 }
 158 
 159                 /* Add this drive to our list */
 160                 i = SDL_numcds;
 161                 SDL_cdlist[i] = (char *)malloc(strlen(drive)+1);
 162                 if ( SDL_cdlist[i] == NULL ) {
 163                         SDL_OutOfMemory();
 164                         return;
 165                 }
 166                 strcpy(SDL_cdlist[i], drive);
 167                 SDL_cdmode[i] = stbuf->st_rdev;
 168                 ++SDL_numcds;
 169 #ifdef DEBUG_CDROM
 170   fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
 171 #endif
 172         }
 173 }
 174 
 175 #ifdef USE_MNTENT
 176 static void CheckMounts(const char *mtab)
     /* [<][>][^][v][top][bottom][index][help] */
 177 {
 178         FILE *mntfp;
 179         struct mntent *mntent;
 180         struct stat stbuf;
 181 
 182         mntfp = setmntent(mtab, "r");
 183         if ( mntfp != NULL ) {
 184                 char *tmp, mnt_type[32], mnt_dev[1024];
 185 
 186                 while ( (mntent=getmntent(mntfp)) != NULL ) {
 187                         /* Warning, possible buffer overflow.. */
 188                         strcpy(mnt_type, mntent->mnt_type);
 189                         strcpy(mnt_dev, mntent->mnt_fsname);
 190 
 191                         /* Handle "supermount" filesystem mounts */
 192                         if ( strcmp(mnt_type, MNTTYPE_SUPER) == 0 ) {
 193                                 tmp = strstr(mntent->mnt_opts, "fs=");
 194                                 if ( tmp ) {
 195                                         strcpy(mnt_type, tmp+strlen("fs="));
 196                                         tmp = strchr(mnt_type, ',');
 197                                         if ( tmp ) {
 198                                                 *tmp = '\0';
 199                                         }
 200                                 }
 201                                 tmp = strstr(mntent->mnt_opts, "dev=");
 202                                 if ( tmp ) {
 203                                         strcpy(mnt_dev, tmp+strlen("dev="));
 204                                         tmp = strchr(mnt_dev, ',');
 205                                         if ( tmp ) {
 206                                                 *tmp = '\0';
 207                                         }
 208                                 }
 209                         }
 210                         if ( strcmp(mnt_type, MNTTYPE_CDROM) == 0 ) {
 211 #ifdef DEBUG_CDROM
 212   fprintf(stderr, "Checking mount path from %s: %s mounted on %s of %s\n",
 213         mtab, mnt_dev, mntent->mnt_dir, mnt_type);
 214 #endif
 215                                 if (CheckDrive(mnt_dev, mnt_type, &stbuf) > 0) {
 216                                         AddDrive(mnt_dev, &stbuf);
 217                                 }
 218                         }
 219                 }
 220                 endmntent(mntfp);
 221         }
 222 }
 223 #endif /* USE_MNTENT */
 224 
 225 int  SDL_SYS_CDInit(void)
     /* [<][>][^][v][top][bottom][index][help] */
 226 {
 227         /* checklist: /dev/cdrom, /dev/hd?, /dev/scd? /dev/sr? */
 228         static char *checklist[] = {
 229                 "cdrom", "?a hd?", "?0 scd?", "?0 sr?", NULL
 230         };
 231         char *SDLcdrom;
 232         int i, j, exists;
 233         char drive[32];
 234         struct stat stbuf;
 235 
 236         /* Fill in our driver capabilities */
 237         SDL_CDcaps.Name = SDL_SYS_CDName;
 238         SDL_CDcaps.Open = SDL_SYS_CDOpen;
 239         SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
 240         SDL_CDcaps.Status = SDL_SYS_CDStatus;
 241         SDL_CDcaps.Play = SDL_SYS_CDPlay;
 242         SDL_CDcaps.Pause = SDL_SYS_CDPause;
 243         SDL_CDcaps.Resume = SDL_SYS_CDResume;
 244         SDL_CDcaps.Stop = SDL_SYS_CDStop;
 245         SDL_CDcaps.Eject = SDL_SYS_CDEject;
 246         SDL_CDcaps.Close = SDL_SYS_CDClose;
 247 
 248         /* Look in the environment for our CD-ROM drive list */
 249         SDLcdrom = getenv("SDL_CDROM"); /* ':' separated list of devices */
 250         if ( SDLcdrom != NULL ) {
 251                 char *cdpath, *delim;
 252                 cdpath = malloc(strlen(SDLcdrom)+1);
 253                 if ( cdpath != NULL ) {
 254                         strcpy(cdpath, SDLcdrom);
 255                         SDLcdrom = cdpath;
 256                         do {
 257                                 delim = strchr(SDLcdrom, ':');
 258                                 if ( delim ) {
 259                                         *delim++ = '\0';
 260                                 }
 261 #ifdef DEBUG_CDROM
 262   fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom);
 263 #endif
 264                                 if ( CheckDrive(SDLcdrom, NULL, &stbuf) > 0 ) {
 265                                         AddDrive(SDLcdrom, &stbuf);
 266                                 }
 267                                 if ( delim ) {
 268                                         SDLcdrom = delim;
 269                                 } else {
 270                                         SDLcdrom = NULL;
 271                                 }
 272                         } while ( SDLcdrom );
 273                         free(cdpath);
 274                 }
 275 
 276                 /* If we found our drives, there's nothing left to do */
 277                 if ( SDL_numcds > 0 ) {
 278                         return(0);
 279                 }
 280         }
 281 
 282 #ifdef USE_MNTENT
 283         /* Check /dev/cdrom first :-) */
 284         if (CheckDrive("/dev/cdrom", NULL, &stbuf) > 0) {
 285                 AddDrive("/dev/cdrom", &stbuf);
 286         }
 287 
 288         /* Now check the currently mounted CD drives */
 289         CheckMounts(_PATH_MOUNTED);
 290 
 291         /* Finally check possible mountable drives in /etc/fstab */
 292         CheckMounts(_PATH_MNTTAB);
 293 
 294         /* If we found our drives, there's nothing left to do */
 295         if ( SDL_numcds > 0 ) {
 296                 return(0);
 297         }
 298 #endif /* USE_MNTENT */
 299 
 300         /* Scan the system for CD-ROM drives.
 301            Not always 100% reliable, so use the USE_MNTENT code above first.
 302          */
 303         for ( i=0; checklist[i]; ++i ) {
 304                 if ( checklist[i][0] == '?' ) {
 305                         char *insert;
 306                         exists = 1;
 307                         for ( j=checklist[i][1]; exists; ++j ) {
 308                                 sprintf(drive, "/dev/%s", &checklist[i][3]);
 309                                 insert = strchr(drive, '?');
 310                                 if ( insert != NULL ) {
 311                                         *insert = j;
 312                                 }
 313 #ifdef DEBUG_CDROM
 314   fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive);
 315 #endif
 316                                 switch (CheckDrive(drive, NULL, &stbuf)) {
 317                                         /* Drive exists and is a CD-ROM */
 318                                         case 1:
 319                                                 AddDrive(drive, &stbuf);
 320                                                 break;
 321                                         /* Drive exists, but isn't a CD-ROM */
 322                                         case 0:
 323                                                 break;
 324                                         /* Drive doesn't exist */
 325                                         case -1:
 326                                                 exists = 0;
 327                                                 break;
 328                                 }
 329                         }
 330                 } else {
 331                         sprintf(drive, "/dev/%s", checklist[i]);
 332 #ifdef DEBUG_CDROM
 333   fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive);
 334 #endif
 335                         if ( CheckDrive(drive, NULL, &stbuf) > 0 ) {
 336                                 AddDrive(drive, &stbuf);
 337                         }
 338                 }
 339         }
 340         return(0);
 341 }
 342 
 343 /* General ioctl() CD-ROM command function */
 344 static int SDL_SYS_CDioctl(int id, int command, void *arg)
     /* [<][>][^][v][top][bottom][index][help] */
 345 {
 346         int retval;
 347 
 348         retval = ioctl(id, command, arg);
 349         if ( retval < 0 ) {
 350                 SDL_SetError("ioctl() error: %s", strerror(errno));
 351         }
 352         return(retval);
 353 }
 354 
 355 static const char *SDL_SYS_CDName(int drive)
     /* [<][>][^][v][top][bottom][index][help] */
 356 {
 357         return(SDL_cdlist[drive]);
 358 }
 359 
 360 static int SDL_SYS_CDOpen(int drive)
     /* [<][>][^][v][top][bottom][index][help] */
 361 {
 362         return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0));
 363 }
 364 
 365 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
     /* [<][>][^][v][top][bottom][index][help] */
 366 {
 367         struct cdrom_tochdr toc;
 368         int i, okay;
 369         struct cdrom_tocentry entry;
 370 
 371         okay = 0;
 372         if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc) == 0 ) {
 373                 cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1;
 374                 if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
 375                         cdrom->numtracks = SDL_MAX_TRACKS;
 376                 }
 377                 /* Read all the track TOC entries */
 378                 for ( i=0; i<=cdrom->numtracks; ++i ) {
 379                         if ( i == cdrom->numtracks ) {
 380                                 cdrom->track[i].id = CDROM_LEADOUT;
 381                         } else {
 382                                 cdrom->track[i].id = toc.cdth_trk0+i;
 383                         }
 384                         entry.cdte_track = cdrom->track[i].id;
 385                         entry.cdte_format = CDROM_MSF;
 386                         if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY,
 387                                                                 &entry) < 0 ) {
 388                                 break;
 389                         } else {
 390                                 if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) {
 391                                         cdrom->track[i].type = SDL_DATA_TRACK;
 392                                 } else {
 393                                         cdrom->track[i].type = SDL_AUDIO_TRACK;
 394                                 }
 395                                 cdrom->track[i].offset = MSF_TO_FRAMES(
 396                                                 entry.cdte_addr.msf.minute,
 397                                                 entry.cdte_addr.msf.second,
 398                                                 entry.cdte_addr.msf.frame);
 399                                 cdrom->track[i].length = 0;
 400                                 if ( i > 0 ) {
 401                                         cdrom->track[i-1].length =
 402                                                 cdrom->track[i].offset-
 403                                                 cdrom->track[i-1].offset;
 404                                 }
 405                         }
 406                 }
 407                 if ( i == (cdrom->numtracks+1) ) {
 408                         okay = 1;
 409                 }
 410         }
 411         return(okay ? 0 : -1);
 412 }
 413 
 414 /* Get CD-ROM status */
 415 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
     /* [<][>][^][v][top][bottom][index][help] */
 416 {
 417         CDstatus status;
 418         struct cdrom_tochdr toc;
 419         struct cdrom_subchnl info;
 420 
 421         info.cdsc_format = CDROM_MSF;
 422         if ( ioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) {
 423                 if ( ERRNO_TRAYEMPTY(errno) ) {
 424                         status = CD_TRAYEMPTY;
 425                 } else {
 426                         status = CD_ERROR;
 427                 }
 428         } else {
 429                 switch (info.cdsc_audiostatus) {
 430                         case CDROM_AUDIO_INVALID:
 431                         case CDROM_AUDIO_NO_STATUS:
 432                                 /* Try to determine if there's a CD available */
 433                                 if (ioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0)
 434                                         status = CD_STOPPED;
 435                                 else
 436                                         status = CD_TRAYEMPTY;
 437                                 break;
 438                         case CDROM_AUDIO_COMPLETED:
 439                                 status = CD_STOPPED;
 440                                 break;
 441                         case CDROM_AUDIO_PLAY:
 442                                 status = CD_PLAYING;
 443                                 break;
 444                         case CDROM_AUDIO_PAUSED:
 445                                 /* Workaround buggy CD-ROM drive */
 446                                 if ( info.cdsc_trk == CDROM_LEADOUT ) {
 447                                         status = CD_STOPPED;
 448                                 } else {
 449                                         status = CD_PAUSED;
 450                                 }
 451                                 break;
 452                         default:
 453                                 status = CD_ERROR;
 454                                 break;
 455                 }
 456         }
 457         if ( position ) {
 458                 if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
 459                         *position = MSF_TO_FRAMES(
 460                                         info.cdsc_absaddr.msf.minute,
 461                                         info.cdsc_absaddr.msf.second,
 462                                         info.cdsc_absaddr.msf.frame);
 463                 } else {
 464                         *position = 0;
 465                 }
 466         }
 467         return(status);
 468 }
 469 
 470 /* Start play */
 471 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
     /* [<][>][^][v][top][bottom][index][help] */
 472 {
 473         struct cdrom_msf playtime;
 474 
 475         FRAMES_TO_MSF(start,
 476            &playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0);
 477         FRAMES_TO_MSF(start+length,
 478            &playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1);
 479 #ifdef DEBUG_CDROM
 480   fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
 481         playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
 482         playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
 483 #endif
 484         return(SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime));
 485 }
 486 
 487 /* Pause play */
 488 static int SDL_SYS_CDPause(SDL_CD *cdrom)
     /* [<][>][^][v][top][bottom][index][help] */
 489 {
 490         return(SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0));
 491 }
 492 
 493 /* Resume play */
 494 static int SDL_SYS_CDResume(SDL_CD *cdrom)
     /* [<][>][^][v][top][bottom][index][help] */
 495 {
 496         return(SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0));
 497 }
 498 
 499 /* Stop play */
 500 static int SDL_SYS_CDStop(SDL_CD *cdrom)
     /* [<][>][^][v][top][bottom][index][help] */
 501 {
 502         return(SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0));
 503 }
 504 
 505 /* Eject the CD-ROM */
 506 static int SDL_SYS_CDEject(SDL_CD *cdrom)
     /* [<][>][^][v][top][bottom][index][help] */
 507 {
 508         return(SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0));
 509 }
 510 
 511 /* Close the CD-ROM handle */
 512 static void SDL_SYS_CDClose(SDL_CD *cdrom)
     /* [<][>][^][v][top][bottom][index][help] */
 513 {
 514         close(cdrom->id);
 515 }
 516 
 517 void SDL_SYS_CDQuit(void)
     /* [<][>][^][v][top][bottom][index][help] */
 518 {
 519         int i;
 520 
 521         if ( SDL_numcds > 0 ) {
 522                 for ( i=0; i<SDL_numcds; ++i ) {
 523                         free(SDL_cdlist[i]);
 524                 }
 525                 SDL_numcds = 0;
 526         }
 527 }
 528 

/* [<][>][^][v][top][bottom][index][help] */