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 * Sun-specific drive control routines. 0022 */ 0023 0024 #if defined(sun) || defined(__sun) 0025 0026 #include "include/wm_config.h" 0027 #include "include/wm_helpers.h" 0028 #include "include/wm_cdrom.h" 0029 #include "include/wm_cdtext.h" 0030 0031 #include <errno.h> 0032 #include <stdio.h> 0033 #include <sys/types.h> 0034 #include <fcntl.h> 0035 #include <string.h> 0036 #include <sys/param.h> 0037 #include <sys/stat.h> 0038 #include <sys/time.h> 0039 #include <sys/ioctl.h> 0040 0041 #include <sys/statvfs.h> 0042 #include <unistd.h> 0043 #include <signal.h> 0044 #ifdef solbourne 0045 # include <mfg/dklabel.h> 0046 # include <mfg/dkio.h> 0047 # include <sys/unistd.h> 0048 # include <dev/srvar.h> 0049 #else /* A real Sun */ 0050 # ifdef SYSV 0051 # include <poll.h> 0052 # include <stdlib.h> 0053 # include <sys/cdio.h> 0054 # include <sys/socket.h> 0055 # include <sys/scsi/impl/uscsi.h> 0056 # include "include/wm_cdda.h" 0057 # else 0058 # include <sys/buf.h> 0059 # include <sun/dkio.h> 0060 # include <scsi/targets/srdef.h> 0061 # include <scsi/impl/uscsi.h> 0062 # include <scsi/generic/commands.h> 0063 # endif 0064 #endif 0065 0066 #include "include/wm_struct.h" 0067 0068 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM 0069 0070 int min_volume = 0; 0071 int max_volume = 255; 0072 0073 static const char *sun_cd_device = NULL; 0074 extern int intermittent_dev; 0075 0076 int current_end; 0077 0078 #if defined(SYSV) && defined(SIGTHAW) 0079 #ifdef __GNUC__ 0080 void sigthawinit(void) __attribute__ ((constructor)); 0081 #else 0082 #pragma init(sigthawinit) 0083 #endif /* GNUC */ 0084 0085 static int last_left, last_right; 0086 static struct wm_drive *thecd = NULL; 0087 0088 /* 0089 * Handling for Sun's Suspend functionality 0090 */ 0091 static void 0092 thawme(int sig) 0093 { 0094 // Just leave this line in as a reminder for a missing 0095 // functionality in the GUI. 0096 // change_mode(NULL, WM_CDM_STOPPED, NULL); 0097 codec_init(); 0098 if( thecd ) 0099 gen_set_volume(thecd, last_left, last_right); 0100 } /* thawme() */ 0101 0102 void 0103 sigthawinit( void ) 0104 { 0105 struct sigaction sa; 0106 0107 sa.sa_handler = thawme; 0108 sigemptyset(&sa.sa_mask); 0109 sa.sa_flags = 0; 0110 0111 sigaction(SIGTHAW, &sa, NULL); 0112 } /* sigthawinit() */ 0113 0114 #endif /* SYSV && SIGTHAW */ 0115 0116 /* 0117 * find_cdrom 0118 * 0119 * Determine the name of the CD-ROM device. 0120 * 0121 * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0 0122 * that exists. (Check for /vol/dev/aliases, not cdrom0, since it won't be 0123 * there if there's no CD in the drive.) This is done so a single SunOS 4.x 0124 * binary can be used on any 4.x or higher Sun system. 0125 */ 0126 const char* 0127 find_cdrom() 0128 { 0129 if (access("/vol/dev/aliases", X_OK) == 0) 0130 { 0131 /* Volume manager. Device might not be there. */ 0132 intermittent_dev = 1; 0133 0134 /* If vold is running us, it'll tell us the device name. */ 0135 sun_cd_device = getenv("VOLUME_DEVICE"); 0136 /* 0137 ** the path of the device has to include /dev 0138 ** otherwise we are vulnerable to race conditions 0139 ** Thomas Biege <thomas@suse.de> 0140 */ 0141 if (sun_cd_device == NULL || 0142 strncmp("/vol/dev/", sun_cd_device, 9) || 0143 strstr(sun_cd_device, "/../") ) 0144 return "/vol/dev/aliases/cdrom0"; 0145 else 0146 return sun_cd_device; 0147 } 0148 else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0) 0149 { 0150 /* Solaris 2.x w/o volume manager. */ 0151 return "/dev/rdsk/c0t6d0s2"; 0152 } 0153 else if (access("/dev/rcd0", F_OK) == 0) 0154 { 0155 return "/dev/rcd0"; 0156 } 0157 else if (access("/dev/rsr0", F_OK) == 0) 0158 return "/dev/rsr0"; 0159 else 0160 { 0161 fprintf(stderr, "Could not find a CD device!\n"); 0162 return NULL; 0163 } 0164 } /* find_cdrom() */ 0165 0166 /* 0167 * Initialize the drive. A no-op for the generic driver. 0168 */ 0169 int 0170 gen_init( struct wm_drive *d ) 0171 { 0172 codec_init(); 0173 return 0; 0174 } /* gen_init() */ 0175 0176 0177 /* 0178 * Open the CD device and figure out what kind of drive is attached. 0179 */ 0180 int 0181 gen_open( struct wm_drive *d ) 0182 { 0183 static int warned = 0; 0184 0185 if (d->fd >= 0) { /* Device already open? */ 0186 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (fd=%d)]\n", d->fd); 0187 return 0; 0188 } 0189 0190 0191 d->fd = open(d->cd_device, 0); 0192 if (d->fd < 0) { 0193 /* Solaris 2.2 volume manager moves links around */ 0194 if (errno == ENOENT && intermittent_dev) 0195 return 1; 0196 0197 if (errno == EACCES) { 0198 if (!warned) { 0199 /* char realname[MAXPATHLEN]; 0200 if (realpath(cd_device, realname) == NULL) { 0201 perror("realpath"); 0202 return 1; 0203 } */ 0204 return -EACCES; 0205 } 0206 } else if (errno != ENXIO) { 0207 return -6; 0208 } 0209 0210 /* No CD in drive. */ 0211 return 1; 0212 } 0213 0214 thecd = d; 0215 0216 return 0; 0217 } 0218 0219 /* 0220 * Send an arbitrary SCSI command out the bus and optionally wait for 0221 * a reply if "retbuf" isn't NULL. 0222 */ 0223 int 0224 gen_scsi( struct wm_drive *d, 0225 unsigned char *cdb, 0226 int cdblen, void *retbuf, 0227 int retbuflen, int getreply ) 0228 { 0229 #ifndef solbourne 0230 char x; 0231 struct uscsi_cmd cmd; 0232 0233 memset(&cmd, 0, sizeof(cmd)); 0234 cmd.uscsi_cdb = (void *) cdb; 0235 cmd.uscsi_cdblen = cdblen; 0236 cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x; 0237 cmd.uscsi_buflen = retbuf ? retbuflen : 0; 0238 cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT; 0239 if (getreply) 0240 cmd.uscsi_flags |= USCSI_READ; 0241 0242 if (ioctl(d->fd, USCSICMD, &cmd)) 0243 return -1; 0244 0245 if (cmd.uscsi_status) 0246 return -1; 0247 0248 return 0; 0249 #else 0250 return -1; 0251 #endif 0252 } 0253 0254 int 0255 gen_close( struct wm_drive *d ) 0256 { 0257 if(d->fd != -1) { 0258 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); 0259 close(d->fd); 0260 d->fd = -1; 0261 } 0262 return 0; 0263 } 0264 0265 /* Alarm signal handler. */ 0266 static void do_nothing( int x ) { x++; } 0267 0268 /* 0269 * Get the current status of the drive: the current play mode, the absolute 0270 * position from start of disc (in frames), and the current track and index 0271 * numbers if the CD is playing or paused. 0272 */ 0273 int 0274 gen_get_drive_status( struct wm_drive *d, 0275 int oldmode, 0276 int *mode, 0277 int *pos, int *track, int *index ) 0278 { 0279 struct cdrom_subchnl sc; 0280 struct itimerval old_timer, new_timer; 0281 struct sigaction old_sig, new_sig; 0282 0283 /* If we can't get status, the CD is ejected, so default to that. */ 0284 *mode = WM_CDM_EJECTED; 0285 0286 /* Is the device open? */ 0287 if (d->fd < 0) { 0288 switch (d->proto.open(d)) { 0289 case -1: /* error */ 0290 return -1; 0291 0292 case 1: /* retry */ 0293 return 0; 0294 } 0295 } 0296 0297 /* 0298 * Solaris 2.2 hangs on this ioctl if someone else ejects the CD. 0299 * So we schedule a signal to break out of the hang if the call 0300 * takes an unreasonable amount of time. The signal handler and 0301 * timer are restored immediately to avoid interfering with XView. 0302 */ 0303 if (intermittent_dev) { 0304 /* 0305 * First clear out the timer so XView's signal doesn't happen 0306 * while we're diddling with the signal handler. 0307 */ 0308 timerclear(&new_timer.it_interval); 0309 timerclear(&new_timer.it_value); 0310 setitimer(ITIMER_REAL, &new_timer, &old_timer); 0311 0312 /* 0313 * Now install the no-op signal handler. 0314 */ 0315 new_sig.sa_handler = do_nothing; 0316 memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask)); 0317 new_sig.sa_flags = 0; 0318 if (sigaction(SIGALRM, &new_sig, &old_sig)) 0319 perror("sigaction"); 0320 0321 /* 0322 * And finally, set the timer. 0323 */ 0324 new_timer.it_value.tv_sec = 2; 0325 setitimer(ITIMER_REAL, &new_timer, NULL); 0326 } 0327 0328 sc.cdsc_format = CDROM_MSF; 0329 0330 if (ioctl(d->fd, CDROMSUBCHNL, &sc)) { 0331 if (intermittent_dev) { 0332 sigaction(SIGALRM, &old_sig, NULL); 0333 setitimer(ITIMER_REAL, &old_timer, NULL); 0334 0335 /* If the device can disappear, let it do so. */ 0336 d->proto.close(d); 0337 } 0338 0339 return 0; 0340 } 0341 0342 if (intermittent_dev) { 0343 sigaction(SIGALRM, &old_sig, NULL); 0344 setitimer(ITIMER_REAL, &old_timer, NULL); 0345 } 0346 0347 switch (sc.cdsc_audiostatus) { 0348 case CDROM_AUDIO_PLAY: 0349 *mode = WM_CDM_PLAYING; 0350 *track = sc.cdsc_trk; 0351 *index = sc.cdsc_ind; 0352 *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 + 0353 sc.cdsc_absaddr.msf.second * 75 + 0354 sc.cdsc_absaddr.msf.frame; 0355 break; 0356 0357 case CDROM_AUDIO_PAUSED: 0358 case CDROM_AUDIO_INVALID: 0359 case CDROM_AUDIO_NO_STATUS: 0360 if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) 0361 { 0362 *mode = WM_CDM_PAUSED; 0363 *track = sc.cdsc_trk; 0364 *index = sc.cdsc_ind; 0365 *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 + 0366 sc.cdsc_absaddr.msf.second * 75 + 0367 sc.cdsc_absaddr.msf.frame; 0368 } 0369 else 0370 *mode = WM_CDM_STOPPED; 0371 break; 0372 0373 /* CD ejected manually during play. */ 0374 case CDROM_AUDIO_ERROR: 0375 break; 0376 0377 case CDROM_AUDIO_COMPLETED: 0378 *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ 0379 break; 0380 0381 default: 0382 *mode = WM_CDM_UNKNOWN; 0383 break; 0384 } 0385 0386 return 0; 0387 } /* gen_get_drive_status() */ 0388 0389 /* 0390 * Get the number of tracks on the CD. 0391 */ 0392 int 0393 gen_get_trackcount( struct wm_drive *d, int *tracks ) 0394 { 0395 struct cdrom_tochdr hdr; 0396 0397 if (ioctl(d->fd, CDROMREADTOCHDR, &hdr)) 0398 return -1; 0399 0400 *tracks = hdr.cdth_trk1; 0401 return 0; 0402 } /* gen_get_trackcount() */ 0403 0404 /* 0405 * Get the start time and mode (data or audio) of a track. 0406 */ 0407 int 0408 gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe) 0409 { 0410 struct cdrom_tocentry entry; 0411 0412 entry.cdte_track = track; 0413 entry.cdte_format = CDROM_MSF; 0414 0415 if (ioctl(d->fd, CDROMREADTOCENTRY, &entry)) 0416 return -1; 0417 0418 *startframe = entry.cdte_addr.msf.minute * 60 * 75 + 0419 entry.cdte_addr.msf.second * 75 + 0420 entry.cdte_addr.msf.frame; 0421 *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0; 0422 0423 return 0; 0424 } /* gen_get_trackinfo() */ 0425 0426 /* 0427 * Get the number of frames on the CD. 0428 */ 0429 int 0430 gen_get_cdlen(struct wm_drive *d, int *frames ) 0431 { 0432 int tmp; 0433 0434 return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames)); 0435 } /* gen_get_cdlen() */ 0436 0437 /* 0438 * Play the CD from one position to another. 0439 * 0440 * d Drive structure. 0441 * start Frame to start playing at. 0442 * end End of this chunk. 0443 */ 0444 int 0445 gen_play( struct wm_drive *d, int start, int end) 0446 { 0447 struct cdrom_msf msf; 0448 unsigned char cmdbuf[10]; 0449 0450 current_end = end; 0451 0452 msf.cdmsf_min0 = start / (60*75); 0453 msf.cdmsf_sec0 = (start % (60*75)) / 75; 0454 msf.cdmsf_frame0 = start % 75; 0455 msf.cdmsf_min1 = end / (60*75); 0456 msf.cdmsf_sec1 = (end % (60*75)) / 75; 0457 msf.cdmsf_frame1 = end % 75; 0458 0459 codec_start(); 0460 if (ioctl(d->fd, CDROMSTART)) 0461 return -1; 0462 if (ioctl(d->fd, CDROMPLAYMSF, &msf)) 0463 return -2; 0464 0465 return 0; 0466 } /* gen_play() */ 0467 0468 /* 0469 * Pause the CD. 0470 */ 0471 int 0472 gen_pause( struct wm_drive *d ) 0473 { 0474 codec_stop(); 0475 return (ioctl(d->fd, CDROMPAUSE)); 0476 } /* gen_pause() */ 0477 0478 /* 0479 * Resume playing the CD (assuming it was paused.) 0480 */ 0481 int 0482 gen_resume( struct wm_drive *d ) 0483 { 0484 codec_start(); 0485 return (ioctl(d->fd, CDROMRESUME)); 0486 } /* gen_resume() */ 0487 0488 /* 0489 * Stop the CD. 0490 */ 0491 int 0492 gen_stop( struct wm_drive *d ) 0493 { 0494 codec_stop(); 0495 return (ioctl(d->fd, CDROMSTOP)); 0496 } /* gen_stop() */ 0497 0498 /* 0499 * Eject the current CD, if there is one. 0500 */ 0501 int 0502 gen_eject( struct wm_drive *d )cddax 0503 { 0504 struct stat stbuf; 0505 struct statvfs ust; 0506 0507 if (fstat(d->fd, &stbuf) != 0) 0508 return -2; 0509 0510 /* Is this a mounted filesystem? */ 0511 if (statvfs(stbuf.st_rdev, &ust) == 0) 0512 return -3; 0513 0514 if (ioctl(d->fd, CDROMEJECT)) 0515 return -1; 0516 0517 /* Close the device if it needs to vanish. */ 0518 if (intermittent_dev) { 0519 d->proto.close(d); 0520 /* Also remember to tell the cddaslave since volume 0521 manager switches links around on us */ 0522 if (d->cdda_slave > -1) { 0523 write(d->cdda_slave, "E", 1); 0524 } 0525 } 0526 0527 return 0; 0528 } /* gen_eject() */ 0529 0530 /*----------------------------------------* 0531 * Close the CD tray 0532 * 0533 * Please edit and send changes to 0534 * milliByte@DeathsDoor.com 0535 *----------------------------------------*/ 0536 0537 int 0538 gen_closetray(struct wm_drive *d) 0539 { 0540 return -1; 0541 } /* gen_closetray() */ 0542 0543 /* 0544 * Set the volume level for the left and right channels. Their values 0545 * range from 0 to 100. 0546 */ 0547 int 0548 gen_set_volume( struct wm_drive *d, int left, int right ) 0549 { 0550 struct cdrom_volctrl v; 0551 0552 #if defined(SIGTHAW) && defined(SYSV) 0553 last_left = left; 0554 last_right = right; 0555 thecd = d; 0556 #endif 0557 0558 CDDARETURN(d) cdda_set_volume(d, left, right); 0559 0560 left = (left * (max_volume - min_volume)) / 100 + min_volume; 0561 right = (right * (max_volume - min_volume)) / 100 + min_volume; 0562 0563 v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left; 0564 v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right; 0565 0566 return (ioctl(d->fd, CDROMVOLCTRL, &v)); 0567 } /* gen_set_volume() */ 0568 0569 /* 0570 * Read the volume from the drive, if available. Each channel 0571 * ranges from 0 to 100, with -1 indicating data not available. 0572 */ 0573 int 0574 gen_get_volume( struct wm_drive *d, int *left, int *right ) 0575 { 0576 CDDARETURN(d) cdda_get_volume(d, left, right); 0577 0578 *left = *right = -1; 0579 0580 return (wm_scsi2_get_volume(d, left, right)); 0581 } /* gen_get_volume() */ 0582 0583 #define CDDABLKSIZE 2368 0584 #define SAMPLES_PER_BLK 588 0585 0586 /* 0587 * This is the fastest way to convert from BCD to 8-bit. 0588 */ 0589 static unsigned char unbcd[256] = { 0590 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,0,0,0,0,0, 0591 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,0,0,0,0,0, 0592 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0,0,0,0,0,0, 0593 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,0,0,0,0,0, 0594 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0,0,0,0,0,0, 0595 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0,0,0,0,0,0, 0596 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0,0,0,0,0,0, 0597 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0,0,0,0,0,0, 0598 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0,0,0,0,0,0, 0599 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0,0,0,0,0,0, 0600 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0601 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0602 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0603 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0604 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0605 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0606 }; 0607 /* 0608 * Try to initialize the CDDA slave. Returns 0 on success. 0609 */ 0610 int 0611 gen_cdda_init( struct wm_drive *d ) 0612 { 0613 enable_cdda_controls(1); 0614 return 0; 0615 } 0616 0617 /* 0618 * Initialize the CDDA data buffer and open the appropriate device. 0619 * 0620 */ 0621 int gen_cdda_open(struct wm_drive *d) 0622 { 0623 int i; 0624 struct cdrom_cdda cdda; 0625 0626 if (d->fd < 0) 0627 return -1; 0628 0629 for (i = 0; i < d->numblocks; ++i) { 0630 d->blocks[i].buflen = d->frames_at_once * CDDABLKSIZE; 0631 d->blocks[i].buf = malloc(d->blocks[i].buflen); 0632 if (!d->blocks[i].buf) { 0633 ERRORLOG("plat_cdda_open: ENOMEM\n"); 0634 return -ENOMEM; 0635 } 0636 } 0637 0638 cdda.cdda_addr = 200; 0639 cdda.cdda_length = 1; 0640 cdda.cdda_data = d->blocks[0].buf; 0641 cdda.cdda_subcode = CDROM_DA_SUBQ; 0642 0643 d->status = WM_CDM_STOPPED; 0644 if((ioctl(d->fd, CDROMCDDA, &cdda) < 0)) { 0645 return -1; 0646 } 0647 0648 return 0; 0649 } 0650 0651 /* 0652 * Normalize a bunch of CDDA data. Basically this means ripping out the 0653 * Q subchannel data and doing byte-swapping, since the CD audio is in 0654 * littleendian format. 0655 * 0656 * Scanning is handled here too. 0657 * 0658 * XXX - do byte swapping on Intel boxes? 0659 */ 0660 long 0661 sun_normalize(struct cdda_block *block) 0662 { 0663 int i, nextq; 0664 long buflen = block->buflen; 0665 int blocks = buflen / CDDABLKSIZE; 0666 unsigned char *rawbuf = block->buf; 0667 unsigned char *dest = rawbuf; 0668 unsigned char tmp; 0669 long *buf32 = (long *)rawbuf, tmp32; 0670 0671 /* 0672 * this was #ifndef LITTLEENDIAN 0673 * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw? 0674 */ 0675 #if WM_BIG_ENDIAN 0676 if (blocks--) { 0677 for (i = 0; i < SAMPLES_PER_BLK * 2; ++i) { 0678 /* Only need to use temp buffer on first block. */ 0679 tmp = *rawbuf++; 0680 *dest++ = *rawbuf++; 0681 *dest++ = tmp; 0682 } 0683 } 0684 #endif 0685 0686 while (blocks--) { 0687 /* Skip over Q data. */ 0688 rawbuf += 16; 0689 0690 for (i = 0; i < SAMPLES_PER_BLK * 2; i++) { 0691 #if WM_LITTLE_ENDIAN 0692 *dest++ = *rawbuf++; 0693 *dest++ = *rawbuf++; 0694 #else 0695 *dest++ = rawbuf[1]; 0696 *dest++ = rawbuf[0]; 0697 rawbuf += 2; 0698 #endif 0699 } 0700 } 0701 0702 buflen -= ((buflen / CDDABLKSIZE) * 16); 0703 0704 return buflen; 0705 } 0706 0707 /* 0708 * Read some blocks from the CD. Stop if we hit the end of the current region. 0709 * 0710 * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason. 0711 */ 0712 int gen_cdda_read(struct wm_drive *d, struct wm_cdda_block *block) 0713 { 0714 struct cdrom_cdda cdda; 0715 int blk; 0716 unsigned char *q; 0717 unsigned char* rawbuf = block->buf; 0718 0719 if (d->fd < 0) 0720 return -1; 0721 0722 /* Hit the end of the CD, probably. */ 0723 if ((direction > 0 && d->current_position >= d->ending_position) || 0724 (direction < 0 && d->current_position < d->starting_position)) { 0725 block->status = WM_CDM_TRACK_DONE; 0726 return 0; 0727 } 0728 0729 cdda.cdda_addr = d->current_position - 150; 0730 if (d->ending_position && d->current_position + d->frames_at_once > d->ending_position) 0731 cdda.cdda_length = d->ending_position - d->current_position; 0732 else 0733 cdda.cdda_length = d->frames_at_once; 0734 cdda.cdda_data = (unsigned char*)block->buf; 0735 cdda.cdda_subcode = CDROM_DA_SUBQ; 0736 0737 if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) { 0738 if (errno == ENXIO) { /* CD ejected! */ 0739 block->status = WM_CDM_EJECTED; 0740 return -1; 0741 } 0742 0743 /* Sometimes it fails once, dunno why */ 0744 if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) { 0745 if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) { 0746 if (ioctl(d->fd, CDROMCDDA, &cdda) < 0) { 0747 perror("CDROMCDDA"); 0748 block->status = WM_CDM_CDDAERROR; 0749 return -1; 0750 } 0751 } 0752 } 0753 } 0754 0755 d->current_position = d->current_position + cdda.cdda_length * direction; 0756 0757 #if 0 0758 /* 0759 * New valid Q-subchannel information? Update the block 0760 * status. 0761 */ 0762 for (blk = 0; blk < d->numblocks; ++blk) { 0763 q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4]; 0764 if (*q == 1) { 0765 block->track = unbcd[q[1]]; 0766 block->index = unbcd[q[2]]; 0767 /*block->minute = unbcd[q[7]]; 0768 block->second = unbcd[q[8]];*/ 0769 block->frame = unbcd[q[9]]; 0770 block->status = WM_CDM_PLAYING; 0771 block->buflen = cdda.cdda_length; 0772 } 0773 } 0774 #endif 0775 return sun_normalize(block); 0776 } 0777 0778 /* 0779 * Close the CD-ROM device in preparation for exiting. 0780 */ 0781 int gen_cdda_close(struct wm_drive *d) 0782 { 0783 int i; 0784 0785 if (d->fd < 0) 0786 return -1; 0787 0788 for (i = 0; i < d->numblocks; i++) { 0789 free(d->blocks[i].buf); 0790 d->blocks[i].buf = 0; 0791 d->blocks[i].buflen = 0; 0792 } 0793 0794 return 0; 0795 } 0796 0797 /* 0798 * The following code activates the internal CD audio passthrough on 0799 * SPARCstation 5 systems (and possibly others.) 0800 * 0801 * Thanks to <stevep@ctc.ih.att.com>, Roger Oscarsson <roger@cs.umu.se> 0802 * and Steve McKinty <> 0803 * 0804 * Most CD drives have a headphone socket on the front, but it 0805 * is often more convenient to route the audio though the 0806 * built-in audio device. That way the user can leave their 0807 * headphones plugged-in to the base system, for use with 0808 * other audio stuff like ShowMeTV 0809 */ 0810 0811 #ifdef CODEC /* { */ 0812 #ifdef SYSV /* { */ 0813 0814 # include <sys/ioctl.h> 0815 # include <sys/audioio.h> 0816 # include <stdlib.h> 0817 0818 #else /* } { */ 0819 0820 # include <sun/audioio.h> 0821 # define AUDIO_DEV_SS5STYLE 5 0822 typedef int audio_device_t; 0823 0824 #endif /* } */ 0825 #endif /* } */ 0826 0827 /* 0828 * Don't do anything with /dev/audio if we can't set it to high quality. 0829 * Also, don't do anything real if it's not Solaris. 0830 */ 0831 #if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */ 0832 codec_init() { return 0; } 0833 codec_start() { return 0; } 0834 codec_stop() { return 0; } 0835 #else 0836 0837 #ifndef AUDIO_INTERNAL_CD_IN 0838 #define AUDIO_INTERNAL_CD_IN 0x4 0839 #endif 0840 0841 static char* devname = 0; 0842 static char* ctlname = 0; 0843 static int ctl_fd = -1; 0844 static int port = AUDIO_LINE_IN; 0845 int internal_audio = 1; 0846 0847 codec_init( void ) 0848 { 0849 register int i; 0850 char* ctlname; 0851 audio_info_t foo; 0852 audio_device_t aud_dev; 0853 0854 if (internal_audio == 0) 0855 { 0856 ctl_fd = -1; 0857 return 0; 0858 } 0859 0860 if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio"; 0861 ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl"); 0862 if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0) 0863 { 0864 perror(ctlname); 0865 return -1; 0866 } 0867 if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0) 0868 { 0869 close(ctl_fd); 0870 ctl_fd = -1; 0871 return -1; 0872 } 0873 /* 0874 * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones. 0875 * Not sure if this is all correct. 0876 */ 0877 #ifdef SYSV 0878 if (strcmp(aud_dev.name, "SUNW,CS4231") && 0879 strcmp(aud_dev.name, "SUNW,sb16") && 0880 strcmp(aud_dev.name, "SUNW,sbpro")) 0881 #else 0882 if (aud_dev != AUDIO_DEV_SS5STYLE) 0883 #endif 0884 { 0885 close(ctl_fd); 0886 ctl_fd = -1; 0887 return 0; /* but it's okay */ 0888 } 0889 0890 /* 0891 * Does the chosen device have an internal CD port? 0892 * If so, use it. If not then try and use the 0893 * Line In port. 0894 */ 0895 if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0) 0896 { 0897 perror("AUDIO_GETINFO"); 0898 close(ctl_fd); 0899 ctl_fd = -1; 0900 return -1; 0901 } 0902 if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN) 0903 port = AUDIO_INTERNAL_CD_IN; 0904 else 0905 port = AUDIO_LINE_IN; 0906 0907 /* 0908 * now set it up to use it. See audio(7I) 0909 */ 0910 0911 AUDIO_INITINFO(&foo); 0912 foo.record.port = port; 0913 foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE; 0914 0915 if (d->cdda_slave > -1) 0916 foo.monitor_gain = 0; 0917 else 0918 foo.monitor_gain = AUDIO_MAX_GAIN; 0919 /* 0920 * These next ones are tricky. The voulme will depend on the CD drive 0921 * volume (set by the knob on the drive and/or by workman's volume 0922 * control), the audio device record gain and the audio device 0923 * play gain. For simplicity we set the latter two to something 0924 * reasonable, but we don't force them to be reset if the user 0925 * wants to change them. 0926 */ 0927 foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100; 0928 foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100; 0929 0930 ioctl(ctl_fd, AUDIO_SETINFO, &foo); 0931 return 0; 0932 } 0933 0934 static int 0935 kick_codec( void ) 0936 { 0937 audio_info_t foo; 0938 int dev_fd; 0939 int retval = 0; 0940 0941 /* 0942 * Open the audio device, not the control device. This 0943 * will fail if someone else has taken it. 0944 */ 0945 0946 if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0) 0947 { 0948 perror(devname); 0949 return -1; 0950 } 0951 0952 AUDIO_INITINFO(&foo); 0953 foo.record.port = port; 0954 foo.monitor_gain = AUDIO_MAX_GAIN; 0955 0956 /* These can only be set on the real device */ 0957 foo.play.sample_rate = 44100; 0958 foo.play.channels = 2; 0959 foo.play.precision = 16; 0960 foo.play.encoding = AUDIO_ENCODING_LINEAR; 0961 0962 if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0) 0963 perror(devname); 0964 0965 close(dev_fd); 0966 return retval; 0967 } /* kick_codec() */ 0968 0969 codec_start( void ) 0970 { 0971 audio_info_t foo; 0972 0973 if (ctl_fd < 0) 0974 return 0; 0975 0976 if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0) 0977 return -1; 0978 0979 if (foo.play.channels != 2) return kick_codec(); 0980 if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec(); 0981 if (foo.play.precision != 16) return kick_codec(); 0982 if (foo.play.sample_rate != 44100) return kick_codec(); 0983 0984 if (foo.record.channels != 2) return kick_codec(); 0985 if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec(); 0986 if (foo.record.precision != 16) return kick_codec(); 0987 if (foo.record.sample_rate != 44100) return kick_codec(); 0988 0989 if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec(); 0990 if (foo.record.port != port) return kick_codec(); 0991 0992 return 0; 0993 } /* codec_start() */ 0994 0995 codec_stop( void ) { return 0; } 0996 0997 #endif /* CODEC } */ 0998 0999 #endif /* sun */