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