src/cdrom/freebsd/SDL_syscdrom.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
permissions -rw-r--r--
more tweaking indent options
     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@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #ifdef SDL_CDROM_FREEBSD
    25 
    26 /* Functions for system-level CD-ROM audio control */
    27 
    28 #include <sys/types.h>
    29 #include <sys/stat.h>
    30 #include <fcntl.h>
    31 #include <errno.h>
    32 #include <unistd.h>
    33 #include <sys/cdio.h>
    34 
    35 #include "SDL_cdrom.h"
    36 #include "../SDL_syscdrom.h"
    37 
    38 
    39 /* The maximum number of CD-ROM drives we'll detect */
    40 #define MAX_DRIVES	16
    41 
    42 /* A list of available CD-ROM drives */
    43 static char *SDL_cdlist[MAX_DRIVES];
    44 static dev_t SDL_cdmode[MAX_DRIVES];
    45 
    46 /* The system-dependent CD control functions */
    47 static const char *SDL_SYS_CDName(int drive);
    48 static int SDL_SYS_CDOpen(int drive);
    49 static int SDL_SYS_CDGetTOC(SDL_CD * cdrom);
    50 static CDstatus SDL_SYS_CDStatus(SDL_CD * cdrom, int *position);
    51 static int SDL_SYS_CDPlay(SDL_CD * cdrom, int start, int length);
    52 static int SDL_SYS_CDPause(SDL_CD * cdrom);
    53 static int SDL_SYS_CDResume(SDL_CD * cdrom);
    54 static int SDL_SYS_CDStop(SDL_CD * cdrom);
    55 static int SDL_SYS_CDEject(SDL_CD * cdrom);
    56 static void SDL_SYS_CDClose(SDL_CD * cdrom);
    57 
    58 /* Some ioctl() errno values which occur when the tray is empty */
    59 #define ERRNO_TRAYEMPTY(errno)	\
    60 	((errno == EIO) || (errno == ENOENT) || (errno == EINVAL))
    61 
    62 /* Check a drive to see if it is a CD-ROM */
    63 static int
    64 CheckDrive(char *drive, struct stat *stbuf)
    65 {
    66     int is_cd, cdfd;
    67     struct ioc_read_subchannel info;
    68 
    69     /* If it doesn't exist, return -1 */
    70     if (stat(drive, stbuf) < 0) {
    71         return (-1);
    72     }
    73 
    74     /* If it does exist, verify that it's an available CD-ROM */
    75     is_cd = 0;
    76     if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode)) {
    77         cdfd = open(drive, (O_RDONLY | O_EXCL | O_NONBLOCK), 0);
    78         if (cdfd >= 0) {
    79             info.address_format = CD_MSF_FORMAT;
    80             info.data_format = CD_CURRENT_POSITION;
    81             info.data_len = 0;
    82             info.data = NULL;
    83             /* Under Linux, EIO occurs when a disk is not present.
    84                This isn't 100% reliable, so we use the USE_MNTENT
    85                code above instead.
    86              */
    87             if ((ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) ||
    88                 ERRNO_TRAYEMPTY(errno)) {
    89                 is_cd = 1;
    90             }
    91             close(cdfd);
    92         }
    93     }
    94     return (is_cd);
    95 }
    96 
    97 /* Add a CD-ROM drive to our list of valid drives */
    98 static void
    99 AddDrive(char *drive, struct stat *stbuf)
   100 {
   101     int i;
   102 
   103     if (SDL_numcds < MAX_DRIVES) {
   104         /* Check to make sure it's not already in our list.
   105            This can happen when we see a drive via symbolic link.
   106          */
   107         for (i = 0; i < SDL_numcds; ++i) {
   108             if (stbuf->st_rdev == SDL_cdmode[i]) {
   109 #ifdef DEBUG_CDROM
   110                 fprintf(stderr, "Duplicate drive detected: %s == %s\n",
   111                         drive, SDL_cdlist[i]);
   112 #endif
   113                 return;
   114             }
   115         }
   116 
   117         /* Add this drive to our list */
   118         i = SDL_numcds;
   119         SDL_cdlist[i] = SDL_strdup(drive);
   120         if (SDL_cdlist[i] == NULL) {
   121             SDL_OutOfMemory();
   122             return;
   123         }
   124         SDL_cdmode[i] = stbuf->st_rdev;
   125         ++SDL_numcds;
   126 #ifdef DEBUG_CDROM
   127         fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
   128 #endif
   129     }
   130 }
   131 
   132 int
   133 SDL_SYS_CDInit(void)
   134 {
   135     /* checklist: /dev/cdrom,/dev/cd?c /dev/acd?c
   136        /dev/matcd?c /dev/mcd?c /dev/scd?c */
   137     static char *checklist[] = {
   138         "cdrom", "?0 cd?", "?0 acd?", "?0 matcd?", "?0 mcd?", "?0 scd?", NULL
   139     };
   140     char *SDLcdrom;
   141     int i, j, exists;
   142     char drive[32];
   143     struct stat stbuf;
   144 
   145     /* Fill in our driver capabilities */
   146     SDL_CDcaps.Name = SDL_SYS_CDName;
   147     SDL_CDcaps.Open = SDL_SYS_CDOpen;
   148     SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
   149     SDL_CDcaps.Status = SDL_SYS_CDStatus;
   150     SDL_CDcaps.Play = SDL_SYS_CDPlay;
   151     SDL_CDcaps.Pause = SDL_SYS_CDPause;
   152     SDL_CDcaps.Resume = SDL_SYS_CDResume;
   153     SDL_CDcaps.Stop = SDL_SYS_CDStop;
   154     SDL_CDcaps.Eject = SDL_SYS_CDEject;
   155     SDL_CDcaps.Close = SDL_SYS_CDClose;
   156 
   157     /* Look in the environment for our CD-ROM drive list */
   158     SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
   159     if (SDLcdrom != NULL) {
   160         char *cdpath, *delim;
   161         size_t len = SDL_strlen(SDLcdrom) + 1;
   162         cdpath = SDL_stack_alloc(char, len);
   163         if (cdpath != NULL) {
   164             SDL_strlcpy(cdpath, SDLcdrom, len);
   165             SDLcdrom = cdpath;
   166             do {
   167                 delim = SDL_strchr(SDLcdrom, ':');
   168                 if (delim) {
   169                     *delim++ = '\0';
   170                 }
   171                 if (CheckDrive(SDLcdrom, &stbuf) > 0) {
   172                     AddDrive(SDLcdrom, &stbuf);
   173                 }
   174                 if (delim) {
   175                     SDLcdrom = delim;
   176                 } else {
   177                     SDLcdrom = NULL;
   178                 }
   179             }
   180             while (SDLcdrom);
   181             SDL_stack_free(cdpath);
   182         }
   183 
   184         /* If we found our drives, there's nothing left to do */
   185         if (SDL_numcds > 0) {
   186             return (0);
   187         }
   188     }
   189 
   190     /* Scan the system for CD-ROM drives */
   191     for (i = 0; checklist[i]; ++i) {
   192         if (checklist[i][0] == '?') {
   193             char *insert;
   194             exists = 1;
   195             for (j = checklist[i][1]; exists; ++j) {
   196                 SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%sc",
   197                              &checklist[i][3]);
   198                 insert = SDL_strchr(drive, '?');
   199                 if (insert != NULL) {
   200                     *insert = j;
   201                 }
   202                 switch (CheckDrive(drive, &stbuf)) {
   203                     /* Drive exists and is a CD-ROM */
   204                 case 1:
   205                     AddDrive(drive, &stbuf);
   206                     break;
   207                     /* Drive exists, but isn't a CD-ROM */
   208                 case 0:
   209                     break;
   210                     /* Drive doesn't exist */
   211                 case -1:
   212                     exists = 0;
   213                     break;
   214                 }
   215             }
   216         } else {
   217             SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s",
   218                          checklist[i]);
   219             if (CheckDrive(drive, &stbuf) > 0) {
   220                 AddDrive(drive, &stbuf);
   221             }
   222         }
   223     }
   224     return (0);
   225 }
   226 
   227 /* General ioctl() CD-ROM command function */
   228 static int
   229 SDL_SYS_CDioctl(int id, int command, void *arg)
   230 {
   231     int retval;
   232 
   233     retval = ioctl(id, command, arg);
   234     if (retval < 0) {
   235         SDL_SetError("ioctl() error: %s", strerror(errno));
   236     }
   237     return (retval);
   238 }
   239 
   240 static const char *
   241 SDL_SYS_CDName(int drive)
   242 {
   243     return (SDL_cdlist[drive]);
   244 }
   245 
   246 static int
   247 SDL_SYS_CDOpen(int drive)
   248 {
   249     return (open(SDL_cdlist[drive], (O_RDONLY | O_EXCL | O_NONBLOCK), 0));
   250 }
   251 
   252 static int
   253 SDL_SYS_CDGetTOC(SDL_CD * cdrom)
   254 {
   255     struct ioc_toc_header toc;
   256     int i, okay;
   257     struct ioc_read_toc_entry entry;
   258     struct cd_toc_entry data;
   259 
   260     okay = 0;
   261     if (SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0) {
   262         cdrom->numtracks = toc.ending_track - toc.starting_track + 1;
   263         if (cdrom->numtracks > SDL_MAX_TRACKS) {
   264             cdrom->numtracks = SDL_MAX_TRACKS;
   265         }
   266         /* Read all the track TOC entries */
   267         for (i = 0; i <= cdrom->numtracks; ++i) {
   268             if (i == cdrom->numtracks) {
   269                 cdrom->track[i].id = 0xAA;      /* CDROM_LEADOUT */
   270             } else {
   271                 cdrom->track[i].id = toc.starting_track + i;
   272             }
   273             entry.starting_track = cdrom->track[i].id;
   274             entry.address_format = CD_MSF_FORMAT;
   275             entry.data_len = sizeof(data);
   276             entry.data = &data;
   277             if (SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS, &entry) < 0) {
   278                 break;
   279             } else {
   280                 cdrom->track[i].type = data.control;
   281                 cdrom->track[i].offset =
   282                     MSF_TO_FRAMES(data.addr.msf.minute,
   283                                   data.addr.msf.second, data.addr.msf.frame);
   284                 cdrom->track[i].length = 0;
   285                 if (i > 0) {
   286                     cdrom->track[i - 1].length =
   287                         cdrom->track[i].offset - cdrom->track[i - 1].offset;
   288                 }
   289             }
   290         }
   291         if (i == (cdrom->numtracks + 1)) {
   292             okay = 1;
   293         }
   294     }
   295     return (okay ? 0 : -1);
   296 }
   297 
   298 /* Get CD-ROM status */
   299 static CDstatus
   300 SDL_SYS_CDStatus(SDL_CD * cdrom, int *position)
   301 {
   302     CDstatus status;
   303     struct ioc_toc_header toc;
   304     struct ioc_read_subchannel info;
   305     struct cd_sub_channel_info data;
   306 
   307     info.address_format = CD_MSF_FORMAT;
   308     info.data_format = CD_CURRENT_POSITION;
   309     info.track = 0;
   310     info.data_len = sizeof(data);
   311     info.data = &data;
   312     if (ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0) {
   313         if (ERRNO_TRAYEMPTY(errno)) {
   314             status = CD_TRAYEMPTY;
   315         } else {
   316             status = CD_ERROR;
   317         }
   318     } else {
   319         switch (data.header.audio_status) {
   320         case CD_AS_AUDIO_INVALID:
   321         case CD_AS_NO_STATUS:
   322             /* Try to determine if there's a CD available */
   323             if (ioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0)
   324                 status = CD_STOPPED;
   325             else
   326                 status = CD_TRAYEMPTY;
   327             break;
   328         case CD_AS_PLAY_COMPLETED:
   329             status = CD_STOPPED;
   330             break;
   331         case CD_AS_PLAY_IN_PROGRESS:
   332             status = CD_PLAYING;
   333             break;
   334         case CD_AS_PLAY_PAUSED:
   335             status = CD_PAUSED;
   336             break;
   337         default:
   338             status = CD_ERROR;
   339             break;
   340         }
   341     }
   342     if (position) {
   343         if (status == CD_PLAYING || (status == CD_PAUSED)) {
   344             *position =
   345                 MSF_TO_FRAMES(data.what.position.absaddr.msf.minute,
   346                               data.what.position.absaddr.msf.second,
   347                               data.what.position.absaddr.msf.frame);
   348         } else {
   349             *position = 0;
   350         }
   351     }
   352     return (status);
   353 }
   354 
   355 /* Start play */
   356 static int
   357 SDL_SYS_CDPlay(SDL_CD * cdrom, int start, int length)
   358 {
   359     struct ioc_play_msf playtime;
   360 
   361     FRAMES_TO_MSF(start,
   362                   &playtime.start_m, &playtime.start_s, &playtime.start_f);
   363     FRAMES_TO_MSF(start + length,
   364                   &playtime.end_m, &playtime.end_s, &playtime.end_f);
   365 #ifdef DEBUG_CDROM
   366     fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
   367             playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
   368             playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
   369 #endif
   370     ioctl(cdrom->id, CDIOCSTART, 0);
   371     return (SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime));
   372 }
   373 
   374 /* Pause play */
   375 static int
   376 SDL_SYS_CDPause(SDL_CD * cdrom)
   377 {
   378     return (SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0));
   379 }
   380 
   381 /* Resume play */
   382 static int
   383 SDL_SYS_CDResume(SDL_CD * cdrom)
   384 {
   385     return (SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0));
   386 }
   387 
   388 /* Stop play */
   389 static int
   390 SDL_SYS_CDStop(SDL_CD * cdrom)
   391 {
   392     return (SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0));
   393 }
   394 
   395 /* Eject the CD-ROM */
   396 static int
   397 SDL_SYS_CDEject(SDL_CD * cdrom)
   398 {
   399     return (SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0));
   400 }
   401 
   402 /* Close the CD-ROM handle */
   403 static void
   404 SDL_SYS_CDClose(SDL_CD * cdrom)
   405 {
   406     close(cdrom->id);
   407 }
   408 
   409 void
   410 SDL_SYS_CDQuit(void)
   411 {
   412     int i;
   413 
   414     if (SDL_numcds > 0) {
   415         for (i = 0; i < SDL_numcds; ++i) {
   416             SDL_free(SDL_cdlist[i]);
   417         }
   418         SDL_numcds = 0;
   419     }
   420 }
   421 
   422 #endif /* SDL_CDROM_FREEBSD */
   423 /* vi: set ts=4 sw=4 expandtab: */