File indexing completed on 2024-04-14 04:49:19
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 * OSF drive control routines. 0022 */ 0023 0024 0025 #if defined(__osf__) || defined(__osf) 0026 0027 0028 #include <errno.h> 0029 #include <stdio.h> 0030 #include <sys/types.h> 0031 #include <fcntl.h> 0032 #include <sys/param.h> 0033 #include <sys/stat.h> 0034 #include <sys/time.h> 0035 #include <ustat.h> 0036 #include <string.h> 0037 /* #include <sys/rzdisk.h> 0038 #include <sys/cdrom.h> */ 0039 #include <io/cam/rzdisk.h> 0040 #include <io/cam/cdrom.h> 0041 0042 #include "include/wm_config.h" 0043 #include "include/wm_struct.h" 0044 #include "include/wm_helpers.h" 0045 #include "include/wm_cdtext.h" 0046 0047 0048 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM 0049 0050 /* 0051 * This structure will be filled with the TOC header and all entries. 0052 * Ultrix doesn't seem to allow getting single TOC entries. 0053 * - Chris Ross (cross@eng.umd.edu) 0054 */ 0055 struct cd_toc_header_and_entries 0056 { 0057 struct cd_toc_header cdth; 0058 struct cd_toc_entry cdte[CDROM_MAX_TRACK+1]; 0059 }; 0060 0061 void *malloc(); 0062 char *strchr(); 0063 0064 int min_volume = 128; 0065 int max_volume = 255; 0066 static char* osf_cd_device = NULL; 0067 0068 /* 0069 * fgetline() 0070 * 0071 * Simulate fgets, but joining continued lines in the output of uerf. 0072 * 0073 * platform specific internal function 0074 * 0075 */ 0076 0077 #define BUF_SIZE 85 /* Max length of a (real) line */ 0078 0079 char * 0080 fgetline( FILE *fp ) 0081 { 0082 static char *retval = NULL; 0083 static char holdbuf[BUF_SIZE + 1]; 0084 char tmp[BUF_SIZE + 1]; 0085 char *stmp; 0086 0087 if (!retval) 0088 { 0089 retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */ 0090 if (!retval) 0091 return(NULL); 0092 else 0093 *retval = '\0'; 0094 } 0095 0096 if (*holdbuf) 0097 { 0098 strcpy(retval, holdbuf); 0099 retval[strlen(retval)-1] = '\0'; 0100 memset(holdbuf, 0, BUF_SIZE+1); 0101 } 0102 0103 while (fgets(tmp, BUF_SIZE, fp)) 0104 { 0105 stmp = tmp + strspn(tmp, " \t"); 0106 if (*stmp == '_') { /* Continuation line */ 0107 retval[strlen(retval)-1] = '\0'; /* Trim off C/R */ 0108 strcat(retval, stmp+1); 0109 } else { 0110 if (*retval) 0111 { 0112 strcpy(holdbuf, tmp); 0113 holdbuf[strlen(holdbuf)-1] = -1; 0114 return retval; 0115 } else { /* First line read, keep reading */ 0116 strcat(retval, stmp); 0117 retval[strlen(retval)-1] = '\0'; 0118 } 0119 } 0120 } 0121 return NULL; 0122 } /* fgetline() */ 0123 0124 0125 /* 0126 * find_cdrom 0127 * 0128 * Determine the name of the CD-ROM device. 0129 * 0130 * Read through the boot records (via a call to uerf) and find the SCSI 0131 * address of the CD-ROM. 0132 */ 0133 const char* 0134 find_cdrom() 0135 { 0136 char *data; 0137 FILE *uerf; 0138 int fds[2]; 0139 int pid; 0140 extern char *getenv(); 0141 const char *device = NULL; 0142 0143 pipe(fds); 0144 0145 device = getenv("CDROM"); 0146 /* 0147 ** the path of the device has to start w/ /dev 0148 ** otherwise we are vulnerable to race conditions 0149 ** Thomas Biege <thomas@suse.de> 0150 */ 0151 if ( device == NULL || 0152 strncmp("/dev/", device, 5) || 0153 strstr(device, "/../") 0154 ) 0155 return NULL; 0156 0157 if ((pid = fork()) == 0) 0158 { 0159 close(fds[0]); 0160 dup2(fds[1], 1); 0161 execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0); 0162 execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0); 0163 return NULL; /* _exit(1); */ 0164 } else if (pid < 0) { 0165 perror("fork"); 0166 return NULL; 0167 } 0168 0169 close(fds[1]); 0170 uerf = fdopen(fds[0], "r"); 0171 0172 while (data = fgetline(uerf)) 0173 if (strstr(data, "RRD42")) 0174 { 0175 char *device_p; 0176 0177 osf_cd_device = (char *)malloc(sizeof("/dev/rrz##c")); 0178 strcpy(osf_cd_device, "/dev/r"); 0179 device_p = strstr(data, "rz"); 0180 device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0'; 0181 strcat(osf_cd_device, device_p); 0182 strcat(osf_cd_device, "c"); 0183 device = osf_cd_device; 0184 break; 0185 } 0186 0187 fclose(uerf); 0188 0189 if (device == NULL) 0190 { 0191 fprintf(stderr, 0192 "No cdrom (RRD42) is installed on this system\n"); 0193 return NULL; 0194 } 0195 0196 kill(pid, 15); 0197 (void)wait((int *)NULL); 0198 return device; 0199 } /* find_cdrom() */ 0200 0201 /* 0202 * Initialize the drive. A no-op for the generic driver. 0203 */ 0204 int 0205 gen_init( struct wm_drive *d ) 0206 { 0207 return 0; 0208 } /* gen_init() */ 0209 0210 /* 0211 * Open the CD device and figure out what kind of drive is attached. 0212 */ 0213 int 0214 gen_open( struct wm_drive *d ) 0215 { 0216 if (d->fd >= 0) { /* Device already open? */ 0217 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (fd=%d)]\n", d->fd); 0218 return 0; 0219 } 0220 0221 d->fd = open(d->cd_device, O_RDWR); 0222 if (d->fd < 0) { 0223 if (errno == EACCES) 0224 return -EACCES; 0225 else if (errno != EINTR) 0226 return -6; 0227 0228 /* No CD in drive. */ 0229 return 1; 0230 } 0231 0232 return 0; 0233 } /* gen_open() */ 0234 0235 /* 0236 * Send an arbitrary SCSI command to a device. 0237 */ 0238 int 0239 gen_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen, 0240 void *retbuf, int retbuflen, int getreply) 0241 { 0242 /* OSF1 doesn't have a SCSI passthrough interface, does it? */ 0243 return -1; 0244 } /* gen_scsi() */ 0245 0246 int 0247 gen_close( struct wm_drive *d ) 0248 { 0249 if(d->fd > -1) { 0250 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); 0251 close(d->fd); 0252 d->fd = -1; 0253 } 0254 return 0; 0255 } 0256 0257 /* 0258 * Get the current status of the drive: the current play mode, the absolute 0259 * position from start of disc (in frames), and the current track and index 0260 * numbers if the CD is playing or paused. 0261 */ 0262 int 0263 gen_get_drive_status(struct wm_drive *d, int oldmode, 0264 int *mode, int *pos, int *track, int *index) 0265 { 0266 struct cd_sub_channel sc; 0267 struct cd_subc_channel_data scd; 0268 0269 /* If we can't get status, the CD is ejected, so default to that. */ 0270 *mode = WM_CDM_EJECTED; 0271 0272 sc.sch_address_format = CDROM_MSF_FORMAT; 0273 sc.sch_data_format = CDROM_CURRENT_POSITION; 0274 sc.sch_track_number = 0; 0275 sc.sch_alloc_length = sizeof(scd); 0276 sc.sch_buffer = (caddr_t)&scd; 0277 0278 /* Is the device open? */ 0279 if (d->fd < 0) { 0280 switch (d->proto.open(d)) { 0281 case -1: /* error */ 0282 return -1; 0283 0284 case 1: /* retry */ 0285 return 0; 0286 } 0287 } 0288 0289 if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc)) 0290 return 0; /* ejected */ 0291 0292 switch (scd.scd_header.sh_audio_status) { 0293 case AS_PLAY_IN_PROGRESS: 0294 *mode = WM_CDM_PLAYING; 0295 dopos: 0296 *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 + 0297 scd.scd_position_data.scp_absaddr.msf.s_units * 75 + 0298 scd.scd_position_data.scp_absaddr.msf.f_units; 0299 *track = scd.scd_position_data.scp_track_number; 0300 *index = scd.scd_position_data.scp_index_number; 0301 break; 0302 0303 case AS_PLAY_PAUSED: 0304 if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) 0305 { 0306 *mode = WM_CDM_PAUSED; 0307 goto dopos; 0308 } 0309 else 0310 *mode = WM_CDM_STOPPED; 0311 break; 0312 0313 case AS_PLAY_COMPLETED: 0314 *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ 0315 break; 0316 0317 case AS_NO_STATUS: 0318 *mode = WM_CDM_STOPPED; 0319 break; 0320 default: 0321 abort(); 0322 } 0323 0324 return (0); 0325 } /* gen_get_drive_status() */ 0326 0327 /* 0328 * Play the CD from one position to another (both in frames.) 0329 */ 0330 int 0331 gen_play( struct wm_drive *d, int start, int end ) 0332 { 0333 struct cd_play_audio_msf msf; 0334 0335 msf.msf_starting_M_unit = start / (60*75); 0336 msf.msf_starting_S_unit = (start % (60*75)) / 75; 0337 msf.msf_starting_F_unit = start % 75; 0338 msf.msf_ending_M_unit = end / (60*75); 0339 msf.msf_ending_S_unit = (end % (60*75)) / 75; 0340 msf.msf_ending_F_unit = end % 75; 0341 0342 if (ioctl(d->fd, SCSI_START_UNIT)) 0343 return (-1); 0344 if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf)) 0345 return (-2); 0346 0347 return (0); 0348 } /* gen_play() */ 0349 0350 /* 0351 * Pause the CD. 0352 */ 0353 int 0354 gen_pause( struct wm_drive *d ) 0355 { 0356 return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0)); 0357 } /* gen_pause() */ 0358 0359 /* 0360 * Resume playing the CD (assuming it was paused.) 0361 */ 0362 int 0363 gen_resume( struct wm_drive *d ) 0364 { 0365 return (ioctl(d->fd, CDROM_RESUME_PLAY, 0)); 0366 } /* gen_resume() */ 0367 0368 /* 0369 * Stop the CD. 0370 */ 0371 int 0372 gen_stop( struct wm_drive *d ) 0373 { 0374 return (ioctl(d->fd, SCSI_STOP_UNIT, 0)); 0375 } /* gen_stop() */ 0376 0377 /* 0378 * Eject the current CD, if there is one. 0379 */ 0380 int 0381 gen_eject(struct wm_drive *d) 0382 { 0383 /* On some systems, we can check to see if the CD is mounted. */ 0384 struct stat stbuf; 0385 struct ustat ust; 0386 0387 if (fstat(d->fd, &stbuf) != 0) 0388 return (-2); 0389 0390 /* Is this a mounted filesystem? */ 0391 if (ustat(stbuf.st_rdev, &ust) == 0) 0392 return (-3); 0393 0394 return (ioctl(d->fd, CDROM_EJECT_CADDY, 0)); 0395 } /* gen_eject() */ 0396 0397 /*----------------------------------------* 0398 * Close the CD tray 0399 * 0400 * Please edit and send changes to 0401 * milliByte@DeathsDoor.com 0402 *----------------------------------------*/ 0403 0404 int 0405 gen_closetray(struct wm_drive *d) 0406 { 0407 return -1; 0408 } /* gen_closetray() */ 0409 0410 /* 0411 * Get the number of tracks on the CD. 0412 */ 0413 int 0414 gen_get_trackcount(struct wm_drive *d, int *tracks) 0415 { 0416 struct cd_toc_header hdr; 0417 0418 if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr)) 0419 return (-1); 0420 0421 *tracks = hdr.th_ending_track; 0422 0423 return (0); 0424 } /* gen_get_trackcount() */ 0425 0426 /* 0427 * Get the start time and mode (data or audio) of a track. 0428 * 0429 * XXX - this should get cached, but that means keeping track of ejects. 0430 */ 0431 int 0432 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) 0433 { 0434 struct cd_toc toc; 0435 struct cd_toc_header hdr; 0436 struct cd_toc_header_and_entries toc_buffer; 0437 0438 if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr)) 0439 return (-1); 0440 0441 bzero((char *)&toc_buffer, sizeof(toc_buffer)); 0442 toc.toc_address_format = CDROM_MSF_FORMAT; 0443 toc.toc_starting_track = 0; 0444 toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) + 0445 hdr.th_data_len0) & 0xfff) + 2; 0446 toc.toc_buffer = (caddr_t)&toc_buffer; 0447 0448 if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc)) 0449 return (-1); 0450 0451 if (track == 0) 0452 track = hdr.th_ending_track + 1; 0453 0454 *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0; 0455 *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 + 0456 toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 + 0457 toc_buffer.cdte[track - 1].te_absaddr.msf.f_units; 0458 0459 return (0); 0460 } /* gen_get_trackinfo() */ 0461 0462 /* 0463 * Get the number of frames on the CD. 0464 */ 0465 int 0466 gen_get_cdlen( struct wm_drive *d, int *frames ) 0467 { 0468 int tmp; 0469 0470 return (gen_get_trackinfo(d, 0, &tmp, frames)); 0471 } /* gen_get_cdlen() */ 0472 0473 /* 0474 * scale_volume(vol, max) 0475 * 0476 * Return a volume value suitable for passing to the CD-ROM drive. "vol" 0477 * is a volume slider setting; "max" is the slider's maximum value. 0478 * 0479 * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack 0480 * increases much faster toward the top end of the volume scale than it 0481 * does at the bottom. To make up for this, we make the volume scale look 0482 * sort of logarithmic (actually an upside-down inverse square curve) so 0483 * that the volume value passed to the drive changes less and less as you 0484 * approach the maximum slider setting. The actual formula looks like 0485 * 0486 * (max^2 - (max - vol)^2) * (max_volume - min_volume) 0487 * v = --------------------------------------------------- + min_volume 0488 * max^2 0489 * 0490 * If your system's volume settings aren't broken in this way, something 0491 * like the following should work: 0492 * 0493 * return ((vol * (max_volume - min_volume)) / max + min_volume); 0494 */ 0495 scale_volume(int vol, int max) 0496 { 0497 return ((max * max - (max - vol) * (max - vol)) * 0498 (max_volume - min_volume) / (max * max) + min_volume); 0499 } /* scale_volume() */ 0500 0501 /* 0502 * unscale_volume(cd_vol, max) 0503 * 0504 * Given a value between min_volume and max_volume, return the volume slider 0505 * value needed to achieve that value. 0506 * 0507 * Rather than perform floating-point calculations to reverse the above 0508 * formula, we simply do a binary search of scale_volume()'s return values. 0509 */ 0510 static int 0511 unscale_volume( int cd_vol, int max ) 0512 { 0513 int vol = 0, top = max, bot = 0, scaled; 0514 0515 while (bot <= top) 0516 { 0517 vol = (top + bot) / 2; 0518 scaled = scale_volume(vol, max); 0519 if (cd_vol == scaled) 0520 break; 0521 if (cd_vol < scaled) 0522 top = vol - 1; 0523 else 0524 bot = vol + 1; 0525 } 0526 0527 if (vol < 0) 0528 vol = 0; 0529 else if (vol > max) 0530 vol = max; 0531 0532 return (vol); 0533 } /* unscale_volume() */ 0534 0535 /* 0536 * Set the volume level for the left and right channels. Their values 0537 * range from 0 to 100. 0538 */ 0539 int 0540 gen_set_volume(struct wm_drive *d, int left, int right) 0541 { 0542 struct cd_playback pb; 0543 struct cd_playback_status ps; 0544 struct cd_playback_control pc; 0545 0546 left = scale_volume(left, 100); 0547 right = scale_volume(right, 100); 0548 0549 bzero((char *)&pb, sizeof(pb)); 0550 bzero((char *)&ps, sizeof(ps)); 0551 bzero((char *)&pc, sizeof(pc)); 0552 0553 pb.pb_alloc_length = sizeof(ps); 0554 pb.pb_buffer = (caddr_t)&ps; 0555 0556 if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb)) 0557 return (-1); 0558 0559 pc.pc_chan0_select = ps.ps_chan0_select; 0560 pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ? 0561 CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ? 0562 CDROM_MAX_VOLUME : left; 0563 pc.pc_chan1_select = ps.ps_chan1_select; 0564 pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ? 0565 CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ? 0566 CDROM_MAX_VOLUME : right; 0567 0568 pb.pb_alloc_length = sizeof(pc); 0569 pb.pb_buffer = (caddr_t)&pc; 0570 0571 if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb)) 0572 return (-1); 0573 0574 return (0); 0575 } /* gen_set_volume() */ 0576 0577 0578 /* 0579 * Read the initial volume from the drive, if available. Each channel 0580 * ranges from 0 to 100, with -1 indicating data not available. 0581 */ 0582 int 0583 gen_get_volume( struct wm_drive *d, int *left, int *right ) 0584 { 0585 struct cd_playback pb; 0586 struct cd_playback_status ps; 0587 0588 bzero((char *)&pb, sizeof(pb)); 0589 bzero((char *)&ps, sizeof(ps)); 0590 0591 pb.pb_alloc_length = sizeof(ps); 0592 pb.pb_buffer = (caddr_t)&ps; 0593 0594 if (d->fd >= 0) 0595 { 0596 if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb)) 0597 *left = *right = -1; 0598 else 0599 { 0600 *left = unscale_volume(ps.ps_chan0_volume, 100); 0601 *right = unscale_volume(ps.ps_chan1_volume, 100); 0602 } 0603 } 0604 else 0605 *left = *right = -1; 0606 0607 return (0); 0608 } /* gen_get_volume() */ 0609 0610 #endif