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