src/cdrom/qnx/SDL_syscdrom.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 08 Mar 2006 01:55:32 +0000
changeset 1482 141528317f4f
parent 1402 d910939febfa
child 1635 92947e3a18db
permissions -rw-r--r--
QNX changes from Mike Gorchak
     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 /* Functions for system-level CD-ROM audio control */
    25 
    26 #include <sys/types.h>
    27 #include <sys/stat.h>
    28 #include <sys/ioctl.h>
    29 #include <fcntl.h>
    30 #include <errno.h>
    31 #include <unistd.h>
    32 #include <sys/cdrom.h>
    33 #include <sys/dcmd_cam.h>
    34 
    35 #include "SDL_timer.h"
    36 #include "SDL_cdrom.h"
    37 #include "../SDL_syscdrom.h"
    38 
    39 /* The maximum number of CD-ROM drives we'll detect */
    40 #define MAX_DRIVES 16
    41 
    42 #define QNX_CD_OPENMODE O_RDONLY | O_EXCL
    43 
    44 /* A list of available CD-ROM drives */
    45 static char *SDL_cdlist[MAX_DRIVES];
    46 static dev_t SDL_cdmode[MAX_DRIVES];
    47 static int   SDL_cdopen[MAX_DRIVES];
    48 
    49 /* The system-dependent CD control functions */
    50 static const char *SDL_SYS_CDName(int drive);
    51 static int SDL_SYS_CDOpen(int drive);
    52 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
    53 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
    54 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
    55 static int SDL_SYS_CDPause(SDL_CD *cdrom);
    56 static int SDL_SYS_CDResume(SDL_CD *cdrom);
    57 static int SDL_SYS_CDStop(SDL_CD *cdrom);
    58 static int SDL_SYS_CDEject(SDL_CD *cdrom);
    59 static void SDL_SYS_CDClose(SDL_CD *cdrom);
    60 
    61 /* Check a drive to see if it is a CD-ROM */
    62 static int CheckDrive(char *drive, struct stat *stbuf)
    63 {
    64     int is_cd, cdfd;
    65     cam_devinfo_t dinfo;
    66     int devctlret=0;
    67 
    68     int atapi;
    69     int removable;
    70     int cdb10;
    71 
    72     /* If it doesn't exist, return -1 */
    73     if (stat(drive, stbuf) < 0)
    74     {
    75         return(-1);
    76     }
    77 
    78     /* If it does exist, verify that it's an available CD-ROM */
    79     is_cd = 0;
    80 
    81     if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode))
    82     {
    83         cdfd = open(drive, QNX_CD_OPENMODE);
    84         if ( cdfd >= 0 )
    85         {
    86             devctlret=devctl(cdfd, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
    87 
    88             if (devctlret==EOK)
    89             {
    90                atapi=dinfo.flags & DEV_ATAPI;
    91                removable=dinfo.flags & DEV_REMOVABLE;
    92                cdb10=dinfo.flags & DEV_CDB_10; /* I'm not sure about that flag */
    93 
    94                /* in the near future need to add more checks for splitting cdroms from other devices */
    95                if ((atapi)&&(removable))
    96                {
    97                    is_cd = 1;
    98                }
    99             }
   100 
   101             close(cdfd);
   102         }
   103     }
   104     return(is_cd);
   105 }
   106 
   107 /* Add a CD-ROM drive to our list of valid drives */
   108 static void AddDrive(char *drive, struct stat *stbuf)
   109 {
   110     int i;
   111 
   112     if (SDL_numcds < MAX_DRIVES)
   113     {
   114         /* Check to make sure it's not already in our list.
   115         This can happen when we see a drive via symbolic link. */
   116 
   117         for (i=0; i<SDL_numcds; ++i)
   118         {
   119             if (stbuf->st_rdev == SDL_cdmode[i])
   120             {
   121                 return;
   122             }
   123         }
   124 
   125         /* Add this drive to our list */
   126 
   127         i = SDL_numcds;
   128         SDL_cdlist[i] = SDL_strdup(drive);
   129         if (SDL_cdlist[i] == NULL)
   130         {
   131             SDL_OutOfMemory();
   132             return;
   133         }
   134         SDL_cdmode[i] = stbuf->st_rdev;
   135         ++SDL_numcds;
   136     }
   137 }
   138 
   139 int SDL_SYS_CDInit(void)
   140 {
   141     /* checklist: /dev/cdrom, /dev/cd?, /dev/scd? */
   142     static char *checklist[]={"cdrom", "?0 cd?", "?1 cd?", "?0 scd?", NULL};
   143 
   144     char *SDLcdrom;
   145     int i, j, exists;
   146     char drive[32];
   147     struct stat stbuf;
   148 
   149     /* Fill in our driver capabilities */
   150     SDL_CDcaps.Name = SDL_SYS_CDName;
   151     SDL_CDcaps.Open = SDL_SYS_CDOpen;
   152     SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
   153     SDL_CDcaps.Status = SDL_SYS_CDStatus;
   154     SDL_CDcaps.Play = SDL_SYS_CDPlay;
   155     SDL_CDcaps.Pause = SDL_SYS_CDPause;
   156     SDL_CDcaps.Resume = SDL_SYS_CDResume;
   157     SDL_CDcaps.Stop = SDL_SYS_CDStop;
   158     SDL_CDcaps.Eject = SDL_SYS_CDEject;
   159     SDL_CDcaps.Close = SDL_SYS_CDClose;
   160 
   161     /* clearing device open status */
   162     for (i=0; i<MAX_DRIVES; i++)
   163     {
   164        SDL_cdopen[i]=0;
   165     }
   166 
   167     /* Look in the environment for our CD-ROM drive list */
   168     SDLcdrom = SDL_getenv("SDL_CDROM");	/* ':' separated list of devices */
   169     if ( SDLcdrom != NULL )
   170     {
   171         char *cdpath, *delim;
   172 	size_t len = SDL_strlen(SDLcdrom)+1;
   173         cdpath = SDL_stack_alloc(char, len);
   174         if (cdpath != NULL)
   175         {
   176             SDL_strlcpy(cdpath, SDLcdrom, len);
   177             SDLcdrom = cdpath;
   178             do {
   179                 delim = SDL_strchr(SDLcdrom, ':');
   180                 if (delim)
   181                 {
   182                     *delim++ = '\0';
   183                 }
   184                 if (CheckDrive(SDLcdrom, &stbuf) > 0)
   185                 {
   186                     AddDrive(SDLcdrom, &stbuf);
   187                 }
   188                 if (delim)
   189                 {
   190                     SDLcdrom = delim;
   191                 }
   192                 else
   193                 {
   194                     SDLcdrom = NULL;
   195                 }
   196             } while (SDLcdrom);
   197             SDL_stack_free(cdpath);
   198         }
   199 
   200         /* If we found our drives, there's nothing left to do */
   201         if (SDL_numcds > 0)
   202         {
   203             return(0);
   204         }
   205     }
   206 
   207     /* Scan the system for CD-ROM drives */
   208     for ( i=0; checklist[i]; ++i )
   209     {
   210         if (checklist[i][0] == '?')
   211         {
   212             char* insert;
   213             exists = 1;
   214 
   215             for ( j=checklist[i][1]; exists; ++j )
   216             {
   217                 SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
   218                 insert = SDL_strchr(drive, '?');
   219                 if (insert != NULL)
   220                 {
   221                     *insert = j;
   222                 }
   223                 switch (CheckDrive(drive, &stbuf))
   224                 {
   225                     /* Drive exists and is a CD-ROM */
   226                     case 1:
   227                              AddDrive(drive, &stbuf);
   228                              break;
   229                     /* Drive exists, but isn't a CD-ROM */
   230                     case 0:
   231                              break;
   232                     /* Drive doesn't exist */
   233                     case -1:
   234                              exists = 0;
   235                              break;
   236                 }
   237             }
   238         }
   239         else
   240         {
   241             SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
   242             if (CheckDrive(drive, &stbuf) > 0)
   243             {
   244                 AddDrive(drive, &stbuf);
   245             }
   246         }
   247     }
   248     return(0);
   249 }
   250 
   251 static const char *SDL_SYS_CDName(int drive)
   252 {
   253     return(SDL_cdlist[drive]);
   254 }
   255 
   256 static int SDL_SYS_CDOpen(int drive)
   257 {
   258     int handle;
   259 
   260     handle=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
   261 
   262     if (handle>0)
   263     {
   264         SDL_cdopen[drive]=handle;
   265     }
   266 
   267     return (handle);
   268 }
   269 
   270 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
   271 {
   272     cdrom_read_toc_t toc;
   273     int i, okay;
   274 
   275     okay = 0;
   276     if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL) == 0)
   277     {
   278         cdrom->numtracks = toc.last_track - toc.first_track + 1;
   279         if (cdrom->numtracks > SDL_MAX_TRACKS)
   280         {
   281             cdrom->numtracks = SDL_MAX_TRACKS;
   282         }
   283         /* Read all the track TOC entries */
   284         for (i=0; i<=cdrom->numtracks; ++i)
   285         {
   286             if (i == cdrom->numtracks)
   287             {
   288                 cdrom->track[i].id = CDROM_LEADOUT;
   289             }
   290             else
   291             {
   292                 cdrom->track[i].id = toc.first_track+i;
   293             }
   294 
   295             cdrom->track[i].type = toc.toc_entry[i].control_adr & 0x0F;
   296             cdrom->track[i].offset = toc.toc_entry[i].addr.lba;
   297             cdrom->track[i].length = 0;
   298 
   299             if (i > 0)
   300             {
   301                  cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
   302             }
   303         }
   304         if (i == (cdrom->numtracks+1))
   305         {
   306             okay = 1;
   307         }
   308     }
   309     return (okay ? 0 : -1);
   310 }
   311 
   312 /* Get CD-ROM status */
   313 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
   314 {
   315     CDstatus status;
   316 
   317     cdrom_read_toc_t toc;
   318     cdrom_subch_data_t info;
   319     cam_devinfo_t dinfo;
   320 
   321     int devctlret=0;
   322     int drive=-1;
   323     int i;
   324     int eagaincnt=0;
   325 
   326     /* check media presence before read subchannel call, some cdroms can lockups */
   327     /* if no media, while calling read subchannel functions.                     */
   328     devctlret=devctl(cdrom->id, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
   329 
   330     if (devctlret==EOK)
   331     {
   332         if ((dinfo.flags & DEV_NO_MEDIA)!=0)
   333         {
   334             status = CD_TRAYEMPTY;
   335             if (position)
   336             {
   337                 *position = 0;
   338             }
   339             return (status);
   340         }
   341     }
   342 
   343     /* if media exists, then do other stuff */
   344 
   345     SDL_memset(&info, 0x00, sizeof(info));
   346     info.subch_command.data_format = CDROM_SUBCH_CURRENT_POSITION;
   347 
   348     do {
   349         devctlret=devctl(cdrom->id, DCMD_CAM_CDROMSUBCHNL, &info, sizeof(info), NULL);
   350         if (devctlret==EIO)
   351         {
   352             /* big workaround for media change, handle is unusable after that,
   353                that bug was found in QNX 6.2, 6.2.1 is not released yet.    */
   354 
   355             for (i=0; i<MAX_DRIVES; i++)
   356             {
   357                 if (SDL_cdopen[i]==cdrom->id)
   358                 {
   359                     drive=i;
   360                     break;
   361                 }
   362             }
   363             if (drive==-1)
   364             {
   365                /* that cannot happen, but ... */
   366                break;
   367             }
   368             close(cdrom->id);
   369             cdrom->id=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
   370             devctlret=EAGAIN;
   371         }
   372         if (devctlret==EAGAIN)
   373         {
   374             eagaincnt++;
   375         }
   376         if (eagaincnt==2)
   377         {
   378             /* workaround for broken cdroms, which can return always EAGAIN when its not ready, */
   379             /* that mean errornous media or just no media avail                                 */
   380             devctlret=ENXIO;
   381             break;
   382         }
   383     } while ((devctlret==EAGAIN)||(devctlret==ESTALE));
   384 
   385     if (devctlret != 0)
   386     {
   387         if (devctlret==ENXIO)
   388         {
   389             status = CD_TRAYEMPTY;
   390         }
   391         else
   392         {
   393             status = CD_ERROR;
   394         }
   395     }
   396     else
   397     {
   398         switch (info.current_position.header.audio_status)
   399         {
   400             case CDROM_AUDIO_INVALID:
   401             case CDROM_AUDIO_NO_STATUS:
   402                  /* Try to determine if there's a CD available */
   403                  if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL)==0)
   404                      status = CD_STOPPED;
   405                  else
   406                      status = CD_TRAYEMPTY;
   407                  break;
   408             case CDROM_AUDIO_COMPLETED:
   409                  status = CD_STOPPED;
   410                  break;
   411             case CDROM_AUDIO_PLAY:
   412                  status = CD_PLAYING;
   413                  break;
   414             case CDROM_AUDIO_PAUSED:
   415                  /* Workaround buggy CD-ROM drive */
   416                  if (info.current_position.data_format == CDROM_LEADOUT)
   417                  {
   418                      status = CD_STOPPED;
   419                  }
   420                  else
   421                  {
   422                      status = CD_PAUSED;
   423                  }
   424                  break;
   425             default:
   426                  status = CD_ERROR;
   427                  break;
   428         }
   429     }
   430 
   431     if (position)
   432     {
   433        if (status==CD_PLAYING || (status==CD_PAUSED))
   434        {
   435            *position = MSF_TO_FRAMES(info.current_position.addr.msf.minute,
   436                                      info.current_position.addr.msf.second,
   437                                      info.current_position.addr.msf.frame);
   438        }
   439        else
   440        {
   441            *position = 0;
   442        }
   443     }
   444 
   445     return (status);
   446 }
   447 
   448 /* Start play */
   449 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
   450 {
   451     cdrom_playmsf_t playtime;
   452 
   453     FRAMES_TO_MSF(start, &playtime.start_minute, &playtime.start_second, &playtime.start_frame);
   454     FRAMES_TO_MSF(start+length, &playtime.end_minute, &playtime.end_second, &playtime.end_frame);
   455 
   456     if (devctl(cdrom->id, DCMD_CAM_CDROMPLAYMSF, &playtime, sizeof(playtime), NULL) != 0)
   457     {
   458        return -1;
   459     }
   460     else
   461     {
   462        return 0;
   463     }
   464 }
   465 
   466 /* Pause play */
   467 static int SDL_SYS_CDPause(SDL_CD *cdrom)
   468 {
   469     if (devctl(cdrom->id, DCMD_CAM_CDROMPAUSE, NULL, 0, NULL)!=0)
   470     {
   471        return -1;
   472     }
   473     else
   474     {
   475        return 0;
   476     }
   477 }
   478 
   479 /* Resume play */
   480 static int SDL_SYS_CDResume(SDL_CD *cdrom)
   481 {
   482     if (devctl(cdrom->id, DCMD_CAM_CDROMRESUME, NULL, 0, NULL)!=0)
   483     {
   484        return -1;
   485     }
   486     else
   487     {
   488        return 0;
   489     }
   490 }
   491 
   492 /* Stop play */
   493 static int SDL_SYS_CDStop(SDL_CD *cdrom)
   494 {
   495     if (devctl(cdrom->id, DCMD_CAM_CDROMSTOP, NULL, 0, NULL)!=0)
   496     {
   497        return -1;
   498     }
   499     else
   500     {
   501        return 0;
   502     }
   503 }
   504 
   505 /* Eject the CD-ROM */
   506 static int SDL_SYS_CDEject(SDL_CD *cdrom)
   507 {
   508     if (devctl(cdrom->id, DCMD_CAM_EJECT_MEDIA, NULL, 0, NULL)!=0)
   509     {
   510        return -1;
   511     }
   512     else
   513     {
   514        return 0;
   515     }
   516 }
   517 
   518 /* Close the CD-ROM handle */
   519 static void SDL_SYS_CDClose(SDL_CD *cdrom)
   520 {
   521     int i;
   522 
   523     for (i=0; i<MAX_DRIVES; i++)
   524     {
   525        if (SDL_cdopen[i]==cdrom->id)
   526        {
   527            SDL_cdopen[i]=0;
   528            break;
   529        }
   530     }
   531 
   532     close(cdrom->id);
   533 }
   534 
   535 void SDL_SYS_CDQuit(void)
   536 {
   537     int i;
   538 
   539     if (SDL_numcds > 0)
   540     {
   541         for (i=0; i<SDL_numcds; ++i)
   542         {
   543             SDL_free(SDL_cdlist[i]);
   544         }
   545         SDL_numcds = 0;
   546     }
   547 }