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  * OSF  drive control routines.
0022  */
0023 
0024 
0025 #if defined(__osf__) || defined(__osf)
0026 
0027 
0028 #include <errno.h>
0029 #include <stdio.h>
0030 #include <sys/types.h>
0031 #include <fcntl.h>
0032 #include <sys/param.h>
0033 #include <sys/stat.h>
0034 #include <sys/time.h>
0035 #include <ustat.h>
0036 #include <string.h>
0037 /* #include <sys/rzdisk.h>
0038 #include <sys/cdrom.h>  */
0039 #include <io/cam/rzdisk.h>
0040 #include <io/cam/cdrom.h>
0041 
0042 #include "include/wm_config.h"
0043 #include "include/wm_struct.h"
0044 #include "include/wm_helpers.h"
0045 #include "include/wm_cdtext.h"
0046 
0047 
0048 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0049 
0050 /*
0051  *   This structure will be filled with the TOC header and all entries.
0052  * Ultrix doesn't seem to allow getting single TOC entries.
0053  *                              - Chris Ross (cross@eng.umd.edu)
0054  */
0055 struct cd_toc_header_and_entries
0056 {
0057   struct cd_toc_header  cdth;
0058   struct cd_toc_entry       cdte[CDROM_MAX_TRACK+1];
0059 };
0060 
0061 void *malloc();
0062 char *strchr();
0063 
0064 int min_volume = 128;
0065 int max_volume = 255;
0066 static char* osf_cd_device = NULL;
0067 
0068 /*
0069  * fgetline()
0070  *
0071  *   Simulate fgets, but joining continued lines in the output of uerf.
0072  *
0073  * platform specific internal function
0074  *
0075  */
0076 
0077 #define BUF_SIZE        85              /* Max length of a (real) line */
0078 
0079 char *
0080 fgetline( FILE *fp )
0081 {
0082   static char     *retval = NULL;
0083   static char     holdbuf[BUF_SIZE + 1];
0084   char            tmp[BUF_SIZE + 1];
0085   char            *stmp;
0086 
0087   if (!retval)
0088     {
0089       retval = malloc(BUF_SIZE * 3);  /* 3 lines can be joined */
0090       if (!retval)
0091     return(NULL);
0092       else
0093     *retval = '\0';
0094     }
0095 
0096   if (*holdbuf)
0097     {
0098       strcpy(retval, holdbuf);
0099       retval[strlen(retval)-1] = '\0';
0100       memset(holdbuf, 0, BUF_SIZE+1);
0101     }
0102 
0103   while (fgets(tmp, BUF_SIZE, fp))
0104     {
0105       stmp = tmp + strspn(tmp, " \t");
0106       if (*stmp == '_') {                     /* Continuation line */
0107     retval[strlen(retval)-1] = '\0';   /* Trim off C/R */
0108     strcat(retval, stmp+1);
0109       } else {
0110     if (*retval)
0111       {
0112         strcpy(holdbuf, tmp);
0113         holdbuf[strlen(holdbuf)-1] = -1;
0114         return retval;
0115       } else {             /* First line read, keep reading */
0116         strcat(retval, stmp);
0117         retval[strlen(retval)-1] = '\0';
0118       }
0119       }
0120     }
0121   return NULL;
0122 } /* fgetline() */
0123 
0124 
0125 /*
0126  * find_cdrom
0127  *
0128  * Determine the name of the CD-ROM device.
0129  *
0130  * Read through the boot records (via a call to uerf) and find the SCSI
0131  * address of the CD-ROM.
0132  */
0133 const char*
0134 find_cdrom()
0135 {
0136   char  *data;
0137   FILE  *uerf;
0138   int   fds[2];
0139   int   pid;
0140   extern char *getenv();
0141   const char *device = NULL;
0142 
0143   pipe(fds);
0144 
0145   device = getenv("CDROM");
0146   /*
0147   ** the path of the device has to start w/ /dev
0148   ** otherwise we are vulnerable to race conditions
0149   ** Thomas Biege <thomas@suse.de>
0150   */
0151   if ( device == NULL ||
0152        strncmp("/dev/", device, 5) ||
0153        strstr(device, "/../")
0154        )
0155     return NULL;
0156 
0157   if ((pid = fork()) == 0)
0158     {
0159       close(fds[0]);
0160       dup2(fds[1], 1);
0161       execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
0162       execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
0163       return NULL; /* _exit(1); */
0164     } else if (pid < 0) {
0165       perror("fork");
0166       return NULL;
0167     }
0168 
0169   close(fds[1]);
0170   uerf = fdopen(fds[0], "r");
0171 
0172   while (data = fgetline(uerf))
0173     if (strstr(data, "RRD42"))
0174       {
0175     char    *device_p;
0176 
0177     osf_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
0178     strcpy(osf_cd_device, "/dev/r");
0179     device_p = strstr(data, "rz");
0180     device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
0181     strcat(osf_cd_device, device_p);
0182     strcat(osf_cd_device, "c");
0183     device = osf_cd_device;
0184     break;
0185       }
0186 
0187   fclose(uerf);
0188 
0189   if (device == NULL)
0190     {
0191       fprintf(stderr,
0192           "No cdrom (RRD42) is installed on this system\n");
0193       return NULL;
0194     }
0195 
0196   kill(pid, 15);
0197   (void)wait((int *)NULL);
0198   return device;
0199 } /* find_cdrom() */
0200 
0201 /*
0202  * Initialize the drive.  A no-op for the generic driver.
0203  */
0204 int
0205 gen_init( struct wm_drive *d )
0206 {
0207   return 0;
0208 } /* gen_init() */
0209 
0210 /*
0211  * Open the CD device and figure out what kind of drive is attached.
0212  */
0213 int
0214 gen_open( struct wm_drive *d )
0215 {
0216     if (d->fd >= 0) {       /* Device already open? */
0217         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (fd=%d)]\n", d->fd);
0218         return 0;
0219     }
0220 
0221     d->fd = open(d->cd_device, O_RDWR);
0222     if (d->fd < 0) {
0223         if (errno == EACCES)
0224             return -EACCES;
0225         else if (errno != EINTR)
0226             return -6;
0227 
0228         /* No CD in drive. */
0229         return 1;
0230     }
0231 
0232     return 0;
0233 } /* gen_open() */
0234 
0235 /*
0236  * Send an arbitrary SCSI command to a device.
0237  */
0238 int
0239 gen_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
0240     void *retbuf, int retbuflen, int getreply)
0241 {
0242     /* OSF1 doesn't have a SCSI passthrough interface, does it? */
0243     return -1;
0244 } /* gen_scsi() */
0245 
0246 int
0247 gen_close( struct wm_drive *d )
0248 {
0249   if(d->fd > -1) {
0250     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
0251     close(d->fd);
0252     d->fd = -1;
0253   }
0254   return 0;
0255 }
0256 
0257 /*
0258  * Get the current status of the drive: the current play mode, the absolute
0259  * position from start of disc (in frames), and the current track and index
0260  * numbers if the CD is playing or paused.
0261  */
0262 int
0263 gen_get_drive_status(struct wm_drive *d, int oldmode,
0264              int *mode, int *pos, int *track, int *index)
0265 {
0266     struct cd_sub_channel       sc;
0267     struct cd_subc_channel_data scd;
0268 
0269     /* If we can't get status, the CD is ejected, so default to that. */
0270     *mode = WM_CDM_EJECTED;
0271 
0272     sc.sch_address_format   = CDROM_MSF_FORMAT;
0273     sc.sch_data_format  = CDROM_CURRENT_POSITION;
0274     sc.sch_track_number = 0;
0275     sc.sch_alloc_length = sizeof(scd);
0276     sc.sch_buffer       = (caddr_t)&scd;
0277 
0278     /* Is the device open? */
0279     if (d->fd < 0) {
0280         switch (d->proto.open(d)) {
0281         case -1:    /* error */
0282             return -1;
0283 
0284         case 1:     /* retry */
0285             return 0;
0286         }
0287     }
0288 
0289     if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
0290         return 0;   /* ejected */
0291 
0292     switch (scd.scd_header.sh_audio_status) {
0293     case AS_PLAY_IN_PROGRESS:
0294       *mode = WM_CDM_PLAYING;
0295     dopos:
0296       *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
0297     scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
0298     scd.scd_position_data.scp_absaddr.msf.f_units;
0299       *track = scd.scd_position_data.scp_track_number;
0300       *index = scd.scd_position_data.scp_index_number;
0301       break;
0302 
0303     case AS_PLAY_PAUSED:
0304       if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
0305     {
0306       *mode = WM_CDM_PAUSED;
0307       goto dopos;
0308     }
0309       else
0310     *mode = WM_CDM_STOPPED;
0311       break;
0312 
0313     case AS_PLAY_COMPLETED:
0314       *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
0315       break;
0316 
0317     case AS_NO_STATUS:
0318       *mode = WM_CDM_STOPPED;
0319       break;
0320     default:
0321       abort();
0322     }
0323 
0324   return (0);
0325 } /* gen_get_drive_status() */
0326 
0327 /*
0328  * Play the CD from one position to another (both in frames.)
0329  */
0330 int
0331 gen_play( struct wm_drive *d, int start, int end )
0332 {
0333   struct cd_play_audio_msf  msf;
0334 
0335   msf.msf_starting_M_unit   = start / (60*75);
0336   msf.msf_starting_S_unit   = (start % (60*75)) / 75;
0337   msf.msf_starting_F_unit   = start % 75;
0338   msf.msf_ending_M_unit = end / (60*75);
0339   msf.msf_ending_S_unit = (end % (60*75)) / 75;
0340   msf.msf_ending_F_unit = end % 75;
0341 
0342   if (ioctl(d->fd, SCSI_START_UNIT))
0343     return (-1);
0344   if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf))
0345     return (-2);
0346 
0347   return (0);
0348 } /* gen_play() */
0349 
0350 /*
0351  * Pause the CD.
0352  */
0353 int
0354 gen_pause( struct wm_drive *d )
0355 {
0356   return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0));
0357 } /* gen_pause() */
0358 
0359 /*
0360  * Resume playing the CD (assuming it was paused.)
0361  */
0362 int
0363 gen_resume( struct wm_drive *d )
0364 {
0365   return (ioctl(d->fd, CDROM_RESUME_PLAY, 0));
0366 } /* gen_resume() */
0367 
0368 /*
0369  * Stop the CD.
0370  */
0371 int
0372 gen_stop( struct wm_drive *d )
0373 {
0374   return (ioctl(d->fd, SCSI_STOP_UNIT, 0));
0375 } /* gen_stop() */
0376 
0377 /*
0378  * Eject the current CD, if there is one.
0379  */
0380 int
0381 gen_eject(struct wm_drive *d)
0382 {
0383   /* On some systems, we can check to see if the CD is mounted. */
0384   struct stat   stbuf;
0385   struct ustat  ust;
0386 
0387   if (fstat(d->fd, &stbuf) != 0)
0388     return (-2);
0389 
0390   /* Is this a mounted filesystem? */
0391   if (ustat(stbuf.st_rdev, &ust) == 0)
0392     return (-3);
0393 
0394   return (ioctl(d->fd, CDROM_EJECT_CADDY, 0));
0395 } /* gen_eject() */
0396 
0397 /*----------------------------------------*
0398  * Close the CD tray
0399  *
0400  * Please edit and send changes to
0401  * milliByte@DeathsDoor.com
0402  *----------------------------------------*/
0403 
0404 int
0405 gen_closetray(struct wm_drive *d)
0406 {
0407     return -1;
0408 } /* gen_closetray() */
0409 
0410 /*
0411  * Get the number of tracks on the CD.
0412  */
0413 int
0414 gen_get_trackcount(struct wm_drive *d, int *tracks)
0415 {
0416   struct cd_toc_header  hdr;
0417 
0418   if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
0419     return (-1);
0420 
0421   *tracks = hdr.th_ending_track;
0422 
0423   return (0);
0424 } /* gen_get_trackcount() */
0425 
0426 /*
0427  * Get the start time and mode (data or audio) of a track.
0428  *
0429  * XXX - this should get cached, but that means keeping track of ejects.
0430  */
0431 int
0432 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
0433 {
0434   struct cd_toc             toc;
0435   struct cd_toc_header          hdr;
0436   struct cd_toc_header_and_entries  toc_buffer;
0437 
0438   if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
0439     return (-1);
0440 
0441   bzero((char *)&toc_buffer, sizeof(toc_buffer));
0442   toc.toc_address_format = CDROM_MSF_FORMAT;
0443   toc.toc_starting_track = 0;
0444   toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
0445                     hdr.th_data_len0) & 0xfff) + 2;
0446   toc.toc_buffer = (caddr_t)&toc_buffer;
0447 
0448   if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
0449     return (-1);
0450 
0451   if (track == 0)
0452     track = hdr.th_ending_track + 1;
0453 
0454   *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
0455   *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
0456     toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
0457     toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
0458 
0459   return (0);
0460 } /* gen_get_trackinfo() */
0461 
0462 /*
0463  * Get the number of frames on the CD.
0464  */
0465 int
0466 gen_get_cdlen( struct wm_drive *d,  int *frames )
0467 {
0468   int       tmp;
0469 
0470   return (gen_get_trackinfo(d, 0, &tmp, frames));
0471 } /* gen_get_cdlen() */
0472 
0473 /*
0474  * scale_volume(vol, max)
0475  *
0476  * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
0477  * is a volume slider setting; "max" is the slider's maximum value.
0478  *
0479  * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
0480  * increases much faster toward the top end of the volume scale than it
0481  * does at the bottom.  To make up for this, we make the volume scale look
0482  * sort of logarithmic (actually an upside-down inverse square curve) so
0483  * that the volume value passed to the drive changes less and less as you
0484  * approach the maximum slider setting.  The actual formula looks like
0485  *
0486  *     (max^2 - (max - vol)^2) * (max_volume - min_volume)
0487  * v = --------------------------------------------------- + min_volume
0488  *                           max^2
0489  *
0490  * If your system's volume settings aren't broken in this way, something
0491  * like the following should work:
0492  *
0493  *  return ((vol * (max_volume - min_volume)) / max + min_volume);
0494  */
0495 scale_volume(int vol, int max)
0496 {
0497   return ((max * max - (max - vol) * (max - vol)) *
0498       (max_volume - min_volume) / (max * max) + min_volume);
0499 } /* scale_volume() */
0500 
0501 /*
0502  * unscale_volume(cd_vol, max)
0503  *
0504  * Given a value between min_volume and max_volume, return the volume slider
0505  * value needed to achieve that value.
0506  *
0507  * Rather than perform floating-point calculations to reverse the above
0508  * formula, we simply do a binary search of scale_volume()'s return values.
0509  */
0510 static int
0511 unscale_volume( int cd_vol, int max )
0512 {
0513   int   vol = 0, top = max, bot = 0, scaled;
0514 
0515   while (bot <= top)
0516     {
0517       vol = (top + bot) / 2;
0518       scaled = scale_volume(vol, max);
0519       if (cd_vol == scaled)
0520     break;
0521       if (cd_vol < scaled)
0522     top = vol - 1;
0523       else
0524     bot = vol + 1;
0525     }
0526 
0527   if (vol < 0)
0528     vol = 0;
0529   else if (vol > max)
0530     vol = max;
0531 
0532   return (vol);
0533 } /* unscale_volume() */
0534 
0535 /*
0536  * Set the volume level for the left and right channels.  Their values
0537  * range from 0 to 100.
0538  */
0539 int
0540 gen_set_volume(struct wm_drive *d, int left, int right)
0541 {
0542   struct cd_playback        pb;
0543   struct cd_playback_status ps;
0544   struct cd_playback_control    pc;
0545 
0546   left = scale_volume(left, 100);
0547   right = scale_volume(right, 100);
0548 
0549   bzero((char *)&pb, sizeof(pb));
0550   bzero((char *)&ps, sizeof(ps));
0551   bzero((char *)&pc, sizeof(pc));
0552 
0553   pb.pb_alloc_length = sizeof(ps);
0554   pb.pb_buffer = (caddr_t)&ps;
0555 
0556   if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
0557     return (-1);
0558 
0559   pc.pc_chan0_select = ps.ps_chan0_select;
0560   pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
0561     CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
0562     CDROM_MAX_VOLUME : left;
0563   pc.pc_chan1_select = ps.ps_chan1_select;
0564   pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
0565     CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
0566     CDROM_MAX_VOLUME : right;
0567 
0568   pb.pb_alloc_length = sizeof(pc);
0569   pb.pb_buffer = (caddr_t)&pc;
0570 
0571   if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
0572     return (-1);
0573 
0574   return (0);
0575 } /* gen_set_volume() */
0576 
0577 
0578 /*
0579  * Read the initial volume from the drive, if available.  Each channel
0580  * ranges from 0 to 100, with -1 indicating data not available.
0581  */
0582 int
0583 gen_get_volume( struct wm_drive *d, int *left, int *right )
0584 {
0585   struct cd_playback        pb;
0586   struct cd_playback_status ps;
0587 
0588   bzero((char *)&pb, sizeof(pb));
0589   bzero((char *)&ps, sizeof(ps));
0590 
0591   pb.pb_alloc_length = sizeof(ps);
0592   pb.pb_buffer = (caddr_t)&ps;
0593 
0594   if (d->fd >= 0)
0595     {
0596       if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
0597     *left = *right = -1;
0598       else
0599     {
0600       *left = unscale_volume(ps.ps_chan0_volume, 100);
0601       *right = unscale_volume(ps.ps_chan1_volume, 100);
0602     }
0603     }
0604   else
0605     *left = *right = -1;
0606 
0607   return (0);
0608 } /* gen_get_volume() */
0609 
0610 #endif