File indexing completed on 2024-03-24 04:57:15
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__ */