File indexing completed on 2024-04-14 04:49:19

0001 /*
0002  * This file is part of WorkMan, the civilized CD player library
0003  * Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
0004  * Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public
0017  * License along with this library; if not, write to the Free
0018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0019  *
0020  *
0021  * Sun-specific drive control routines.
0022  */
0023 
0024 #if defined(sun) || defined(__sun)
0025 
0026 #include "include/wm_config.h"
0027 #include "include/wm_helpers.h"
0028 #include "include/wm_cdrom.h"
0029 #include "include/wm_cdtext.h"
0030 
0031 #include <errno.h>
0032 #include <stdio.h>
0033 #include <sys/types.h>
0034 #include <fcntl.h>
0035 #include <string.h>
0036 #include <sys/param.h>
0037 #include <sys/stat.h>
0038 #include <sys/time.h>
0039 #include <sys/ioctl.h>
0040 
0041 #include <sys/statvfs.h>
0042 #include <unistd.h>
0043 #include <signal.h>
0044 #ifdef solbourne
0045 # include <mfg/dklabel.h>
0046 # include <mfg/dkio.h>
0047 # include <sys/unistd.h>
0048 # include <dev/srvar.h>
0049 #else /* A real Sun */
0050 # ifdef SYSV
0051 #  include <poll.h>
0052 #  include <stdlib.h>
0053 #  include <sys/cdio.h>
0054 #  include <sys/socket.h>
0055 #  include <sys/scsi/impl/uscsi.h>
0056 #  include "include/wm_cdda.h"
0057 # else
0058 #  include <sys/buf.h>
0059 #  include <sun/dkio.h>
0060 #  include <scsi/targets/srdef.h>
0061 #  include <scsi/impl/uscsi.h>
0062 #  include <scsi/generic/commands.h>
0063 # endif
0064 #endif
0065 
0066 #include "include/wm_struct.h"
0067 
0068 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0069 
0070 int min_volume = 0;
0071 int max_volume = 255;
0072 
0073 static const char *sun_cd_device = NULL;
0074 extern int  intermittent_dev;
0075 
0076 int current_end;
0077 
0078 #if defined(SYSV) && defined(SIGTHAW)
0079 #ifdef __GNUC__
0080 void sigthawinit(void) __attribute__ ((constructor));
0081 #else
0082 #pragma init(sigthawinit)
0083 #endif /* GNUC */
0084 
0085 static int last_left, last_right;
0086 static struct wm_drive *thecd = NULL;
0087 
0088 /*
0089  * Handling for Sun's Suspend functionality
0090  */
0091 static void
0092 thawme(int sig)
0093 {
0094 // Just leave this line in as a reminder for a missing
0095 // functionality in the GUI.
0096 // change_mode(NULL, WM_CDM_STOPPED, NULL);
0097   codec_init();
0098   if( thecd )
0099     gen_set_volume(thecd, last_left, last_right);
0100 } /* thawme() */
0101 
0102 void
0103 sigthawinit( void )
0104 {
0105   struct sigaction sa;
0106 
0107   sa.sa_handler = thawme;
0108   sigemptyset(&sa.sa_mask);
0109   sa.sa_flags = 0;
0110 
0111   sigaction(SIGTHAW, &sa, NULL);
0112 } /* sigthawinit() */
0113 
0114 #endif /* SYSV && SIGTHAW */
0115 
0116 /*
0117  * find_cdrom
0118  *
0119  * Determine the name of the CD-ROM device.
0120  *
0121  * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0
0122  * that exists.  (Check for /vol/dev/aliases, not cdrom0, since it won't be
0123  * there if there's no CD in the drive.)  This is done so a single SunOS 4.x
0124  * binary can be used on any 4.x or higher Sun system.
0125  */
0126 const char*
0127 find_cdrom()
0128 {
0129   if (access("/vol/dev/aliases", X_OK) == 0)
0130     {
0131       /* Volume manager.  Device might not be there. */
0132       intermittent_dev = 1;
0133 
0134       /* If vold is running us, it'll tell us the device name. */
0135       sun_cd_device = getenv("VOLUME_DEVICE");
0136       /*
0137       ** the path of the device has to include /dev
0138       ** otherwise we are vulnerable to race conditions
0139       ** Thomas Biege <thomas@suse.de>
0140       */
0141       if (sun_cd_device == NULL ||
0142       strncmp("/vol/dev/", sun_cd_device, 9) ||
0143       strstr(sun_cd_device, "/../") )
0144     return "/vol/dev/aliases/cdrom0";
0145       else
0146         return sun_cd_device;
0147     }
0148   else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0)
0149     {
0150       /* Solaris 2.x w/o volume manager. */
0151       return "/dev/rdsk/c0t6d0s2";
0152     }
0153   else if (access("/dev/rcd0", F_OK) == 0)
0154     {
0155       return "/dev/rcd0";
0156     }
0157   else if (access("/dev/rsr0", F_OK) == 0)
0158     return "/dev/rsr0";
0159   else
0160     {
0161       fprintf(stderr, "Could not find a CD device!\n");
0162       return NULL;
0163     }
0164 } /* find_cdrom() */
0165 
0166 /*
0167  * Initialize the drive.  A no-op for the generic driver.
0168  */
0169 int
0170 gen_init( struct wm_drive *d )
0171 {
0172   codec_init();
0173   return 0;
0174 } /* gen_init() */
0175 
0176 
0177 /*
0178  * Open the CD device and figure out what kind of drive is attached.
0179  */
0180 int
0181 gen_open( struct wm_drive *d )
0182 {
0183     static int warned = 0;
0184 
0185     if (d->fd >= 0) {       /* Device already open? */
0186         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (fd=%d)]\n", d->fd);
0187         return 0;
0188     }
0189 
0190 
0191     d->fd = open(d->cd_device, 0);
0192     if (d->fd < 0) {
0193         /* Solaris 2.2 volume manager moves links around */
0194         if (errno == ENOENT && intermittent_dev)
0195             return 1;
0196 
0197         if (errno == EACCES) {
0198             if (!warned) {
0199               /* char realname[MAXPATHLEN];
0200                 if (realpath(cd_device, realname) == NULL) {
0201                     perror("realpath");
0202                     return 1;
0203                 } */
0204                 return -EACCES;
0205             }
0206         } else if (errno != ENXIO) {
0207             return -6;
0208         }
0209 
0210         /* No CD in drive. */
0211         return 1;
0212     }
0213 
0214     thecd = d;
0215 
0216   return 0;
0217 }
0218 
0219 /*
0220  * Send an arbitrary SCSI command out the bus and optionally wait for
0221  * a reply if "retbuf" isn't NULL.
0222  */
0223 int
0224 gen_scsi( struct wm_drive *d,
0225      unsigned char *cdb,
0226      int cdblen, void *retbuf,
0227      int retbuflen, int getreply )
0228 {
0229 #ifndef solbourne
0230   char          x;
0231   struct uscsi_cmd  cmd;
0232 
0233   memset(&cmd, 0, sizeof(cmd));
0234   cmd.uscsi_cdb = (void *) cdb;
0235   cmd.uscsi_cdblen = cdblen;
0236   cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x;
0237   cmd.uscsi_buflen = retbuf ? retbuflen : 0;
0238   cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT;
0239   if (getreply)
0240     cmd.uscsi_flags |= USCSI_READ;
0241 
0242   if (ioctl(d->fd, USCSICMD, &cmd))
0243     return -1;
0244 
0245   if (cmd.uscsi_status)
0246     return -1;
0247 
0248   return 0;
0249 #else
0250   return -1;
0251 #endif
0252 }
0253 
0254 int
0255 gen_close( struct wm_drive *d )
0256 {
0257   if(d->fd != -1) {
0258     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
0259     close(d->fd);
0260     d->fd = -1;
0261   }
0262   return 0;
0263 }
0264 
0265 /* Alarm signal handler. */
0266 static void do_nothing( int x ) { x++; }
0267 
0268 /*
0269  * Get the current status of the drive: the current play mode, the absolute
0270  * position from start of disc (in frames), and the current track and index
0271  * numbers if the CD is playing or paused.
0272  */
0273 int
0274 gen_get_drive_status( struct wm_drive *d,
0275               int oldmode,
0276               int *mode,
0277               int *pos, int *track, int *index )
0278 {
0279   struct cdrom_subchnl      sc;
0280   struct itimerval      old_timer, new_timer;
0281   struct sigaction      old_sig, new_sig;
0282 
0283   /* If we can't get status, the CD is ejected, so default to that. */
0284   *mode = WM_CDM_EJECTED;
0285 
0286   /* Is the device open? */
0287   if (d->fd < 0) {
0288     switch (d->proto.open(d)) {
0289     case -1:    /* error */
0290       return -1;
0291 
0292     case 1:     /* retry */
0293       return 0;
0294     }
0295   }
0296 
0297   /*
0298    * Solaris 2.2 hangs on this ioctl if someone else ejects the CD.
0299    * So we schedule a signal to break out of the hang if the call
0300    * takes an unreasonable amount of time.  The signal handler and
0301    * timer are restored immediately to avoid interfering with XView.
0302    */
0303   if (intermittent_dev) {
0304     /*
0305     * First clear out the timer so XView's signal doesn't happen
0306     * while we're diddling with the signal handler.
0307     */
0308     timerclear(&new_timer.it_interval);
0309     timerclear(&new_timer.it_value);
0310     setitimer(ITIMER_REAL, &new_timer, &old_timer);
0311 
0312     /*
0313     * Now install the no-op signal handler.
0314     */
0315     new_sig.sa_handler = do_nothing;
0316     memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask));
0317     new_sig.sa_flags = 0;
0318     if (sigaction(SIGALRM, &new_sig, &old_sig))
0319       perror("sigaction");
0320 
0321     /*
0322     * And finally, set the timer.
0323     */
0324     new_timer.it_value.tv_sec = 2;
0325     setitimer(ITIMER_REAL, &new_timer, NULL);
0326   }
0327 
0328   sc.cdsc_format = CDROM_MSF;
0329 
0330   if (ioctl(d->fd, CDROMSUBCHNL, &sc)) {
0331     if (intermittent_dev) {
0332       sigaction(SIGALRM, &old_sig, NULL);
0333       setitimer(ITIMER_REAL, &old_timer, NULL);
0334 
0335       /* If the device can disappear, let it do so. */
0336       d->proto.close(d);
0337     }
0338 
0339     return 0;
0340   }
0341 
0342   if (intermittent_dev) {
0343     sigaction(SIGALRM, &old_sig, NULL);
0344     setitimer(ITIMER_REAL, &old_timer, NULL);
0345   }
0346 
0347   switch (sc.cdsc_audiostatus) {
0348   case CDROM_AUDIO_PLAY:
0349     *mode = WM_CDM_PLAYING;
0350     *track = sc.cdsc_trk;
0351     *index = sc.cdsc_ind;
0352     *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
0353       sc.cdsc_absaddr.msf.second * 75 +
0354       sc.cdsc_absaddr.msf.frame;
0355     break;
0356 
0357   case CDROM_AUDIO_PAUSED:
0358   case CDROM_AUDIO_INVALID:
0359   case CDROM_AUDIO_NO_STATUS:
0360     if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
0361       {
0362     *mode = WM_CDM_PAUSED;
0363     *track = sc.cdsc_trk;
0364     *index = sc.cdsc_ind;
0365     *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
0366       sc.cdsc_absaddr.msf.second * 75 +
0367       sc.cdsc_absaddr.msf.frame;
0368       }
0369     else
0370       *mode = WM_CDM_STOPPED;
0371     break;
0372 
0373     /* CD ejected manually during play. */
0374   case CDROM_AUDIO_ERROR:
0375     break;
0376 
0377   case CDROM_AUDIO_COMPLETED:
0378     *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
0379     break;
0380 
0381   default:
0382     *mode = WM_CDM_UNKNOWN;
0383     break;
0384   }
0385 
0386   return 0;
0387 } /* gen_get_drive_status() */
0388 
0389 /*
0390  * Get the number of tracks on the CD.
0391  */
0392 int
0393 gen_get_trackcount( struct wm_drive *d, int *tracks )
0394 {
0395   struct cdrom_tochdr   hdr;
0396 
0397   if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
0398     return -1;
0399 
0400   *tracks = hdr.cdth_trk1;
0401   return 0;
0402 } /* gen_get_trackcount() */
0403 
0404 /*
0405  * Get the start time and mode (data or audio) of a track.
0406  */
0407 int
0408 gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
0409 {
0410   struct cdrom_tocentry entry;
0411 
0412   entry.cdte_track = track;
0413   entry.cdte_format = CDROM_MSF;
0414 
0415   if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
0416     return -1;
0417 
0418   *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
0419     entry.cdte_addr.msf.second * 75 +
0420     entry.cdte_addr.msf.frame;
0421   *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
0422 
0423   return 0;
0424 } /* gen_get_trackinfo() */
0425 
0426 /*
0427  * Get the number of frames on the CD.
0428  */
0429 int
0430 gen_get_cdlen(struct wm_drive *d, int *frames )
0431 {
0432   int       tmp;
0433 
0434   return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
0435 } /* gen_get_cdlen() */
0436 
0437 /*
0438  * Play the CD from one position to another.
0439  *
0440  *  d       Drive structure.
0441  *  start       Frame to start playing at.
0442  *  end     End of this chunk.
0443  */
0444 int
0445 gen_play( struct wm_drive *d, int start, int end)
0446 {
0447   struct cdrom_msf      msf;
0448   unsigned char         cmdbuf[10];
0449 
0450   current_end = end;
0451 
0452   msf.cdmsf_min0 = start / (60*75);
0453   msf.cdmsf_sec0 = (start % (60*75)) / 75;
0454   msf.cdmsf_frame0 = start % 75;
0455   msf.cdmsf_min1 = end / (60*75);
0456   msf.cdmsf_sec1 = (end % (60*75)) / 75;
0457   msf.cdmsf_frame1 = end % 75;
0458 
0459   codec_start();
0460   if (ioctl(d->fd, CDROMSTART))
0461     return -1;
0462   if (ioctl(d->fd, CDROMPLAYMSF, &msf))
0463     return -2;
0464 
0465   return 0;
0466 } /* gen_play() */
0467 
0468 /*
0469  * Pause the CD.
0470  */
0471 int
0472 gen_pause( struct wm_drive *d )
0473 {
0474   codec_stop();
0475   return (ioctl(d->fd, CDROMPAUSE));
0476 } /* gen_pause() */
0477 
0478 /*
0479  * Resume playing the CD (assuming it was paused.)
0480  */
0481 int
0482 gen_resume( struct wm_drive *d )
0483 {
0484   codec_start();
0485   return (ioctl(d->fd, CDROMRESUME));
0486 } /* gen_resume() */
0487 
0488 /*
0489  * Stop the CD.
0490  */
0491 int
0492 gen_stop( struct wm_drive *d )
0493 {
0494   codec_stop();
0495   return (ioctl(d->fd, CDROMSTOP));
0496 } /* gen_stop() */
0497 
0498 /*
0499  * Eject the current CD, if there is one.
0500  */
0501 int
0502 gen_eject( struct wm_drive *d )cddax
0503 {
0504   struct stat   stbuf;
0505   struct statvfs ust;
0506 
0507   if (fstat(d->fd, &stbuf) != 0)
0508     return -2;
0509 
0510   /* Is this a mounted filesystem? */
0511   if (statvfs(stbuf.st_rdev, &ust) == 0)
0512     return -3;
0513 
0514   if (ioctl(d->fd, CDROMEJECT))
0515     return -1;
0516 
0517   /* Close the device if it needs to vanish. */
0518   if (intermittent_dev) {
0519     d->proto.close(d);
0520     /* Also remember to tell the cddaslave since volume
0521     manager switches links around on us */
0522     if (d->cdda_slave > -1) {
0523       write(d->cdda_slave, "E", 1);
0524     }
0525   }
0526 
0527   return 0;
0528 } /* gen_eject() */
0529 
0530 /*----------------------------------------*
0531  * Close the CD tray
0532  *
0533  * Please edit and send changes to
0534  * milliByte@DeathsDoor.com
0535  *----------------------------------------*/
0536 
0537 int
0538 gen_closetray(struct wm_drive *d)
0539 {
0540     return -1;
0541 } /* gen_closetray() */
0542 
0543 /*
0544  * Set the volume level for the left and right channels.  Their values
0545  * range from 0 to 100.
0546  */
0547 int
0548 gen_set_volume( struct wm_drive *d, int left, int right )
0549 {
0550   struct cdrom_volctrl v;
0551 
0552 #if defined(SIGTHAW) && defined(SYSV)
0553   last_left = left;
0554   last_right = right;
0555   thecd = d;
0556 #endif
0557 
0558   CDDARETURN(d) cdda_set_volume(d, left, right);
0559 
0560   left = (left * (max_volume - min_volume)) / 100 + min_volume;
0561   right = (right * (max_volume - min_volume)) / 100 + min_volume;
0562 
0563   v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left;
0564   v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right;
0565 
0566   return (ioctl(d->fd, CDROMVOLCTRL, &v));
0567 } /* gen_set_volume() */
0568 
0569 /*
0570  * Read the volume from the drive, if available.  Each channel
0571  * ranges from 0 to 100, with -1 indicating data not available.
0572  */
0573 int
0574 gen_get_volume( struct wm_drive *d, int *left, int *right )
0575 {
0576   CDDARETURN(d) cdda_get_volume(d, left, right);
0577 
0578   *left = *right = -1;
0579 
0580   return (wm_scsi2_get_volume(d, left, right));
0581 } /* gen_get_volume() */
0582 
0583 #define CDDABLKSIZE 2368
0584 #define SAMPLES_PER_BLK 588
0585 
0586 /*
0587  * This is the fastest way to convert from BCD to 8-bit.
0588  */
0589 static unsigned char unbcd[256] = {
0590   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,0,0,0,0,0,
0591  10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,0,0,0,0,0,
0592  20, 21, 22, 23, 24, 25, 26, 27, 28, 29,  0,0,0,0,0,0,
0593  30, 31, 32, 33, 34, 35, 36, 37, 38, 39,  0,0,0,0,0,0,
0594  40, 41, 42, 43, 44, 45, 46, 47, 48, 49,  0,0,0,0,0,0,
0595  50, 51, 52, 53, 54, 55, 56, 57, 58, 59,  0,0,0,0,0,0,
0596  60, 61, 62, 63, 64, 65, 66, 67, 68, 69,  0,0,0,0,0,0,
0597  70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  0,0,0,0,0,0,
0598  80, 81, 82, 83, 84, 85, 86, 87, 88, 89,  0,0,0,0,0,0,
0599  90, 91, 92, 93, 94, 95, 96, 97, 98, 99,  0,0,0,0,0,0,
0600  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0601  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0602  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0603  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0604  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0605  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0606 };
0607 /*
0608  * Try to initialize the CDDA slave.  Returns 0 on success.
0609  */
0610 int
0611 gen_cdda_init( struct wm_drive *d )
0612 {
0613     enable_cdda_controls(1);
0614     return 0;
0615 }
0616 
0617 /*
0618  * Initialize the CDDA data buffer and open the appropriate device.
0619  *
0620  */
0621 int gen_cdda_open(struct wm_drive *d)
0622 {
0623     int i;
0624     struct cdrom_cdda cdda;
0625 
0626     if (d->fd < 0)
0627         return -1;
0628 
0629     for (i = 0; i < d->numblocks; ++i) {
0630         d->blocks[i].buflen = d->frames_at_once * CDDABLKSIZE;
0631         d->blocks[i].buf = malloc(d->blocks[i].buflen);
0632         if (!d->blocks[i].buf) {
0633             ERRORLOG("plat_cdda_open: ENOMEM\n");
0634             return -ENOMEM;
0635         }
0636     }
0637 
0638     cdda.cdda_addr = 200;
0639     cdda.cdda_length = 1;
0640     cdda.cdda_data = d->blocks[0].buf;
0641     cdda.cdda_subcode = CDROM_DA_SUBQ;
0642 
0643     d->status = WM_CDM_STOPPED;
0644     if((ioctl(d->fd, CDROMCDDA, &cdda) < 0)) {
0645         return -1;
0646     }
0647 
0648     return 0;
0649 }
0650 
0651 /*
0652  * Normalize a bunch of CDDA data.  Basically this means ripping out the
0653  * Q subchannel data and doing byte-swapping, since the CD audio is in
0654  * littleendian format.
0655  *
0656  * Scanning is handled here too.
0657  *
0658  * XXX - do byte swapping on Intel boxes?
0659  */
0660 long
0661 sun_normalize(struct cdda_block *block)
0662 {
0663     int i, nextq;
0664     long buflen = block->buflen;
0665     int blocks = buflen / CDDABLKSIZE;
0666     unsigned char *rawbuf = block->buf;
0667     unsigned char *dest = rawbuf;
0668     unsigned char tmp;
0669     long *buf32 = (long *)rawbuf, tmp32;
0670 
0671 /*
0672  * this was #ifndef LITTLEENDIAN
0673  * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
0674  */
0675 #if WM_BIG_ENDIAN
0676     if (blocks--) {
0677         for (i = 0; i < SAMPLES_PER_BLK * 2; ++i) {
0678             /* Only need to use temp buffer on first block. */
0679             tmp = *rawbuf++;
0680             *dest++ = *rawbuf++;
0681             *dest++ = tmp;
0682         }
0683     }
0684 #endif
0685 
0686     while (blocks--) {
0687         /* Skip over Q data. */
0688         rawbuf += 16;
0689 
0690         for (i = 0; i < SAMPLES_PER_BLK * 2; i++) {
0691 #if WM_LITTLE_ENDIAN
0692             *dest++ = *rawbuf++;
0693             *dest++ = *rawbuf++;
0694 #else
0695             *dest++ = rawbuf[1];
0696             *dest++ = rawbuf[0];
0697             rawbuf += 2;
0698 #endif
0699         }
0700     }
0701 
0702     buflen -= ((buflen / CDDABLKSIZE) * 16);
0703 
0704     return buflen;
0705 }
0706 
0707 /*
0708  * Read some blocks from the CD.  Stop if we hit the end of the current region.
0709  *
0710  * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
0711  */
0712 int gen_cdda_read(struct wm_drive *d, struct wm_cdda_block *block)
0713 {
0714     struct cdrom_cdda cdda;
0715     int blk;
0716     unsigned char *q;
0717     unsigned char* rawbuf = block->buf;
0718 
0719     if (d->fd < 0)
0720         return -1;
0721 
0722     /* Hit the end of the CD, probably. */
0723     if ((direction > 0 && d->current_position >= d->ending_position) ||
0724         (direction < 0 && d->current_position < d->starting_position)) {
0725         block->status = WM_CDM_TRACK_DONE;
0726         return 0;
0727     }
0728 
0729     cdda.cdda_addr = d->current_position - 150;
0730     if (d->ending_position && d->current_position + d->frames_at_once > d->ending_position)
0731         cdda.cdda_length = d->ending_position - d->current_position;
0732     else
0733         cdda.cdda_length = d->frames_at_once;
0734     cdda.cdda_data = (unsigned char*)block->buf;
0735     cdda.cdda_subcode = CDROM_DA_SUBQ;
0736 
0737     if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) {
0738         if (errno == ENXIO) { /* CD ejected! */
0739             block->status = WM_CDM_EJECTED;
0740             return -1;
0741         }
0742 
0743         /* Sometimes it fails once, dunno why */
0744         if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) {
0745             if (ioctl(d->fd, CDROMCDDA, &cdda) < 0)  {
0746                 if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) {
0747                     perror("CDROMCDDA");
0748                     block->status = WM_CDM_CDDAERROR;
0749                     return -1;
0750                 }
0751             }
0752         }
0753     }
0754 
0755     d->current_position = d->current_position + cdda.cdda_length * direction;
0756 
0757 #if 0
0758     /*
0759      * New valid Q-subchannel information?  Update the block
0760      * status.
0761      */
0762     for (blk = 0; blk < d->numblocks; ++blk) {
0763         q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
0764         if (*q == 1) {
0765             block->track =  unbcd[q[1]];
0766             block->index =  unbcd[q[2]];
0767             /*block->minute = unbcd[q[7]];
0768             block->second = unbcd[q[8]];*/
0769             block->frame =  unbcd[q[9]];
0770             block->status = WM_CDM_PLAYING;
0771             block->buflen = cdda.cdda_length;
0772         }
0773     }
0774 #endif
0775     return sun_normalize(block);
0776 }
0777 
0778 /*
0779  * Close the CD-ROM device in preparation for exiting.
0780  */
0781 int gen_cdda_close(struct wm_drive *d)
0782 {
0783     int i;
0784 
0785     if (d->fd < 0)
0786         return -1;
0787 
0788     for (i = 0; i < d->numblocks; i++) {
0789         free(d->blocks[i].buf);
0790         d->blocks[i].buf = 0;
0791         d->blocks[i].buflen = 0;
0792     }
0793 
0794     return 0;
0795 }
0796 
0797 /*
0798  * The following code activates the internal CD audio passthrough on
0799  * SPARCstation 5 systems (and possibly others.)
0800  *
0801  * Thanks to <stevep@ctc.ih.att.com>, Roger Oscarsson <roger@cs.umu.se>
0802  * and Steve McKinty <>
0803  *
0804  * Most CD drives have a headphone socket on the front, but it
0805  * is often more convenient to route the audio though the
0806  * built-in audio device. That way the user can leave their
0807  * headphones plugged-in to the base system, for use with
0808  * other audio stuff like ShowMeTV
0809  */
0810 
0811 #ifdef CODEC /* { */
0812 #ifdef SYSV /* { */
0813 
0814 # include <sys/ioctl.h>
0815 # include <sys/audioio.h>
0816 # include <stdlib.h>
0817 
0818 #else /* } { */
0819 
0820 # include <sun/audioio.h>
0821 # define AUDIO_DEV_SS5STYLE 5
0822 typedef int audio_device_t;
0823 
0824 #endif /* } */
0825 #endif /* } */
0826 
0827 /*
0828  * Don't do anything with /dev/audio if we can't set it to high quality.
0829  * Also, don't do anything real if it's not Solaris.
0830  */
0831 #if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */
0832 codec_init() { return 0; }
0833 codec_start() { return 0; }
0834 codec_stop() { return 0; }
0835 #else
0836 
0837 #ifndef AUDIO_INTERNAL_CD_IN
0838 #define AUDIO_INTERNAL_CD_IN    0x4
0839 #endif
0840 
0841 static char* devname = 0;
0842 static char* ctlname = 0;
0843 static int ctl_fd = -1;
0844 static int port = AUDIO_LINE_IN;
0845 int internal_audio = 1;
0846 
0847 codec_init( void )
0848 {
0849   register int i;
0850   char* ctlname;
0851   audio_info_t foo;
0852   audio_device_t aud_dev;
0853 
0854   if (internal_audio == 0)
0855     {
0856       ctl_fd = -1;
0857       return 0;
0858     }
0859 
0860   if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio";
0861   ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl");
0862   if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0)
0863     {
0864       perror(ctlname);
0865       return -1;
0866     }
0867   if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0)
0868     {
0869       close(ctl_fd);
0870       ctl_fd = -1;
0871       return -1;
0872     }
0873   /*
0874    * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones.
0875    * Not sure if this is all correct.
0876    */
0877 #ifdef SYSV
0878   if (strcmp(aud_dev.name, "SUNW,CS4231") &&
0879       strcmp(aud_dev.name, "SUNW,sb16") &&
0880       strcmp(aud_dev.name, "SUNW,sbpro"))
0881 #else
0882     if (aud_dev != AUDIO_DEV_SS5STYLE)
0883 #endif
0884       {
0885     close(ctl_fd);
0886     ctl_fd = -1;
0887     return 0;                   /* but it's okay */
0888       }
0889 
0890   /*
0891    * Does the chosen device have an internal CD port?
0892    * If so, use it. If not then try and use the
0893    * Line In port.
0894    */
0895   if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
0896     {
0897       perror("AUDIO_GETINFO");
0898       close(ctl_fd);
0899       ctl_fd = -1;
0900       return -1;
0901     }
0902   if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN)
0903     port = AUDIO_INTERNAL_CD_IN;
0904   else
0905     port = AUDIO_LINE_IN;
0906 
0907   /*
0908    * now set it up to use it. See audio(7I)
0909    */
0910 
0911   AUDIO_INITINFO(&foo);
0912   foo.record.port = port;
0913   foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE;
0914 
0915   if (d->cdda_slave > -1)
0916     foo.monitor_gain = 0;
0917   else
0918     foo.monitor_gain = AUDIO_MAX_GAIN;
0919   /*
0920    * These next ones are tricky. The voulme will depend on the CD drive
0921    * volume (set by the knob on the drive and/or by workman's volume
0922    * control), the audio device record gain and the audio device
0923    * play gain.  For simplicity we set the latter two to something
0924    * reasonable, but we don't force them to be reset if the user
0925    * wants to change them.
0926    */
0927   foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100;
0928   foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100;
0929 
0930   ioctl(ctl_fd, AUDIO_SETINFO, &foo);
0931   return 0;
0932 }
0933 
0934 static int
0935 kick_codec( void )
0936 {
0937   audio_info_t foo;
0938   int dev_fd;
0939   int retval = 0;
0940 
0941   /*
0942    * Open the audio device, not the control device. This
0943    * will fail if someone else has taken it.
0944    */
0945 
0946   if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0)
0947     {
0948       perror(devname);
0949       return -1;
0950     }
0951 
0952   AUDIO_INITINFO(&foo);
0953   foo.record.port = port;
0954   foo.monitor_gain = AUDIO_MAX_GAIN;
0955 
0956   /* These can only be set on the real device */
0957   foo.play.sample_rate = 44100;
0958   foo.play.channels = 2;
0959   foo.play.precision = 16;
0960   foo.play.encoding = AUDIO_ENCODING_LINEAR;
0961 
0962   if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0)
0963     perror(devname);
0964 
0965   close(dev_fd);
0966   return retval;
0967 } /* kick_codec() */
0968 
0969 codec_start( void )
0970 {
0971   audio_info_t foo;
0972 
0973   if (ctl_fd < 0)
0974     return 0;
0975 
0976   if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
0977     return -1;
0978 
0979   if (foo.play.channels != 2) return kick_codec();
0980   if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
0981   if (foo.play.precision != 16) return kick_codec();
0982   if (foo.play.sample_rate != 44100) return kick_codec();
0983 
0984   if (foo.record.channels != 2) return kick_codec();
0985   if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
0986   if (foo.record.precision != 16) return kick_codec();
0987   if (foo.record.sample_rate != 44100) return kick_codec();
0988 
0989   if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec();
0990   if (foo.record.port != port) return kick_codec();
0991 
0992   return 0;
0993 } /* codec_start() */
0994 
0995 codec_stop( void ) { return 0; }
0996 
0997 #endif /* CODEC } */
0998 
0999 #endif /* sun */