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