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

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