Mercurial > sdl-ios-xcode
comparison src/cdrom/bsdi/SDL_syscdrom.c @ 178:1fc1a101bf23
Added CD-ROM support for BSD/OS (thanks Steven!)
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Thu, 13 Sep 2001 15:57:48 +0000 |
parents | |
children | e8157fcb3114 |
comparison
equal
deleted
inserted
replaced
177:5f46f27efb6a | 178:1fc1a101bf23 |
---|---|
1 /* | |
2 SDL - Simple DirectMedia Layer | |
3 Copyright (C) 1997, 1998, 1999, 2000 Sam Lantinga | |
4 | |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 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 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
20 slouken@devolution.com | |
21 */ | |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 /* | |
29 * Functions for system-level CD-ROM audio control for BSD/OS 4.x | |
30 * This started life out as a copy of the freebsd/SDL_cdrom.c file but was | |
31 * heavily modified. Works for standard (MMC) SCSI and ATAPI CDrom drives. | |
32 * | |
33 * Steven Schultz - sms@to.gd-es.com | |
34 */ | |
35 | |
36 #include <sys/types.h> | |
37 #include <stdlib.h> | |
38 #include <sys/stat.h> | |
39 #include <fcntl.h> | |
40 #include <stdio.h> | |
41 #include <string.h> | |
42 #include <errno.h> | |
43 #include <err.h> | |
44 #include <unistd.h> | |
45 #include <sys/ioctl.h> | |
46 #include </sys/dev/scsi/scsi.h> | |
47 #include </sys/dev/scsi/scsi_ioctl.h> | |
48 | |
49 #include "SDL_error.h" | |
50 #include "SDL_cdrom.h" | |
51 #include "SDL_syscdrom.h" | |
52 | |
53 /* | |
54 * The msf_to_frame and frame_to_msf were yanked from libcdrom and inlined | |
55 * here so that -lcdrom doesn't have to be dragged in for something so simple. | |
56 */ | |
57 | |
58 #define FRAMES_PER_SECOND 75 | |
59 #define FRAMES_PER_MINUTE (FRAMES_PER_SECOND * 60) | |
60 | |
61 int | |
62 msf_to_frame(int minute, int second, int frame) | |
63 { | |
64 return(minute * FRAMES_PER_MINUTE + second * FRAMES_PER_SECOND + frame); | |
65 } | |
66 | |
67 void | |
68 frame_to_msf(int frame, int *minp, int *secp, int *framep) | |
69 { | |
70 *minp = frame / FRAMES_PER_MINUTE; | |
71 *secp = (frame % FRAMES_PER_MINUTE) / FRAMES_PER_SECOND; | |
72 *framep = frame % FRAMES_PER_SECOND; | |
73 } | |
74 | |
75 /* The maximum number of CD-ROM drives we'll detect */ | |
76 #define MAX_DRIVES 16 | |
77 | |
78 /* A list of available CD-ROM drives */ | |
79 static char *SDL_cdlist[MAX_DRIVES]; | |
80 static dev_t SDL_cdmode[MAX_DRIVES]; | |
81 | |
82 /* The system-dependent CD control functions */ | |
83 static const char *SDL_SYS_CDName(int drive); | |
84 static int SDL_SYS_CDOpen(int drive); | |
85 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); | |
86 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); | |
87 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); | |
88 static int SDL_SYS_CDPause(SDL_CD *cdrom); | |
89 static int SDL_SYS_CDResume(SDL_CD *cdrom); | |
90 static int SDL_SYS_CDStop(SDL_CD *cdrom); | |
91 static int SDL_SYS_CDEject(SDL_CD *cdrom); | |
92 static void SDL_SYS_CDClose(SDL_CD *cdrom); | |
93 | |
94 typedef struct scsi_cdb cdb_t; | |
95 | |
96 static int scsi_cmd(int fd, | |
97 struct scsi_cdb *cdb, | |
98 int cdblen, | |
99 int rw, | |
100 caddr_t data, | |
101 int datalen, | |
102 struct scsi_user_cdb *sus) | |
103 { | |
104 int scsistatus; | |
105 unsigned char *cp; | |
106 struct scsi_user_cdb suc; | |
107 | |
108 /* safety checks */ | |
109 if (!cdb) return(-1); | |
110 if (rw != SUC_READ && rw != SUC_WRITE) return(-1); | |
111 | |
112 suc.suc_flags = rw; | |
113 suc.suc_cdblen = cdblen; | |
114 bcopy(cdb, suc.suc_cdb, cdblen); | |
115 suc.suc_datalen = datalen; | |
116 suc.suc_data = data; | |
117 suc.suc_timeout = 10; /* 10 secs max for TUR or SENSE */ | |
118 if (ioctl(fd, SCSIRAWCDB, &suc) == -1) | |
119 return(-11); | |
120 scsistatus = suc.suc_sus.sus_status; | |
121 cp = suc.suc_sus.sus_sense; | |
122 | |
123 /* | |
124 * If a place to copy the sense data back to has been provided then the | |
125 * caller is responsible for checking the errors and printing any information | |
126 * out if the status was not successful. | |
127 */ | |
128 if (scsistatus != 0 && !sus) | |
129 { | |
130 fprintf(stderr,"scsistatus = %x cmd = %x\n", | |
131 scsistatus, cdb[0]); | |
132 fprintf(stderr, "sense %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", | |
133 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], | |
134 cp[6], cp[7], cp[8], cp[9], cp[10], cp[11], | |
135 cp[12], cp[13], cp[14], cp[15]); | |
136 return(1); | |
137 } | |
138 if (sus) | |
139 bcopy(&suc, sus, sizeof (struct scsi_user_cdb)); | |
140 if (scsistatus) | |
141 return(1); /* Return non-zero for unsuccessful status */ | |
142 return(0); | |
143 } | |
144 | |
145 /* request vendor brand and model */ | |
146 unsigned char *Inquiry(int fd) | |
147 { | |
148 static struct scsi_cdb6 cdb = | |
149 { | |
150 0x12, | |
151 0, 0, 0, | |
152 56, | |
153 0 | |
154 }; | |
155 static unsigned char Inqbuffer[56]; | |
156 | |
157 if (scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, Inqbuffer, | |
158 sizeof(Inqbuffer), 0)) | |
159 return("\377"); | |
160 return(Inqbuffer); | |
161 } | |
162 | |
163 #define ADD_SENSECODE 12 | |
164 #define ADD_SC_QUALIFIER 13 | |
165 | |
166 int TestForMedium(int fd) | |
167 { | |
168 int sts, asc, ascq; | |
169 struct scsi_user_cdb sus; | |
170 static struct scsi_cdb6 cdb = | |
171 { | |
172 CMD_TEST_UNIT_READY, /* command */ | |
173 0, /* reserved */ | |
174 0, /* reserved */ | |
175 0, /* reserved */ | |
176 0, /* reserved */ | |
177 0 /* reserved */ | |
178 }; | |
179 | |
180 again: sts = scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, 0, 0, &sus); | |
181 asc = sus.suc_sus.sus_sense[ADD_SENSECODE]; | |
182 ascq = sus.suc_sus.sus_sense[ADD_SC_QUALIFIER]; | |
183 if (asc == 0x3a && ascq == 0x0) /* no medium */ | |
184 return(0); | |
185 if (asc == 0x28 && ascq == 0x0) /* medium changed */ | |
186 goto again; | |
187 if (asc == 0x4 && ascq == 0x1 ) /* coming ready */ | |
188 { | |
189 sleep(2); | |
190 goto again; | |
191 } | |
192 return(1); | |
193 } | |
194 | |
195 /* Check a drive to see if it is a CD-ROM */ | |
196 static int CheckDrive(char *drive, struct stat *stbuf) | |
197 { | |
198 int is_cd = 0, cdfd; | |
199 char *p; | |
200 | |
201 /* If it doesn't exist, return -1 */ | |
202 if ( stat(drive, stbuf) < 0 ) { | |
203 return(-1); | |
204 } | |
205 | |
206 /* If it does exist, verify that it's an available CD-ROM */ | |
207 cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); | |
208 if ( cdfd >= 0 ) { | |
209 p = Inquiry(cdfd); | |
210 if (*p == TYPE_ROM) | |
211 is_cd = 1; | |
212 close(cdfd); | |
213 } | |
214 return(is_cd); | |
215 } | |
216 | |
217 /* Add a CD-ROM drive to our list of valid drives */ | |
218 static void AddDrive(char *drive, struct stat *stbuf) | |
219 { | |
220 int i; | |
221 | |
222 if ( SDL_numcds < MAX_DRIVES ) { | |
223 /* Check to make sure it's not already in our list. | |
224 This can happen when we see a drive via symbolic link. | |
225 */ | |
226 for ( i=0; i<SDL_numcds; ++i ) { | |
227 if ( stbuf->st_rdev == SDL_cdmode[i] ) { | |
228 #ifdef DEBUG_CDROM | |
229 fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); | |
230 #endif | |
231 return; | |
232 } | |
233 } | |
234 | |
235 /* Add this drive to our list */ | |
236 i = SDL_numcds; | |
237 SDL_cdlist[i] = (char *)malloc(strlen(drive)+1); | |
238 if ( SDL_cdlist[i] == NULL ) { | |
239 SDL_OutOfMemory(); | |
240 return; | |
241 } | |
242 strcpy(SDL_cdlist[i], drive); | |
243 SDL_cdmode[i] = stbuf->st_rdev; | |
244 ++SDL_numcds; | |
245 #ifdef DEBUG_CDROM | |
246 fprintf(stderr, "Added CD-ROM drive: %s\n", drive); | |
247 #endif | |
248 } | |
249 } | |
250 | |
251 int SDL_SYS_CDInit(void) | |
252 { | |
253 /* checklist: /dev/rsr?c */ | |
254 static char *checklist[] = { | |
255 "?0 rsr?", NULL | |
256 }; | |
257 char *SDLcdrom; | |
258 int i, j, exists; | |
259 char drive[32]; | |
260 struct stat stbuf; | |
261 | |
262 /* Fill in our driver capabilities */ | |
263 SDL_CDcaps.Name = SDL_SYS_CDName; | |
264 SDL_CDcaps.Open = SDL_SYS_CDOpen; | |
265 SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; | |
266 SDL_CDcaps.Status = SDL_SYS_CDStatus; | |
267 SDL_CDcaps.Play = SDL_SYS_CDPlay; | |
268 SDL_CDcaps.Pause = SDL_SYS_CDPause; | |
269 SDL_CDcaps.Resume = SDL_SYS_CDResume; | |
270 SDL_CDcaps.Stop = SDL_SYS_CDStop; | |
271 SDL_CDcaps.Eject = SDL_SYS_CDEject; | |
272 SDL_CDcaps.Close = SDL_SYS_CDClose; | |
273 | |
274 /* Look in the environment for our CD-ROM drive list */ | |
275 SDLcdrom = getenv("SDL_CDROM"); /* ':' separated list of devices */ | |
276 if ( SDLcdrom != NULL ) { | |
277 char *cdpath, *delim; | |
278 cdpath = malloc(strlen(SDLcdrom)+1); | |
279 if ( cdpath != NULL ) { | |
280 strcpy(cdpath, SDLcdrom); | |
281 SDLcdrom = cdpath; | |
282 do { | |
283 delim = strchr(SDLcdrom, ':'); | |
284 if ( delim ) { | |
285 *delim++ = '\0'; | |
286 } | |
287 if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { | |
288 AddDrive(SDLcdrom, &stbuf); | |
289 } | |
290 if ( delim ) { | |
291 SDLcdrom = delim; | |
292 } else { | |
293 SDLcdrom = NULL; | |
294 } | |
295 } while ( SDLcdrom ); | |
296 free(cdpath); | |
297 } | |
298 | |
299 /* If we found our drives, there's nothing left to do */ | |
300 if ( SDL_numcds > 0 ) { | |
301 return(0); | |
302 } | |
303 } | |
304 | |
305 /* Scan the system for CD-ROM drives */ | |
306 for ( i=0; checklist[i]; ++i ) { | |
307 if ( checklist[i][0] == '?' ) { | |
308 char *insert; | |
309 exists = 1; | |
310 for ( j=checklist[i][1]; exists; ++j ) { | |
311 sprintf(drive, "/dev/%sc", &checklist[i][3]); | |
312 insert = strchr(drive, '?'); | |
313 if ( insert != NULL ) { | |
314 *insert = j; | |
315 } | |
316 switch (CheckDrive(drive, &stbuf)) { | |
317 /* Drive exists and is a CD-ROM */ | |
318 case 1: | |
319 AddDrive(drive, &stbuf); | |
320 break; | |
321 /* Drive exists, but isn't a CD-ROM */ | |
322 case 0: | |
323 break; | |
324 /* Drive doesn't exist */ | |
325 case -1: | |
326 exists = 0; | |
327 break; | |
328 } | |
329 } | |
330 } else { | |
331 sprintf(drive, "/dev/%s", checklist[i]); | |
332 if ( CheckDrive(drive, &stbuf) > 0 ) { | |
333 AddDrive(drive, &stbuf); | |
334 } | |
335 } | |
336 } | |
337 return(0); | |
338 } | |
339 | |
340 static const char *SDL_SYS_CDName(int drive) | |
341 { | |
342 return(SDL_cdlist[drive]); | |
343 } | |
344 | |
345 static int SDL_SYS_CDOpen(int drive) | |
346 { | |
347 return(open(SDL_cdlist[drive], O_RDONLY | O_NONBLOCK | O_EXCL, 0)); | |
348 } | |
349 | |
350 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) | |
351 { | |
352 u_char cdb[10], buf[4], *p, *toc; | |
353 struct scsi_user_cdb sus; | |
354 int i, sts, first_track, last_track, ntracks, toc_size; | |
355 | |
356 bzero(cdb, sizeof (cdb)); | |
357 cdb[0] = 0x43; /* Read TOC */ | |
358 cdb[1] = 0x2; /* MSF */ | |
359 cdb[8] = 4; /* size TOC header */ | |
360 sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, 4, &sus); | |
361 if (sts < 0) | |
362 return(-1); | |
363 first_track = buf[2]; | |
364 last_track = buf[3]; | |
365 ntracks = last_track - first_track + 1; | |
366 cdrom->numtracks = ntracks; | |
367 toc_size = 4 + (ntracks + 1) * 8; | |
368 toc = (u_char *)malloc(toc_size); | |
369 if (toc == NULL) | |
370 return(-1); | |
371 bzero(cdb, sizeof (cdb)); | |
372 cdb[0] = 0x43; | |
373 cdb[1] = 0x2; | |
374 cdb[6] = first_track; | |
375 cdb[7] = toc_size >> 8; | |
376 cdb[8] = toc_size & 0xff; | |
377 sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, toc, toc_size, | |
378 &sus); | |
379 if (sts < 0) | |
380 { | |
381 free(toc); | |
382 return(-1); | |
383 } | |
384 | |
385 for (i = 0, p = toc+4; i <= ntracks; i++, p+= 8) | |
386 { | |
387 if (i == ntracks) | |
388 cdrom->track[i].id = 0xAA; /* Leadout */ | |
389 else | |
390 cdrom->track[i].id = first_track + i; | |
391 if (p[1] & 0x20) | |
392 cdrom->track[i].type = SDL_DATA_TRACK; | |
393 else | |
394 cdrom->track[i].type = SDL_AUDIO_TRACK; | |
395 cdrom->track[i].offset = msf_to_frame(p[5], p[6], p[7]); | |
396 cdrom->track[i].length = 0; | |
397 if (i > 0) | |
398 cdrom->track[i-1].length = cdrom->track[i].offset - | |
399 cdrom->track[i-1].offset; | |
400 } | |
401 free(toc); | |
402 return(0); | |
403 } | |
404 | |
405 /* Get CD-ROM status */ | |
406 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) | |
407 { | |
408 CDstatus status; | |
409 u_char cdb[10], buf[16]; | |
410 int sts; | |
411 struct scsi_user_cdb sus; | |
412 | |
413 bzero(cdb, sizeof (cdb)); | |
414 cdb[0] = 0x42; /* read subq */ | |
415 cdb[1] = 0x2; /* MSF */ | |
416 cdb[2] = 0x40; /* q channel */ | |
417 cdb[3] = 1; /* current pos */ | |
418 cdb[7] = sizeof (buf) >> 8; | |
419 cdb[8] = sizeof (buf) & 0xff; | |
420 sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, sizeof (buf), | |
421 &sus); | |
422 if (sts < 0) | |
423 return(-1); | |
424 if (sts) | |
425 { | |
426 if (TestForMedium(cdrom->id) == 0) | |
427 status = CD_TRAYEMPTY; | |
428 else | |
429 status = CD_ERROR; | |
430 } | |
431 else | |
432 { | |
433 switch (buf[1]) | |
434 { | |
435 case 0x11: | |
436 status = CD_PLAYING; | |
437 break; | |
438 case 0x12: | |
439 status = CD_PAUSED; | |
440 break; | |
441 case 0x13: | |
442 case 0x14: | |
443 case 0x15: | |
444 status = CD_STOPPED; | |
445 break; | |
446 default: | |
447 status = CD_ERROR; | |
448 break; | |
449 } | |
450 } | |
451 if (position) | |
452 { | |
453 if ( status == CD_PLAYING || (status == CD_PAUSED) ) | |
454 *position = msf_to_frame(buf[9], buf[10], buf[11]); | |
455 else | |
456 *position = 0; | |
457 } | |
458 return(status); | |
459 } | |
460 | |
461 /* Start play */ | |
462 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) | |
463 { | |
464 u_char cdb[10]; | |
465 int sts, minute, second, frame, eminute, esecond, eframe; | |
466 struct scsi_user_cdb sus; | |
467 | |
468 bzero(cdb, sizeof(cdb)); | |
469 cdb[0] = 0x47; /* Play */ | |
470 frame_to_msf(start, &minute, &second, &frame); | |
471 frame_to_msf(start + length, &eminute, &esecond, &eframe); | |
472 cdb[3] = minute; | |
473 cdb[4] = second; | |
474 cdb[5] = frame; | |
475 cdb[6] = eminute; | |
476 cdb[7] = esecond; | |
477 cdb[8] = eframe; | |
478 sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus); | |
479 return(sts); | |
480 } | |
481 | |
482 static int | |
483 pauseresume(SDL_CD *cdrom, int flag) | |
484 { | |
485 u_char cdb[10]; | |
486 struct scsi_user_cdb sus; | |
487 | |
488 bzero(cdb, sizeof (cdb)); | |
489 cdb[0] = 0x4b; | |
490 cdb[8] = flag & 0x1; | |
491 return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus)); | |
492 } | |
493 | |
494 /* Pause play */ | |
495 static int SDL_SYS_CDPause(SDL_CD *cdrom) | |
496 { | |
497 return(pauseresume(cdrom, 0)); | |
498 } | |
499 | |
500 /* Resume play */ | |
501 static int SDL_SYS_CDResume(SDL_CD *cdrom) | |
502 { | |
503 return(pauseresume(cdrom, 1)); | |
504 } | |
505 | |
506 /* Stop play */ | |
507 static int SDL_SYS_CDStop(SDL_CD *cdrom) | |
508 { | |
509 u_char cdb[6]; | |
510 struct scsi_user_cdb sus; | |
511 | |
512 bzero(cdb, sizeof (cdb)); | |
513 cdb[0] = 0x1b; /* stop */ | |
514 cdb[1] = 1; /* immediate */ | |
515 return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus)); | |
516 } | |
517 | |
518 /* Eject the CD-ROM */ | |
519 static int SDL_SYS_CDEject(SDL_CD *cdrom) | |
520 { | |
521 u_char cdb[6]; | |
522 struct scsi_user_cdb sus; | |
523 | |
524 bzero(cdb, sizeof (cdb)); | |
525 cdb[0] = 0x1b; /* stop */ | |
526 cdb[1] = 1; /* immediate */ | |
527 cdb[4] = 2; /* eject */ | |
528 return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus)); | |
529 } | |
530 | |
531 /* Close the CD-ROM handle */ | |
532 static void SDL_SYS_CDClose(SDL_CD *cdrom) | |
533 { | |
534 close(cdrom->id); | |
535 } | |
536 | |
537 void SDL_SYS_CDQuit(void) | |
538 { | |
539 int i; | |
540 | |
541 if ( SDL_numcds > 0 ) { | |
542 for ( i=0; i<SDL_numcds; ++i ) { | |
543 free(SDL_cdlist[i]); | |
544 } | |
545 } | |
546 SDL_numcds = 0; | |
547 } |