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