File indexing completed on 2024-04-21 04:54:23

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  * OpenBSD-specific drive control routines. (Based on plat_freebsd.c)
0022  *
0023  * Michael Shalayeff, 7/24/96
0024  * Todd Pfaff, 3/20/94
0025  *
0026  */
0027 
0028 #if defined(__OpenBSD__) || defined(__OpenBSD)
0029 
0030 
0031 #include <errno.h>
0032 #include <stdio.h>
0033 #include <sys/types.h>
0034 #include <sys/param.h>
0035 #include <sys/mount.h>
0036 #include <fcntl.h>
0037 #include <sys/stat.h>
0038 
0039 #include "include/wm_config.h"
0040 #include "include/wm_cdrom.h"
0041 #include "include/wm_helpers.h"
0042 
0043 /* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */
0044 #ifdef __GLIBC__
0045 #include <sys/ustat.h>
0046 #endif
0047 
0048 #include <sys/time.h>
0049 #include <string.h>
0050 #include <sys/ioctl.h>
0051 #include <sys/cdio.h>
0052 #include <sys/scsiio.h>
0053 #include <scsi/scsi_all.h>
0054 #include <scsi/cd.h>
0055 #include <scsi/scsi_cd.h>
0056 
0057 #include "include/wm_struct.h"
0058 #include "include/wm_cdtext.h"
0059 
0060 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0061 
0062 void *malloc();
0063 
0064 int min_volume = 10;
0065 int max_volume = 255;
0066 
0067 const char*
0068 find_cdrom()
0069 {
0070     if (access("/dev/rcd0c", F_OK) == 0) {
0071         return "/dev/rcd0c";
0072     } else if (access("/dev/rcd1c", F_OK) == 0) {
0073         return "/dev/rcd1c";
0074     } else if (access("/dev/acd0c", F_OK) == 0) {
0075         return "/dev/acd0c";
0076     } else {
0077         fprintf(stderr, "Could not find a CD device!\n");
0078         return NULL;
0079     }
0080 } /* find_cdrom() */
0081 
0082 /*
0083  * Initialize the drive.  A no-op for the generic driver.
0084  */
0085 int
0086 gen_init(struct wm_drive *d)
0087 {
0088   return (0);
0089 } /* gen_init() */
0090 
0091 
0092 /*
0093  * Open the CD device and figure out what kind of drive is attached.
0094  */
0095 int
0096 gen_open(struct wm_drive *d)
0097 {
0098     if (d->fd >= 0) {       /* Device already open? */
0099        wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (fd=%d)]\n", d->fd);
0100        return 0;
0101     }
0102 
0103     d->fd = open(d->cd_device, O_RDONLY);
0104     if (d->fd < 0) {
0105         if (errno == EIO) /* No CD in drive. */
0106             return 1;
0107         else
0108             return -errno;
0109     }
0110 
0111     return 0;
0112 } /* gen_open() */
0113 
0114 /*
0115  * Send an arbitrary SCSI command to a device.
0116  *
0117  */
0118 int
0119 gen_scsi(struct wm_drive *d, unsigned char *cdb,
0120     int cdblen, void *retbuf, int retbuflen, int getreply)
0121 {
0122   return -1;
0123 } /* gen_scsi() */
0124 
0125 int
0126 gen_close( struct wm_drive *d )
0127 {
0128   if(d->fd > -1) {
0129     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
0130     close(d->fd);
0131   }
0132   d->fd = -1;
0133   return 0;
0134 }
0135 
0136 /*
0137  * Get the current status of the drive: the current play mode, the absolute
0138  * position from start of disc (in frames), and the current track and index
0139  * numbers if the CD is playing or paused.
0140  */
0141 int
0142 gen_get_drive_status(struct wm_drive *d, int oldmode,
0143              int *mode, int *pos, int *track, int *index)
0144 {
0145   struct ioc_read_subchannel    sc;
0146   struct cd_sub_channel_info    scd;
0147 
0148   /* If we can't get status, the CD is ejected, so default to that. */
0149   *mode = WM_CDM_EJECTED;
0150 
0151   sc.address_format = CD_MSF_FORMAT;
0152   sc.data_format    = CD_CURRENT_POSITION;
0153   sc.track      = 0;
0154   sc.data_len       = sizeof(scd);
0155   sc.data       = (struct cd_sub_channel_info *)&scd;
0156 
0157   /* Is the device open? */
0158   if (d->fd < 0) {
0159     switch (d->proto.open(d)) {
0160     case -1:    /* error */
0161       return -1;
0162     case 1:     /* retry */
0163       return 0;
0164     }
0165   }
0166 
0167   if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc)) {
0168     /* we need to release the device so the kernel will notice
0169     reloaded media */
0170     d->proto.close(d);
0171     return 0;   /* ejected */
0172   }
0173 
0174   switch (scd.header.audio_status) {
0175     case CD_AS_PLAY_IN_PROGRESS:
0176       *mode = WM_CDM_PLAYING;
0177       break;
0178 
0179     case CD_AS_PLAY_PAUSED:
0180       if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
0181         *mode = WM_CDM_PAUSED;
0182       else
0183         *mode = WM_CDM_STOPPED;
0184       break;
0185 
0186     case CD_AS_PLAY_COMPLETED:
0187       *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
0188       break;
0189 
0190     case CD_AS_NO_STATUS:
0191     case 0:
0192       *mode = WM_CDM_STOPPED;
0193       break;
0194   }
0195 
0196   switch(*mode) {
0197   case WM_CDM_PLAYING:
0198   case WM_CDM_PAUSED:
0199     *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
0200       scd.what.position.absaddr.msf.second * 75 +
0201       scd.what.position.absaddr.msf.frame;
0202     *track = scd.what.position.track_number;
0203     *index = scd.what.position.index_number;
0204     break;
0205   }
0206 
0207   return 0;
0208 } /* gen_get_drive_status() */
0209 
0210 /*
0211  * Get the number of tracks on the CD.
0212  */
0213 int
0214 gen_get_trackcount(struct wm_drive *d, int *tracks)
0215 {
0216   struct ioc_toc_header hdr;
0217 
0218   if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
0219     return (-1);
0220 
0221   *tracks = hdr.ending_track - hdr.starting_track + 1;
0222   return (0);
0223 } /* gen_get_trackcount() */
0224 
0225 /*
0226  * Get the start time and mode (data or audio) of a track.
0227  *
0228  * XXX - this should get cached, but that means keeping track of ejects.
0229  */
0230 int
0231 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
0232 {
0233   struct ioc_read_toc_entry toc;
0234   struct cd_toc_entry       toc_buffer;
0235 
0236   bzero((char *)&toc_buffer, sizeof(toc_buffer));
0237   toc.address_format = CD_MSF_FORMAT;
0238   toc.starting_track = track;
0239   toc.data_len = sizeof(toc_buffer);
0240   toc.data = &toc_buffer;
0241 
0242   if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
0243     return (-1);
0244 
0245   *data = ((toc_buffer.control & 0x4) != 0);
0246 
0247   *startframe = toc_buffer.addr.msf.minute*60*75 +
0248     toc_buffer.addr.msf.second * 75 +
0249     toc_buffer.addr.msf.frame;
0250 
0251   return (0);
0252 } /* gen_get_trackinfo() */
0253 
0254 /*
0255  * Get the number of frames on the CD.
0256  */
0257 int
0258 gen_get_cdlen(struct wm_drive *d, int *frames)
0259 {
0260   int       tmp;
0261   struct ioc_toc_header     hdr;
0262   int status;
0263 
0264 #define LEADOUT 0xaa            /* see scsi.c.  what a hack! */
0265   return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
0266 } /* gen_get_cdlen() */
0267 
0268 /*
0269  * Play the CD from one position to another (both in frames.)
0270  */
0271 int
0272 gen_play(struct wm_drive *d, int start, int end)
0273 {
0274   struct ioc_play_msf   msf;
0275 
0276   msf.start_m   = start / (60*75);
0277   msf.start_s   = (start % (60*75)) / 75;
0278   msf.start_f   = start % 75;
0279   msf.end_m = end / (60*75);
0280   msf.end_s = (end % (60*75)) / 75;
0281   msf.end_f = end % 75;
0282 
0283   if (ioctl(d->fd, CDIOCSTART))
0284     return (-1);
0285 
0286   if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
0287     return (-2);
0288 
0289   return (0);
0290 } /* gen_play() */
0291 
0292 /*
0293  * Pause the CD.
0294  */
0295 int
0296 gen_pause(struct wm_drive *d)
0297 {
0298   return (ioctl(d->fd, CDIOCPAUSE));
0299 } /* gen_pause() */
0300 
0301 /*
0302  * Resume playing the CD (assuming it was paused.)
0303  */
0304 int
0305 gen_resume(struct wm_drive *d)
0306 {
0307   return (ioctl(d->fd, CDIOCRESUME));
0308 } /* gen_resume() */
0309 
0310 /*
0311  * Stop the CD.
0312  */
0313 int
0314 gen_stop(struct wm_drive *d)
0315 {
0316   return (ioctl(d->fd, CDIOCSTOP));
0317 } /* gen_stop() */
0318 
0319 /*
0320  * Eject the current CD, if there is one.
0321  */
0322 int
0323 gen_eject(struct wm_drive *d)
0324 {
0325   /* On some systems, we can check to see if the CD is mounted. */
0326   struct stat   stbuf;
0327   struct statfs buf;
0328   int rval;
0329 
0330   if (fstat(d->fd, &stbuf) != 0)
0331     return (-2);
0332 
0333   /* Is this a mounted filesystem? */
0334   if (fstatfs(stbuf.st_rdev, &buf) == 0)
0335     return (-3);
0336 
0337   rval = ioctl(d->fd, CDIOCALLOW);
0338   if (rval == 0)
0339     rval = ioctl(d->fd, CDIOCEJECT);
0340   if (rval == 0)
0341     rval = ioctl(d->fd, CDIOCPREVENT);
0342   if (rval == 0)
0343     rval = close(d->fd);
0344   if (rval == 0)
0345     d->fd = -1;
0346   return rval;
0347 } /* gen_eject() */
0348 
0349 /*----------------------------------------*
0350  * Close the CD tray
0351  *
0352  * Please edit and send changes to
0353  * milliByte@DeathsDoor.com
0354  *----------------------------------------*/
0355 
0356 int
0357 gen_closetray(struct wm_drive *d)
0358 {
0359     return -1;
0360 } /* gen_closetray() */
0361 
0362 /*
0363  * scale_volume(vol, max)
0364  *
0365  * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
0366  * is a volume slider setting; "max" is the slider's maximum value.
0367  *
0368  * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
0369  * increases much faster toward the top end of the volume scale than it
0370  * does at the bottom.  To make up for this, we make the volume scale look
0371  * sort of logarithmic (actually an upside-down inverse square curve) so
0372  * that the volume value passed to the drive changes less and less as you
0373  * approach the maximum slider setting.  The actual formula looks like
0374  *
0375  *     (max^2 - (max - vol)^2) * (max_volume - min_volume)
0376  * v = --------------------------------------------------- + min_volume
0377  *                           max^2
0378  *
0379  * If your system's volume settings aren't broken in this way, something
0380  * like the following should work:
0381  *
0382  *  return ((vol * (max_volume - min_volume)) / max + min_volume);
0383  */
0384 static int
0385 scale_volume(int vol, int max)
0386 {
0387   return ((vol * (max_volume - min_volume)) / max + min_volume);
0388 } /* scale_volume() */
0389 
0390 /*
0391  * unscale_volume(cd_vol, max)
0392  *
0393  * Given a value between min_volume and max_volume, return the volume slider
0394  * value needed to achieve that value.
0395  *
0396  * Rather than perform floating-point calculations to reverse the above
0397  * formula, we simply do a binary search of scale_volume()'s return values.
0398  */
0399 static int
0400 unscale_volume(int cd_vol, int max)
0401 {
0402     int vol = 0, top = max, bot = 0, scaled;
0403 
0404     while (bot <= top)
0405     {
0406         vol = (top + bot) / 2;
0407         scaled = scale_volume(vol, max);
0408         if (cd_vol == scaled)
0409             break;
0410         if (cd_vol < scaled)
0411             top = vol - 1;
0412         else
0413             bot = vol + 1;
0414     }
0415 
0416     if (vol < 0)
0417         vol = 0;
0418     else if (vol > max)
0419         vol = max;
0420 
0421     return (vol);
0422 } /* unscale_volume() */
0423 
0424 /*
0425  * Set the volume level for the left and right channels.  Their values
0426  * range from 0 to 100.
0427  */
0428 int
0429 gen_set_volume(struct wm_drive *d, int left, int right)
0430 {
0431   struct ioc_vol vol;
0432 
0433   if (left < 0) /* don't laugh, I saw this happen once! */
0434     left = 0;
0435   if (right < 0)
0436     right = 0;
0437   left = scale_volume(left, 100);
0438   right = scale_volume(right, 100);
0439 
0440   bzero((char *)&vol, sizeof(vol));
0441 
0442   vol.vol[LEFT_PORT] = left;
0443   vol.vol[RIGHT_PORT] = right;
0444 
0445   if (ioctl(d->fd, CDIOCSETVOL, &vol))
0446     return (-1);
0447 
0448   return (0);
0449 } /* gen_set_volume() */
0450 
0451 
0452 /*
0453  * Read the initial volume from the drive, if available.  Each channel
0454  * ranges from 0 to 100, with -1 indicating data not available.
0455  */
0456 int
0457 gen_get_volume(struct wm_drive *d, int *left, int *right)
0458 {
0459   struct ioc_vol vol;
0460 
0461   if (d->fd >= 0)
0462     {
0463       bzero((char *)&vol, sizeof(vol));
0464 
0465       if (ioctl(d->fd, CDIOCGETVOL, &vol))
0466     *left = *right = -1;
0467       else
0468     {
0469       *left = unscale_volume(vol.vol[LEFT_PORT], 100);
0470       *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
0471     }
0472     }
0473   else
0474     *left = *right = -1;
0475 
0476   return (0);
0477 } /* gen_get_volume() */
0478 
0479 #endif