Home | History | Annotate | Download | only in qnx
      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     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