SDL-1.2.14
[sdl_omap.git] / src / cdrom / aix / SDL_syscdrom.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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     Carsten Griwodz
20     griff@kom.tu-darmstadt.de
21
22     based on linux/SDL_syscdrom.c by Sam Lantinga
23 */
24 #include "SDL_config.h"
25
26 #ifdef SDL_CDROM_AIX
27
28 /* Functions for system-level CD-ROM audio control */
29
30 /*#define DEBUG_CDROM 1*/
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <unistd.h>
37
38 #include <sys/ioctl.h>
39 #include <sys/devinfo.h>
40 #include <sys/mntctl.h>
41 #include <sys/statfs.h>
42 #include <sys/vmount.h>
43 #include <fstab.h>
44 #include <sys/scdisk.h>
45
46 #include "SDL_cdrom.h"
47 #include "../SDL_syscdrom.h"
48
49 /* The maximum number of CD-ROM drives we'll detect */
50 #define MAX_DRIVES      16      
51
52 /* A list of available CD-ROM drives */
53 static char *SDL_cdlist[MAX_DRIVES];
54 static dev_t SDL_cdmode[MAX_DRIVES];
55
56 /* The system-dependent CD control functions */
57 static const char *SDL_SYS_CDName(int drive);
58 static int         SDL_SYS_CDOpen(int drive);
59 static int         SDL_SYS_CDGetTOC(SDL_CD *cdrom);
60 static CDstatus    SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
61 static int         SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
62 static int         SDL_SYS_CDPause(SDL_CD *cdrom);
63 static int         SDL_SYS_CDResume(SDL_CD *cdrom);
64 static int         SDL_SYS_CDStop(SDL_CD *cdrom);
65 static int         SDL_SYS_CDEject(SDL_CD *cdrom);
66 static void        SDL_SYS_CDClose(SDL_CD *cdrom);
67 static int         SDL_SYS_CDioctl(int id, int command, void *arg);
68
69 /* Check a drive to see if it is a CD-ROM */
70 static int CheckDrive(char *drive, struct stat *stbuf)
71 {
72     int is_cd;
73     int cdfd;
74     int ret;
75     struct devinfo info;
76
77     /* If it doesn't exist, return -1 */
78     if ( stat(drive, stbuf) < 0 ) {
79         return -1;
80     }
81
82     /* If it does exist, verify that it's an available CD-ROM */
83     is_cd = 0;
84     if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
85         cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
86         if ( cdfd >= 0 ) {
87             ret = SDL_SYS_CDioctl( cdfd, IOCINFO, &info );
88             if ( ret < 0 ) {
89                 /* Some kind of error */
90                 is_cd = 0;
91             } else {
92                 if ( info.devtype == DD_CDROM ) {
93                     is_cd = 1;
94                 } else {
95                     is_cd = 0;
96                 }
97             }
98             close(cdfd);
99         }
100 #ifdef DEBUG_CDROM
101         else
102         {
103             fprintf(stderr, "Could not open drive %s (%s)\n", drive, strerror(errno));
104         }
105 #endif
106     }
107     return is_cd;
108 }
109
110 /* Add a CD-ROM drive to our list of valid drives */
111 static void AddDrive(char *drive, struct stat *stbuf)
112 {
113         int i;
114
115         if ( SDL_numcds < MAX_DRIVES ) {
116                 /* Check to make sure it's not already in our list.
117                    This can happen when we see a drive via symbolic link.
118                  */
119                 for ( i=0; i<SDL_numcds; ++i ) {
120                         if ( stbuf->st_rdev == SDL_cdmode[i] ) {
121 #ifdef DEBUG_CDROM
122   fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
123 #endif
124                                 return;
125                         }
126                 }
127
128                 /* Add this drive to our list */
129                 i = SDL_numcds;
130                 SDL_cdlist[i] = SDL_strdup(drive);
131                 if ( SDL_cdlist[i] == NULL ) {
132                         SDL_OutOfMemory();
133                         return;
134                 }
135                 SDL_cdmode[i] = stbuf->st_rdev;
136                 ++SDL_numcds;
137 #ifdef DEBUG_CDROM
138   fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
139 #endif
140         }
141 }
142
143 static void CheckMounts()
144 {
145     char*          buffer;
146     int            bufsz;
147     struct vmount* ptr;
148     int            ret;
149
150     buffer = (char*)SDL_malloc(10);
151     bufsz  = 10;
152     if ( buffer==NULL )
153     {
154         fprintf(stderr, "Could not allocate 10 bytes in aix/SDL_syscdrom.c:CheckMounts\n" );
155         exit ( -10 );
156     }
157
158     do
159     {
160         /* mntctrl() returns an array of all mounted filesystems */
161         ret = mntctl ( MCTL_QUERY, bufsz, buffer );
162         if ( ret == 0 )
163         {
164                                    /* Buffer was too small, realloc.    */
165             bufsz = *(int*)buffer; /* Required size is in first word.   */
166                                    /* (whatever a word is in AIX 4.3.3) */
167                                    /* int seems to be OK in 32bit mode. */
168             SDL_free(buffer);
169             buffer = (char*)SDL_malloc(bufsz);
170             if ( buffer==NULL )
171             {
172                 fprintf(stderr,
173                         "Could not allocate %d bytes in aix/SDL_syscdrom.c:CheckMounts\n",
174                         bufsz );
175                 exit ( -10 );
176             }
177         }
178         else if ( ret < 0 )
179         {
180 #ifdef DEBUG_CDROM
181             fprintf(stderr, "Error reading vmount structures\n");
182 #endif
183             return;
184         }
185     }
186     while ( ret == 0 );
187
188 #ifdef DEBUG_CDROM
189     fprintf ( stderr, "Read %d vmount structures\n",ret );
190 #endif
191     ptr = (struct vmount*)buffer;
192     do
193     {
194             switch(ptr->vmt_gfstype)
195             {
196             case MNT_CDROM :
197                 {
198                     struct stat stbuf;
199                     char*       text;
200
201                     text = (char*)ptr + ptr->vmt_data[VMT_OBJECT].vmt_off;
202 #ifdef DEBUG_CDROM
203   fprintf(stderr, "Checking mount path: %s mounted on %s\n",
204         text, (char*)ptr + ptr->vmt_data[VMT_STUB].vmt_off );
205 #endif
206                     if ( CheckDrive( text, &stbuf) > 0)
207                     {
208                         AddDrive( text, &stbuf);
209                     }
210                 }
211                 break;
212             default :
213                 break;
214             }
215             ptr = (struct vmount*)((char*)ptr + ptr->vmt_length);
216             ret--;
217     }
218     while ( ret > 0 );
219
220     free ( buffer );
221 }
222
223 static int CheckNonmounts()
224 {
225 #ifdef _THREAD_SAFE
226     AFILE_t      fsFile = NULL;
227     int          passNo = 0;
228     int          ret;
229     struct fstab entry;
230     struct stat  stbuf;
231
232     ret = setfsent_r( &fsFile, &passNo );
233     if ( ret != 0 ) return -1;
234     do
235     {
236         ret = getfsent_r ( &entry, &fsFile, &passNo );
237         if ( ret == 0 ) {
238             char* l = SDL_strrchr(entry.fs_spec,'/');
239             if ( l != NULL ) {
240                 if ( !SDL_strncmp("cd",++l,2) ) {
241 #ifdef DEBUG_CDROM
242                     fprintf(stderr,
243                             "Found unmounted CD ROM drive with device name %s\n",
244                             entry.fs_spec);
245 #endif
246                     if ( CheckDrive( entry.fs_spec, &stbuf) > 0)
247                     {
248                         AddDrive( entry.fs_spec, &stbuf);
249                     }
250                 }
251             }
252         }
253     }
254     while ( ret == 0 );
255     ret = endfsent_r ( &fsFile );
256     if ( ret != 0 ) return -1;
257     return 0;
258 #else
259     struct fstab* entry;
260     struct stat  stbuf;
261
262     setfsent();
263     do
264     {
265         entry = getfsent();
266         if ( entry != NULL ) {
267             char* l = SDL_strrchr(entry->fs_spec,'/');
268             if ( l != NULL ) {
269                 if ( !SDL_strncmp("cd",++l,2) ) {
270 #ifdef DEBUG_CDROM
271                     fprintf(stderr,"Found unmounted CD ROM drive with device name %s", entry->fs_spec);
272 #endif
273                     if ( CheckDrive( entry->fs_spec, &stbuf) > 0)
274                     {
275                         AddDrive( entry->fs_spec, &stbuf);
276                     }
277                 }
278             }
279         }
280     }
281     while ( entry != NULL );
282     endfsent();
283 #endif
284 }
285
286 int  SDL_SYS_CDInit(void)
287 {
288         char *SDLcdrom;
289         struct stat stbuf;
290
291         /* Fill in our driver capabilities */
292         SDL_CDcaps.Name = SDL_SYS_CDName;
293         SDL_CDcaps.Open = SDL_SYS_CDOpen;
294         SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
295         SDL_CDcaps.Status = SDL_SYS_CDStatus;
296         SDL_CDcaps.Play = SDL_SYS_CDPlay;
297         SDL_CDcaps.Pause = SDL_SYS_CDPause;
298         SDL_CDcaps.Resume = SDL_SYS_CDResume;
299         SDL_CDcaps.Stop = SDL_SYS_CDStop;
300         SDL_CDcaps.Eject = SDL_SYS_CDEject;
301         SDL_CDcaps.Close = SDL_SYS_CDClose;
302
303         /* Look in the environment for our CD-ROM drive list */
304         SDLcdrom = SDL_getenv("SDL_CDROM");     /* ':' separated list of devices */
305         if ( SDLcdrom != NULL ) {
306                 char *cdpath, *delim;
307                 size_t len = SDL_strlen(SDLcdrom)+1;
308                 cdpath = SDL_stack_alloc(char, len);
309                 if ( cdpath != NULL ) {
310                         SDL_strlcpy(cdpath, SDLcdrom, len);
311                         SDLcdrom = cdpath;
312                         do {
313                                 delim = SDL_strchr(SDLcdrom, ':');
314                                 if ( delim ) {
315                                         *delim++ = '\0';
316                                 }
317 #ifdef DEBUG_CDROM
318   fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom);
319 #endif
320                                 if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
321                                         AddDrive(SDLcdrom, &stbuf);
322                                 }
323                                 if ( delim ) {
324                                         SDLcdrom = delim;
325                                 } else {
326                                         SDLcdrom = NULL;
327                                 }
328                         } while ( SDLcdrom );
329                         SDL_stack_free(cdpath);
330                 }
331
332                 /* If we found our drives, there's nothing left to do */
333                 if ( SDL_numcds > 0 ) {
334                         return(0);
335                 }
336         }
337
338         CheckMounts();
339         CheckNonmounts();
340
341         return 0;
342 }
343
344 /* General ioctl() CD-ROM command function */
345 static int SDL_SYS_CDioctl(int id, int command, void *arg)
346 {
347     int retval;
348
349     retval = ioctl(id, command, arg);
350     if ( retval < 0 ) {
351         SDL_SetError("ioctl() error: %s", strerror(errno));
352     }
353     return retval;
354 }
355
356 static const char *SDL_SYS_CDName(int drive)
357 {
358         return(SDL_cdlist[drive]);
359 }
360
361 static int SDL_SYS_CDOpen(int drive)
362 {
363     int   fd;
364     char* lastsl;
365     char* cdromname;
366     size_t len;
367
368     /*
369      * We found /dev/cd? drives and that is in our list. But we can
370      * open only the /dev/rcd? versions of those devices for Audio CD.
371      */
372     len = SDL_strlen(SDL_cdlist[drive])+2;
373     cdromname = (char*)SDL_malloc(len);
374     SDL_strlcpy(cdromname,SDL_cdlist[drive],len);
375     lastsl = SDL_strrchr(cdromname,'/');
376     if (lastsl) {
377         *lastsl = 0;
378         SDL_strlcat(cdromname,"/r",len);
379         lastsl = SDL_strrchr(SDL_cdlist[drive],'/');
380         if (lastsl) {
381             lastsl++;
382             SDL_strlcat(cdromname,lastsl,len);
383         }
384     }
385
386 #ifdef DEBUG_CDROM
387     fprintf(stderr, "Should open drive %s, opening %s\n", SDL_cdlist[drive], cdromname);
388 #endif
389
390     /*
391      * Use exclusive access. Don't use SC_DIAGNOSTICS as xmcd does because they
392      * require root priviledges, and we don't want that. SC_SINGLE provides
393      * exclusive access with less trouble.
394      */
395     fd = openx(cdromname, O_RDONLY, NULL, SC_SINGLE);
396     if ( fd < 0 )
397     {
398 #ifdef DEBUG_CDROM
399             fprintf(stderr, "Could not open drive %s (%s)\n", cdromname, strerror(errno));
400 #endif
401     }
402     else
403     {
404         struct mode_form_op cdMode;
405         int                 ret;
406 #ifdef DEBUG_CDROM
407         cdMode.action = CD_GET_MODE;
408         ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode);
409         if ( ret < 0 ) {
410             fprintf(stderr,
411                     "Could not get drive mode for %s (%s)\n",
412                     cdromname, strerror(errno));
413         } else {
414             switch(cdMode.cd_mode_form) {
415                 case CD_MODE1 :
416                     fprintf(stderr,
417                         "Drive mode for %s is %s\n",
418                         cdromname, "CD-ROM Data Mode 1");
419                     break;
420                 case CD_MODE2_FORM1 :
421                     fprintf(stderr,
422                         "Drive mode for %s is %s\n",
423                         cdromname, "CD-ROM XA Data Mode 2 Form 1");
424                     break;
425                 case CD_MODE2_FORM2 :
426                     fprintf(stderr,
427                         "Drive mode for %s is %s\n",
428                         cdromname, "CD-ROM XA Data Mode 2 Form 2");
429                     break;
430                 case CD_DA :
431                     fprintf(stderr,
432                         "Drive mode for %s is %s\n",
433                         cdromname, "CD-DA");
434                     break;
435                 default :
436                     fprintf(stderr,
437                         "Drive mode for %s is %s\n",
438                         cdromname, "unknown");
439                     break;
440             }
441         }
442 #endif
443
444         cdMode.action       = CD_CHG_MODE;
445         cdMode.cd_mode_form = CD_DA;
446         ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode);
447         if ( ret < 0 ) {
448 #ifdef DEBUG_CDROM
449             fprintf(stderr,
450                     "Could not set drive mode for %s (%s)\n",
451                     cdromname, strerror(errno));
452 #endif
453             SDL_SetError("ioctl() error: Could not set CD drive mode, %s",
454                          strerror(errno));
455         } else {
456 #ifdef DEBUG_CDROM
457             fprintf(stderr,
458                     "Drive mode for %s set to CD_DA\n",
459                     cdromname);
460 #endif
461         }
462     }
463     SDL_free(cdromname);
464     return fd;
465 }
466
467 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
468 {
469     struct cd_audio_cmd cmd;
470     struct cd_audio_cmd entry;
471     int                 i;
472     int                 okay;
473
474     cmd.audio_cmds = CD_TRK_INFO_AUDIO;
475     cmd.msf_flag   = FALSE;
476     if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) {
477         return -1;
478     }
479
480     okay = 0;
481     cdrom->numtracks = cmd.indexing.track_index.last_track
482                      - cmd.indexing.track_index.first_track+1;
483     if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
484         cdrom->numtracks = SDL_MAX_TRACKS;
485     }
486
487     /* Read all the track TOC entries */
488     for ( i=0; i<=cdrom->numtracks; ++i ) {
489         if ( i == cdrom->numtracks ) {
490             cdrom->track[i].id = 0xAA;;
491         } else {
492             cdrom->track[i].id = cmd.indexing.track_index.first_track+i;
493         }
494         entry.audio_cmds         = CD_GET_TRK_MSF;
495         entry.indexing.track_msf.track = cdrom->track[i].id;
496         if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &entry) < 0 ) {
497             break;
498         } else {
499             cdrom->track[i].type = 0;    /* don't know how to detect 0x04 data track */
500             cdrom->track[i].offset = MSF_TO_FRAMES(
501                 entry.indexing.track_msf.mins,
502                 entry.indexing.track_msf.secs,
503                 entry.indexing.track_msf.frames);
504             cdrom->track[i].length = 0;
505             if ( i > 0 ) {
506                 cdrom->track[i-1].length = cdrom->track[i].offset
507                                          - cdrom->track[i-1].offset;
508             }
509         }
510     }
511     if ( i == (cdrom->numtracks+1) ) {
512         okay = 1;
513     }
514     return(okay ? 0 : -1);
515 }
516
517 /* Get CD-ROM status */
518 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
519 {
520     CDstatus            status;
521     struct cd_audio_cmd cmd;
522     cmd.audio_cmds = CD_INFO_AUDIO;
523
524     if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) {
525 #ifdef DEBUG_CDROM
526     fprintf(stderr, "ioctl failed in SDL_SYS_CDStatus (%s)\n", SDL_GetError());
527 #endif
528         status = CD_ERROR;
529     } else {
530         switch (cmd.status) {
531             case CD_NO_AUDIO:
532             case CD_COMPLETED:
533                 status = CD_STOPPED;
534                 break;
535             case CD_PLAY_AUDIO:
536                 status = CD_PLAYING;
537                 break;
538             case CD_PAUSE_AUDIO:
539                 status = CD_PAUSED;
540                 break;
541             case CD_NOT_VALID:
542 #ifdef DEBUG_CDROM
543     fprintf(stderr, "cdStatus failed with CD_NOT_VALID\n");
544 #endif
545                 status = CD_ERROR;
546                 break;
547             case CD_STATUS_ERROR:
548 #ifdef DEBUG_CDROM
549     fprintf(stderr, "cdStatus failed with CD_STATUS_ERROR\n");
550 #endif
551                 status = CD_ERROR;
552                 break;
553             default:
554 #ifdef DEBUG_CDROM
555     fprintf(stderr, "cdStatus failed with unknown error\n");
556 #endif
557                 status = CD_ERROR;
558                 break;
559         }
560     }
561     if ( position ) {
562         if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
563             *position = MSF_TO_FRAMES( cmd.indexing.info_audio.current_mins,
564                                        cmd.indexing.info_audio.current_secs,
565                                        cmd.indexing.info_audio.current_frames);
566         } else {
567             *position = 0;
568         }
569     }
570     return status;
571 }
572
573 /* Start play */
574 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
575 {
576     struct cd_audio_cmd cmd;
577
578     /*
579      * My CD Rom is muted by default. I think I read that this is new with
580      * AIX 4.3. SDL does not change the volume, so I need a kludge. Maybe
581      * its better to do this elsewhere?
582      */
583     cmd.audio_cmds = CD_PLAY_AUDIO | CD_SET_VOLUME;
584     cmd.msf_flag   = TRUE;
585     FRAMES_TO_MSF(start,
586                   &cmd.indexing.msf.first_mins,
587                   &cmd.indexing.msf.first_secs,
588                   &cmd.indexing.msf.first_frames);
589     FRAMES_TO_MSF(start+length,
590                   &cmd.indexing.msf.last_mins,
591                   &cmd.indexing.msf.last_secs,
592                   &cmd.indexing.msf.last_frames);
593     cmd.volume_type     = CD_VOLUME_ALL;
594     cmd.all_channel_vol = 255;   /* This is a uchar. What is a good value? No docu! */
595     cmd.out_port_0_sel  = CD_AUDIO_CHNL_0;
596     cmd.out_port_1_sel  = CD_AUDIO_CHNL_1;
597     cmd.out_port_2_sel  = CD_AUDIO_CHNL_2;
598     cmd.out_port_3_sel  = CD_AUDIO_CHNL_3;
599
600 #ifdef DEBUG_CDROM
601   fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
602         cmd.indexing.msf.first_mins,
603         cmd.indexing.msf.first_secs,
604         cmd.indexing.msf.first_frames,
605         cmd.indexing.msf.last_mins,
606         cmd.indexing.msf.last_secs,
607         cmd.indexing.msf.last_frames);
608 #endif
609         return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
610 }
611
612 /* Pause play */
613 static int SDL_SYS_CDPause(SDL_CD *cdrom)
614 {
615     struct cd_audio_cmd cmd;
616     cmd.audio_cmds = CD_PAUSE_AUDIO;
617     return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
618 }
619
620 /* Resume play */
621 static int SDL_SYS_CDResume(SDL_CD *cdrom)
622 {
623     struct cd_audio_cmd cmd;
624     cmd.audio_cmds = CD_RESUME_AUDIO;
625     return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
626 }
627
628 /* Stop play */
629 static int SDL_SYS_CDStop(SDL_CD *cdrom)
630 {
631     struct cd_audio_cmd cmd;
632     cmd.audio_cmds = CD_STOP_AUDIO;
633     return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
634 }
635
636 /* Eject the CD-ROM */
637 static int SDL_SYS_CDEject(SDL_CD *cdrom)
638 {
639     return(SDL_SYS_CDioctl(cdrom->id, DKEJECT, 0));
640 }
641
642 /* Close the CD-ROM handle */
643 static void SDL_SYS_CDClose(SDL_CD *cdrom)
644 {
645     close(cdrom->id);
646 }
647
648 void SDL_SYS_CDQuit(void)
649 {
650         int i;
651
652         if ( SDL_numcds > 0 ) {
653                 for ( i=0; i<SDL_numcds; ++i ) {
654                         SDL_free(SDL_cdlist[i]);
655                 }
656                 SDL_numcds = 0;
657         }
658 }
659
660 #endif /* SDL_CDROM_AIX */