File indexing completed on 2024-04-28 08:46:54

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  * BSD/386-specific drive control routines.
0022  */
0023 
0024 #if defined(__bsdi__) || defined(__bsdi)
0025 
0026 #include <errno.h>
0027 #include <stdio.h>
0028 #include <sys/types.h>
0029 #include <fcntl.h>
0030 #include <unistd.h>
0031 #include <sys/param.h>
0032 #include <sys/stat.h>
0033 
0034 #include "include/wm_config.h"
0035 
0036 /*
0037  * The following is included from the Linux module. However, I didn't
0038  * see a check here if the CD to be ejected is mounted...
0039  */
0040 #if defined(BSD_MOUNTTEST)
0041   #include <mntent.h>
0042 #endif
0043 
0044 
0045 #include <sys/time.h>
0046 #include <string.h>
0047 #include <cdrom.h>
0048 #ifdef SOUNDBLASTER
0049 # include <sys/soundcard.h>
0050 #endif
0051 
0052 #include "include/wm_struct.h"
0053 #include "include/wm_helpers.h"
0054 #include "include/wm_cdtext.h"
0055 
0056 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0057 
0058 
0059 /*
0060  * Since we can't sense the drive type with libcdrom anyway, and since the
0061  * library doesn't provide "pause" or "resume" functions, use the daux field
0062  * to point to the frame number at which we paused.
0063  */
0064 struct pause_info
0065 {
0066   int   frame;
0067   int   endframe;
0068 };
0069 
0070 #define PAUSE_FRAME (((struct pause_info *) d->daux)->frame)
0071 #define END_FRAME   (((struct pause_info *) d->daux)->endframe)
0072 #define CUR_CD      ((struct cdinfo *) d->aux)
0073 
0074 void *malloc();
0075 
0076 #ifdef SOUNDBLASTER
0077        int     min_volume = 0;
0078        int     max_volume = 100;
0079        int     min_volume_drive = 10;  /* Toshiba drive does low values. */
0080        int     max_volume_drive = 255;
0081 #else
0082        int     min_volume = 10;
0083        int     max_volume = 255;
0084 #endif
0085 
0086 /*--------------------------------------------------------*
0087  * Initialize the drive.  A no-op for the generic driver.
0088  *--------------------------------------------------------*/
0089 int
0090 gen_init(struct wm_drive *d)
0091 {
0092     return 0;
0093 } /* gen_init() */
0094 
0095 /*-----------------------------------------------------------------------*
0096  * Open the CD device.  We can't determine the drive type under BSD/386.
0097  *-----------------------------------------------------------------------*/
0098 int
0099 gen_open(struct wm_drvie *d)
0100 {
0101     void    *aux = NULL, *daux = NULL;
0102     int fd = -1;
0103 
0104     if (d->aux) {   /* Device already open? */
0105         wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (aux=%d)]\n", d->aux);
0106         return 0;
0107     }
0108 
0109     if ((aux = cdopen(d->cd_device)) == NULL) {
0110         fprintf(stderr, "No cdrom found by libcdrom\n");
0111         return -6;
0112     }
0113 
0114     if ((daux = malloc(sizeof(struct pause_info))) == NULL) {
0115         cdclose( aux );
0116         return -1;
0117     }
0118 
0119 #ifdef SOUNDBLASTER
0120     fd = open("/dev/mixer", O_RDWR, 0);
0121 #endif
0122 
0123     d->aux = aux;
0124     d->daux = daux;
0125     d->fd = fd;
0126     PAUSE_FRAME = 0;
0127     END_FRAME = 0;
0128 
0129     return 0;
0130 }
0131 
0132 /*---------------------------------------------*
0133  * Send an arbitrary SCSI command to a device.
0134  *---------------------------------------------*/
0135 int
0136 gen_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
0137         void *retbuf, int retbuflen, int getreply)
0138 {
0139     /* Don't know how to do SCSI passthrough... */
0140     return (-1);
0141 } /* gen_scsi() */
0142 
0143 int
0144 gen_close( struct wm_drive *d )
0145 {
0146   if(d->fd != -1) {
0147     wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
0148     close(d->fd); /* close mixer if open */
0149     d->fd = -1;
0150     cdclose( d->aux ); /* close the cdrom drive! */
0151     d->aux = NULL;
0152     free(d->daux);
0153     d->daux = NULL;
0154   }
0155   return 0;
0156 }
0157 
0158 /*--------------------------------------------------------------------------*
0159  * Get the current status of the drive: the current play mode, the absolute
0160  * position from start of disc (in frames), and the current track and index
0161  * numbers if the CD is playing or paused.
0162  *--------------------------------------------------------------------------*/
0163 #define DOPOS \
0164   *pos = status.abs_frame; \
0165 *track = status.track_num; \
0166 *index = status.index_num
0167 
0168 int
0169 gen_get_drive_status(struct wm_drive *d, int oldmode,
0170                      int *mode, int *pos, int *track, int *index)
0171 {
0172     struct cdstatus status;
0173     extern enum wm_cd_modes cur_cdmode;
0174 
0175     /* If we can't get status, the CD is ejected, so default to that. */
0176     *mode = WM_CDM_EJECTED;
0177 
0178     /* Is the device open? */
0179     if (d->aux == NULL) {
0180         switch (d->proto.open(d)) {
0181         case -1:    /* error */
0182             return -1;
0183 
0184         case 1:     /* retry */
0185             return 0;
0186         }
0187     }
0188 
0189     if (cdstatus (CUR_CD, &status) < 0) {
0190         *mode = WM_CDM_TRACK_DONE;  /* waiting for next track. */
0191         return 0;
0192     }
0193 
0194     switch (status.state) {
0195     case cdstate_playing:
0196         *mode = WM_CDM_PLAYING;
0197         break;
0198 
0199     case cdstate_stopped:
0200         /* the MITSUMI drive doesn't have a "paused" state,
0201         so it always comes here and not to the paused section.
0202         The PAUSE_FRAME stuff below (in gen_pause())
0203         fakes out the paused state. */
0204         if (oldmode == WM_CDM_PLAYING) {
0205             *mode = WM_CDM_TRACK_DONE;
0206             break;
0207         } else if (cur_cdmode != WM_CDM_PAUSED) {
0208             *mode = WM_CDM_STOPPED;
0209             break;
0210         }
0211         /* fall through if paused */
0212 
0213     case cdstate_paused:
0214       /* the SCSI2 code in the cdrom library only pauses with
0215      cdstop(); it never truly stops a disc (until an in-progress
0216      play reaches the end).  So it always comes here. */
0217         if (cur_cdmode == WM_CDM_STOPPED)
0218             *mode = WM_CDM_STOPPED;
0219         else if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
0220             *mode = WM_CDM_PAUSED;
0221         else
0222             *mode = WM_CDM_STOPPED;
0223         break;
0224 
0225     default:
0226         *mode = WM_CDM_STOPPED;
0227     }
0228 
0229     switch (*mode) {
0230     case WM_CDM_PLAYING:
0231     case WM_CDM_PAUSED:
0232     case WM_CDM_STOPPED:
0233         *pos = status.abs_frame;
0234         *track = status.track_num;
0235         *index = status.index_num;
0236         break;
0237     }
0238 
0239     return 0;
0240 } /* gen_get_drive_status() */
0241 
0242 
0243 /*-------------------------------------*
0244  * Get the number of tracks on the CD.
0245  *-------------------------------------*/
0246 int
0247 gen_get_trackcount(struct wm_drive *d, int *tracks)
0248 {
0249   *tracks = CUR_CD->ntracks;
0250 
0251   return 0;
0252 } /* gen_get_trackcount() */
0253 
0254 /*---------------------------------------------------------*
0255  * Get the start time and mode (data or audio) of a track.
0256  *---------------------------------------------------------*/
0257 int
0258 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
0259 {
0260   *data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0;
0261   *startframe = CUR_CD->tracks[track - 1].start_frame;
0262 
0263   return 0;
0264 } /* gen_get_trackinfo() */
0265 
0266 /*-------------------------------------*
0267  * Get the number of frames on the CD.
0268  *-------------------------------------*/
0269 int
0270 gen_get_cdlen(struct wm_drive *d, int *frames)
0271 {
0272   *frames = CUR_CD->total_frames;
0273 
0274   return 0;
0275 } /* gen_get_cdlen() */
0276 
0277 /*------------------------------------------------------------*
0278  * Play the CD from one position to another (both in frames.)
0279  *------------------------------------------------------------*/
0280 int
0281 gen_play(struct wm_drive *d, int start, int end)
0282 {
0283   END_FRAME = end;
0284   if (cdplay(d->aux, start, end) < 0)
0285     return -1;
0286   else
0287     return 0;
0288 } /* gen_play() */
0289 
0290 /*--------------------------------------------------------------------*
0291  * Pause the CD.  This is a bit of a trick since there's no cdpause()
0292  * function in the library.  We fake it by saving the frame number
0293  * and stopping.
0294  *--------------------------------------------------------------------*/
0295 int
0296 gen_pause(struct wm_drive *d)
0297 {
0298   struct cdstatus   status;
0299 
0300   if (cdstatus(d->aux, &status) < 0)
0301     return -1;
0302   if (status.state != cdstate_playing)
0303     PAUSE_FRAME = CUR_CD->tracks[0].start_frame;
0304   else
0305     PAUSE_FRAME = status.abs_frame;
0306   if (cdstop(d->aux) < 0)
0307     return -1;
0308 
0309   return 0;
0310 } /* gen_pause() */
0311 
0312 /*-------------------------------------------------*
0313  * Resume playing the CD (assuming it was paused.)
0314  *-------------------------------------------------*/
0315 int
0316 gen_resume(struct wm_drive *d)
0317 {
0318   int   status;
0319 
0320   status = (d->play)(d, PAUSE_FRAME, END_FRAME);
0321   PAUSE_FRAME = 0;
0322   return status;
0323 } /* gen_resume() */
0324 
0325 /*--------------*
0326  * Stop the CD.
0327  *--------------*/
0328 int
0329 gen_stop(struct wm_drive *d)
0330 {
0331   return cdstop(d->aux);
0332 } /* gen_stop() */
0333 
0334 /*----------------------------------------*
0335  * Eject the current CD, if there is one.
0336  *----------------------------------------*/
0337 int
0338 gen_eject(struct wm_drive *d)
0339 {
0340   cdeject(d->aux);
0341   cdclose(d->aux);
0342   d->aux = NULL;
0343   free(d->daux);
0344   d->daux = NULL;
0345 
0346   if (d->fd >= 0)
0347      close(d->fd);     /* close mixer */
0348   d->fd = -1;
0349   return 0;
0350 } /* gen_eject() */
0351 
0352 /*----------------------------------------*
0353  * Close the CD tray
0354  *----------------------------------------*/
0355 int
0356 gen_closetray(struct wm_drive *d)
0357 {
0358   if (!cdload(d->aux))
0359       return 0;
0360    return -1;
0361 } /* gen_closetray() */
0362 
0363 
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 static int
0369 scale_volume(int vol, int max)
0370 {
0371   /* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */
0372   return ((vol * (max_volume - min_volume)) / max + min_volume);
0373 } /* scale_volume() */
0374 
0375 /*---------------------------------------------------------------------------*
0376  * unscale_volume(cd_vol, max)
0377  *
0378  * Given a value between min_volume and max_volume, return the volume slider
0379  * value needed to achieve that value.
0380  *
0381  * Rather than perform floating-point calculations to reverse the above
0382  * formula, we simply do a binary search of scale_volume()'s return values.
0383  *--------------------------------------------------------------------------*/
0384 static int
0385 unscale_volume(int cd_vol, int max)
0386 {
0387     int vol = 0, top = max, bot = 0, scaled;
0388 
0389     while (bot <= top) {
0390         vol = (top + bot) / 2;
0391         scaled = scale_volume(vol, max);
0392         if (cd_vol == scaled)
0393             break;
0394         if (cd_vol < scaled)
0395             top = vol - 1;
0396         else
0397             bot = vol + 1;
0398     }
0399 
0400     if (vol < 0)
0401         vol = 0;
0402     else if (vol > max)
0403         vol = max;
0404 
0405   return vol;
0406 } /* unscale_volume() */
0407 
0408 /*---------------------------------------------------------------------*
0409  * Set the volume level for the left and right channels.  Their values
0410  * range from 0 to 100.
0411  *---------------------------------------------------------------------*/
0412 int
0413 gen_set_volume(struct wm_drive *d, int left, int right)
0414 {
0415   int level;
0416 
0417   left = scale_volume(left, 100);
0418   right = scale_volume(right, 100);
0419   level = right << 8 | left;
0420 
0421   /* Send a Mixer IOCTL */
0422   if (d->fd >= 0)
0423        (void) ioctl(d->fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &level);
0424 #ifdef notnow
0425   /* NOTE: the cdvolume2() call is an addition to the cdrom library.
0426      Pick it up from the archives on bsdi.com */
0427   cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left,
0428          right < 0 ? 0 : right > 255 ? 255 : right);
0429 
0430 #endif
0431   return 0;
0432 }
0433 
0434 /*---------------------------------------------------------------------*
0435  * Read the initial volume from the drive, if available.  Each channel
0436  * ranges from 0 to 100, with -1 indicating data not available.
0437  *---------------------------------------------------------------------*/
0438 int
0439 gen_get_volume(struct wm_drive *d, int *left, int *right)
0440 {
0441   int level;
0442 
0443   /* Most systems can't seem to do this... */
0444   *left = *right = -1;
0445 
0446   /* Send a Mixer IOCTL */
0447   if (d->fd >= 0) {
0448       if (ioctl(d->fd, MIXER_READ(SOUND_MIXER_VOLUME), &level) == 0) {
0449          *left = unscale_volume((level & 0xff) & 0xff, 100);
0450          *right = unscale_volume((level >> 8) & 0xff, 100);
0451        }
0452     }
0453   return 0;
0454 }
0455 
0456 #endif /* __bsdi__ */