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