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