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 }