SDL_syscdrom.c revision 46be48730333120a7b939116cef075e61c12c703
1ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
2ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    Tru64 audio module for SDL (Simple DirectMedia Layer)
3ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    Copyright (C) 2003
4ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
5ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    This library is free software; you can redistribute it and/or
6ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    modify it under the terms of the GNU Library General Public
7ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    License as published by the Free Software Foundation; either
8ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    version 2 of the License, or (at your option) any later version.
9ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
10ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    This library is distributed in the hope that it will be useful,
11ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    but WITHOUT ANY WARRANTY; without even the implied warranty of
12ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    Library General Public License for more details.
14ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
15ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    You should have received a copy of the GNU Library General Public
16ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    License along with this library; if not, write to the Free
17ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
19ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
20*/
21#include "SDL_config.h"
22
23#ifdef SDL_CDROM_OSF
24
25/* Functions for system-level CD-ROM audio control */
26
27/* #define DEBUG_CDROM 1 */
28
29#include <sys/types.h>
30#include <dirent.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <io/cam/cdrom.h>
34#include <io/cam/rzdisk.h>
35#include <io/common/devgetinfo.h>
36
37#include "SDL_cdrom.h"
38#include "../SDL_syscdrom.h"
39
40/* The maximum number of CD-ROM drives we'll detect */
41#define MAX_DRIVES 16
42
43/* A list of available CD-ROM drives */
44static char *SDL_cdlist[MAX_DRIVES];
45static dev_t SDL_cdmode[MAX_DRIVES];
46
47/* The system-dependent CD control functions */
48static const char *SDL_SYS_CDName(int drive);
49static int         SDL_SYS_CDOpen(int drive);
50static int         SDL_SYS_CDGetTOC(SDL_CD *cdrom);
51static CDstatus    SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
52static int         SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
53static int         SDL_SYS_CDPause(SDL_CD *cdrom);
54static int         SDL_SYS_CDResume(SDL_CD *cdrom);
55static int         SDL_SYS_CDStop(SDL_CD *cdrom);
56static int         SDL_SYS_CDEject(SDL_CD *cdrom);
57static void        SDL_SYS_CDClose(SDL_CD *cdrom);
58
59/* Check a drive to see if it is a CD-ROM */
60/* Caution!! Not tested. */
61static int CheckDrive(char *drive, struct stat *stbuf)
62{
63    int cdfd, is_cd = 0;
64    struct mode_sel_sns_params msp;
65    struct inquiry_info inq;
66
67#ifdef DEBUG_CDROM
68    char *devtype[] = {"Disk", "Tape", "Printer", "Processor", "WORM",
69	"CD-ROM", "Scanner", "Optical", "Changer", "Comm", "Unknown"};
70#endif
71
72    bzero(&msp, sizeof(msp));
73    bzero(&inq, sizeof(inq));
74
75    /* If it doesn't exist, return -1 */
76    if ( stat(drive, stbuf) < 0 ) {
77	return(-1);
78    }
79
80    if ( (cdfd = open(drive, (O_RDWR|O_NDELAY), 0)) >= 0 ) {
81	msp.msp_addr   =   (caddr_t) &inq;
82	msp.msp_pgcode =                0;
83	msp.msp_pgctrl =                0;
84	msp.msp_length =      sizeof(inq);
85	msp.msp_setps  =                0;
86
87	if ( ioctl(cdfd, SCSI_GET_INQUIRY_DATA, &msp) )
88	    return (0);
89
90#ifdef DEBUG_CDROM
91	fprintf(stderr, "Device Type: %s\n", devtype[inq.perfdt]);
92	fprintf(stderr, "Vendor: %.8s\n", inq.vndrid);
93	fprintf(stderr, "Product: %.8s\n", inq.prodid);
94	fprintf(stderr, "Revision: %.8s\n", inq.revlvl);
95#endif
96	if ( inq.perfdt == DTYPE_RODIRECT )
97	    is_cd = 1;
98    }
99
100    return(is_cd);
101}
102
103/* Add a CD-ROM drive to our list of valid drives */
104static void AddDrive(char *drive, struct stat *stbuf)
105{
106    int i;
107
108    if ( SDL_numcds < MAX_DRIVES ) {
109	/* Check to make sure it's not already in our list.
110	 * This can happen when we see a drive via symbolic link.
111	 *
112	 */
113	for ( i=0; i<SDL_numcds; ++i ) {
114	    if ( stbuf->st_rdev == SDL_cdmode[i] ) {
115#ifdef DEBUG_CDROM
116  fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
117#endif
118	    return;
119	    }
120	}
121
122	/* Add this drive to our list */
123	i = SDL_numcds;
124	SDL_cdlist[i] = SDL_strdup(drive);
125	if ( SDL_cdlist[i] == NULL ) {
126	    SDL_OutOfMemory();
127	    return;
128	}
129	SDL_cdmode[i] = stbuf->st_rdev;
130	++SDL_numcds;
131#ifdef DEBUG_CDROM
132  fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
133#endif
134    }
135}
136
137int  SDL_SYS_CDInit(void)
138{
139    /* checklist:
140     *
141     * Tru64 5.X (/dev/rdisk/cdrom?c)
142     * dir: /dev/rdisk, name: cdrom
143     *
144     * Digital UNIX 4.0X (/dev/rrz?c)
145     * dir: /dev, name: rrz
146     *
147     */
148    struct {
149	char *dir;
150	char *name;
151    } checklist[] = {
152	{"/dev/rdisk", "cdrom"},
153	{"/dev", "rrz"},
154	{NULL, NULL}};
155    char drive[32];
156    char *SDLcdrom;
157    int i, j, exists;
158    struct stat stbuf;
159
160    /* Fill in our driver capabilities */
161    SDL_CDcaps.Name   = SDL_SYS_CDName;
162    SDL_CDcaps.Open   = SDL_SYS_CDOpen;
163    SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
164    SDL_CDcaps.Status = SDL_SYS_CDStatus;
165    SDL_CDcaps.Play   = SDL_SYS_CDPlay;
166    SDL_CDcaps.Pause  = SDL_SYS_CDPause;
167    SDL_CDcaps.Resume = SDL_SYS_CDResume;
168    SDL_CDcaps.Stop   = SDL_SYS_CDStop;
169    SDL_CDcaps.Eject  = SDL_SYS_CDEject;
170    SDL_CDcaps.Close  = SDL_SYS_CDClose;
171
172
173    /* Look in the environment for our CD-ROM drive list */
174    SDLcdrom = SDL_getenv("SDL_CDROM");	/* ':' separated list of devices */
175    if ( SDLcdrom != NULL ) {
176	char *cdpath, *delim;
177	size_t len = SDL_strlen(SDLcdrom)+1;
178	cdpath = SDL_stack_alloc(char, len);
179	if ( cdpath != NULL ) {
180	    SDL_strlcpy(cdpath, SDLcdrom, len);
181	    SDLcdrom = cdpath;
182	    do {
183		delim = SDL_strchr(SDLcdrom, ':');
184		if ( delim ) {
185		    *delim++ = '\0';
186		}
187		if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
188		    AddDrive(SDLcdrom, &stbuf);
189		}
190		if ( delim ) {
191		    SDLcdrom = delim;
192		} else {
193		    SDLcdrom = NULL;
194		}
195	    } while ( SDLcdrom );
196	    SDL_stack_free(cdpath);
197	}
198
199	/* If we found our drives, there's nothing left to do */
200	if ( SDL_numcds > 0 ) {
201	    return(0);
202	}
203    }
204    /* Scan the system for CD-ROM drives */
205    for ( i = 0; checklist[i].dir; ++i) {
206	DIR *devdir;
207	struct dirent *devent;
208	int name_len;
209
210	devdir = opendir(checklist[i].dir);
211	if (devdir) {
212	    name_len = SDL_strlen(checklist[i].name);
213	    while (devent = readdir(devdir))
214		if (SDL_memcmp(checklist[i].name, devent->d_name, name_len) == 0)
215		    if (devent->d_name[devent->d_namlen-1] == 'c') {
216			SDL_snprintf(drive, SDL_arraysize(drive), "%s/%s", checklist[i].dir, devent->d_name);
217#ifdef DEBUG_CDROM
218			fprintf(stderr, "Try to add drive: %s\n", drive);
219#endif
220			if ( CheckDrive(drive, &stbuf) > 0 )
221			    AddDrive(drive, &stbuf);
222		    }
223	    closedir(devdir);
224	} else {
225#ifdef DEBUG_CDROM
226	    fprintf(stderr, "cannot open dir: %s\n", checklist[i].dir);
227#endif
228	}
229    }
230    return (0);
231}
232
233static const char *SDL_SYS_CDName(int drive)
234{
235    return(SDL_cdlist[drive]);
236}
237
238static int SDL_SYS_CDOpen(int drive)
239{
240    /* O_RDWR: To use ioctl(fd, SCSI_STOP_UNIT) */
241    return(open(SDL_cdlist[drive], (O_RDWR|O_NDELAY), 0));
242}
243
244static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
245{
246    struct cd_toc                  toc;
247    struct cd_toc_header           hdr;
248    struct cd_toc_entry          *cdte;
249    int i;
250    int okay = 0;
251    if ( ioctl(cdrom->id, CDROM_TOC_HEADER, &hdr) ) {
252	fprintf(stderr,"ioctl error CDROM_TOC_HEADER\n");
253	return -1;
254    }
255    cdrom->numtracks = hdr.th_ending_track - hdr.th_starting_track + 1;
256    if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
257	cdrom->numtracks = SDL_MAX_TRACKS;
258    }
259#ifdef DEBUG_CDROM
260  fprintf(stderr,"hdr.th_data_len1 = %d\n", hdr.th_data_len1);
261  fprintf(stderr,"hdr.th_data_len0 = %d\n", hdr.th_data_len0);
262  fprintf(stderr,"hdr.th_starting_track = %d\n", hdr.th_starting_track);
263  fprintf(stderr,"hdr.th_ending_track = %d\n", hdr.th_ending_track);
264  fprintf(stderr,"cdrom->numtracks = %d\n", cdrom->numtracks);
265#endif
266    toc.toc_address_format = CDROM_LBA_FORMAT;
267    toc.toc_starting_track = 0;
268    toc.toc_alloc_length = (hdr.th_data_len1 << 8) +
269			    hdr.th_data_len0 + sizeof(hdr);
270    if ( (toc.toc_buffer = alloca(toc.toc_alloc_length)) == NULL) {
271	fprintf(stderr,"cannot allocate toc.toc_buffer\n");
272	return -1;
273    }
274
275    bzero (toc.toc_buffer, toc.toc_alloc_length);
276    if (ioctl(cdrom->id, CDROM_TOC_ENTRYS, &toc)) {
277	fprintf(stderr,"ioctl error CDROM_TOC_ENTRYS\n");
278	return -1;
279    }
280
281    cdte =(struct cd_toc_entry *) ((char *) toc.toc_buffer + sizeof(hdr));
282    for (i=0; i <= cdrom->numtracks; ++i) {
283	if (i == cdrom->numtracks ) {
284	    cdrom->track[i].id = 0xAA;;
285	} else {
286	    cdrom->track[i].id = hdr.th_starting_track + i;
287	}
288
289	cdrom->track[i].type =
290	    cdte[i].te_control & CDROM_DATA_TRACK;
291	cdrom->track[i].offset =
292	    cdte[i].te_absaddr.lba.addr3 << 24 |
293	    cdte[i].te_absaddr.lba.addr2 << 16 |
294	    cdte[i].te_absaddr.lba.addr1 << 8  |
295	    cdte[i].te_absaddr.lba.addr0;
296	cdrom->track[i].length = 0;
297	if ( i > 0 ) {
298	    cdrom->track[i - 1].length =
299		cdrom->track[i].offset -
300		cdrom->track[i - 1].offset;
301	}
302    }
303#ifdef DEBUG_CDROM
304  for (i = 0; i <= cdrom->numtracks; i++) {
305    fprintf(stderr,"toc_entry[%d].te_track_number = %d\n",
306	    i,cdte[i].te_track_number);
307    fprintf(stderr,"cdrom->track[%d].id = %d\n", i,cdrom->track[i].id);
308    fprintf(stderr,"cdrom->track[%d].type = %x\n", i,cdrom->track[i].type);
309    fprintf(stderr,"cdrom->track[%d].offset = %d\n", i,cdrom->track[i].offset);
310    fprintf(stderr,"cdrom->track[%d].length = %d\n", i,cdrom->track[i].length);
311  }
312#endif
313    if ( i == (cdrom->numtracks+1) ) {
314	okay = 1;
315    }
316
317    return(okay ? 0 : -1);
318}
319
320/* Get CD-ROM status */
321static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
322{
323    CDstatus                     status;
324    struct cd_sub_channel            sc;
325    struct cd_subc_channel_data     scd;
326
327    sc.sch_address_format = CDROM_LBA_FORMAT;
328    sc.sch_data_format    = CDROM_CURRENT_POSITION;
329    sc.sch_track_number   = 0;
330    sc.sch_alloc_length   = sizeof(scd);
331    sc.sch_buffer         = (caddr_t)&scd;
332    if ( ioctl(cdrom->id, CDROM_READ_SUBCHANNEL, &sc) ) {
333	status = CD_ERROR;
334	fprintf(stderr,"ioctl error CDROM_READ_SUBCHANNEL \n");
335    } else {
336	switch (scd.scd_header.sh_audio_status) {
337	    case AS_AUDIO_INVALID:
338		status = CD_STOPPED;
339		break;
340	    case AS_PLAY_IN_PROGRESS:
341		status = CD_PLAYING;
342		break;
343	    case AS_PLAY_PAUSED:
344		status = CD_PAUSED;
345		break;
346	    case AS_PLAY_COMPLETED:
347		status = CD_STOPPED;
348		break;
349	    case AS_PLAY_ERROR:
350		status = CD_ERROR;
351		break;
352	    case AS_NO_STATUS:
353		status = CD_STOPPED;
354		break;
355	    default:
356		status = CD_ERROR;
357		break;
358	}
359#ifdef DEBUG_CDROM
360  fprintf(stderr,"scd.scd_header.sh_audio_status = %x\n",
361	scd.scd_header.sh_audio_status);
362#endif
363    }
364    if (position) {
365	if (status == CD_PLAYING || (status == CD_PAUSED) ) {
366	    *position =
367		scd.scd_position_data.scp_absaddr.lba.addr3 << 24 |
368		scd.scd_position_data.scp_absaddr.lba.addr2 << 16 |
369		scd.scd_position_data.scp_absaddr.lba.addr1 << 8  |
370		scd.scd_position_data.scp_absaddr.lba.addr0;
371	} else {
372	    *position = 0;
373	}
374    }
375
376    return status;
377}
378
379/* Start play */
380static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
381{
382/*
383 * Play MSF
384 */
385    struct cd_play_audio_msf msf;
386    int end;
387
388    bzero(&msf, sizeof(msf));
389    end = start +length;
390    FRAMES_TO_MSF(start + 150, /* LBA = 4500*M + 75*S + F - 150 */
391		  &msf.msf_starting_M_unit,
392		  &msf.msf_starting_S_unit,
393		  &msf.msf_starting_F_unit);
394    FRAMES_TO_MSF(end + 150, /* LBA = 4500*M + 75*S + F - 150 */
395		  &msf.msf_ending_M_unit,
396		  &msf.msf_ending_S_unit,
397		  &msf.msf_ending_F_unit);
398
399    return(ioctl(cdrom->id, CDROM_PLAY_AUDIO_MSF, &msf));
400}
401
402/* Pause play */
403static int SDL_SYS_CDPause(SDL_CD *cdrom)
404{
405    return(ioctl(cdrom->id, CDROM_PAUSE_PLAY));
406}
407
408/* Resume play */
409static int SDL_SYS_CDResume(SDL_CD *cdrom)
410{
411    return(ioctl(cdrom->id, CDROM_RESUME_PLAY));
412}
413
414/* Stop play */
415static int SDL_SYS_CDStop(SDL_CD *cdrom)
416{
417    return(ioctl(cdrom->id, SCSI_STOP_UNIT));
418}
419
420/* Eject the CD-ROM */
421static int SDL_SYS_CDEject(SDL_CD *cdrom)
422{
423    return(ioctl(cdrom->id, CDROM_EJECT_CADDY));
424}
425
426/* Close the CD-ROM handle */
427static void SDL_SYS_CDClose(SDL_CD *cdrom)
428{
429    close(cdrom->id);
430}
431
432void SDL_SYS_CDQuit(void)
433{
434    int i;
435
436    if ( SDL_numcds > 0 ) {
437	for ( i=0; i<SDL_numcds; ++i ) {
438	    SDL_free(SDL_cdlist[i]);
439	}
440	SDL_numcds = 0;
441    }
442}
443
444#endif /* SDL_CDROM_OSF */
445