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