2 * $Id: plat_ultrix.c,v 1.6 1999/03/07 08:36:41 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * ULTRIX 4.2 drive control routines.
28 #if defined(ultrix) || defined(__ultrix)
30 static char plat_ultrix_id
[] = "$Id: plat_ultrix.c,v 1.6 1999/03/07 08:36:41 dirk Exp $";
34 #include <sys/types.h>
36 #include <sys/param.h>
41 #include <sys/rzdisk.h>
42 #include <sys/cdrom.h>
44 #include "include/wm_config.h"
45 #include "include/wm_struct.h"
47 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
50 * This structure will be filled with the TOC header and all entries.
51 * Ultrix doesn't seem to allow getting single TOC entries.
52 * - Chris Ross (cross@eng.umd.edu)
54 struct cd_toc_header_and_entries
{
55 struct cd_toc_header cdth
;
56 struct cd_toc_entry cdte
[CDROM_MAX_TRACK
+1];
65 extern char *cd_device
;
70 * Determine the name of the CD-ROM device.
72 * Read through the boot records (via a call to uerf) and find the SCSI
73 * address of the CD-ROM. If the "CDROM" environment variable is set,
79 char *data
, *fgetline();
84 if ((cd_device
= getenv("CDROM")) != NULL
)
89 if ((pid
= fork()) == 0) {
92 execl("/etc/uerf", "uerf", "-R", "-r", "300", NULL
);
93 execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", NULL
);
101 uerf
= fdopen(fds
[0], "r");
103 while (data
= fgetline(uerf
))
104 if (strstr(data
, "RRD42")) {
107 cd_device
= (char *)malloc(sizeof("/dev/rrz##c"));
108 strcpy(cd_device
, "/dev/r");
109 device
= strstr(data
, "rz");
110 device
[(int)(strchr(device
, ' ') - device
)] = '\0';
111 strcat(cd_device
, device
);
112 strcat(cd_device
, "c");
118 if (cd_device
== NULL
) {
120 "No cdrom (RRD42) is installed on this system\n");
125 (void)wait((int *)NULL
);
129 * Initialize the drive. A no-op for the generic driver.
139 * Get the number of tracks on the CD.
142 gen_get_trackcount(d
, tracks
)
146 struct cd_toc_header hdr
;
148 if (ioctl(d
->fd
, CDROM_TOC_HEADER
, &hdr
))
151 *tracks
= hdr
.th_ending_track
;
157 * Get the start time and mode (data or audio) of a track.
159 * XXX - this should get cached, but that means keeping track of ejects.
162 gen_get_trackinfo(d
, track
, data
, startframe
)
164 int track
, *data
, *startframe
;
167 struct cd_toc_header hdr
;
168 struct cd_toc_header_and_entries toc_buffer
;
170 if (ioctl(d
->fd
, CDROM_TOC_HEADER
, &hdr
))
173 bzero((char *)&toc_buffer
, sizeof(toc_buffer
));
174 toc
.toc_address_format
= CDROM_MSF_FORMAT
;
175 toc
.toc_starting_track
= 0;
176 toc
.toc_alloc_length
= (u_short
)(((hdr
.th_data_len1
<< 8) +
177 hdr
.th_data_len0
) & 0xfff) + 2;
178 toc
.toc_buffer
= (caddr_t
)&toc_buffer
;
180 if (ioctl(d
->fd
, CDROM_TOC_ENTRYS
, &toc
))
184 track
= hdr
.th_ending_track
+ 1;
186 *data
= (toc_buffer
.cdte
[track
-1].te_control
& CDROM_DATA_TRACK
) ? 1:0;
187 *startframe
= toc_buffer
.cdte
[track
- 1].te_absaddr
.msf
.m_units
*60*75 +
188 toc_buffer
.cdte
[track
- 1].te_absaddr
.msf
.s_units
* 75 +
189 toc_buffer
.cdte
[track
- 1].te_absaddr
.msf
.f_units
;
195 * Get the number of frames on the CD.
198 gen_get_cdlen(d
, frames
)
204 return (gen_get_trackinfo(d
, 0, &tmp
, frames
));
208 * Get the current status of the drive: the current play mode, the absolute
209 * position from start of disc (in frames), and the current track and index
210 * numbers if the CD is playing or paused.
213 gen_get_drive_status(d
, oldmode
, mode
, pos
, track
, index
)
215 enum wm_cd_modes oldmode
, *mode
;
216 int *pos
, *track
, *index
;
218 struct cd_sub_channel sc
;
219 struct cd_subc_channel_data scd
;
221 /* If we can't get status, the CD is ejected, so default to that. */
222 *mode
= WM_CDM_EJECTED
;
224 sc
.sch_address_format
= CDROM_MSF_FORMAT
;
225 sc
.sch_data_format
= CDROM_CURRENT_POSITION
;
226 sc
.sch_track_number
= 0;
227 sc
.sch_alloc_length
= sizeof(scd
);
228 sc
.sch_buffer
= (caddr_t
)&scd
;
230 /* Is the device open? */
233 switch (wmcd_open(d
)) {
242 if (ioctl(d
->fd
, CDROM_READ_SUBCHANNEL
, &sc
))
243 return (0); /* ejected */
245 switch (scd
.scd_header
.sh_audio_status
) {
246 case AS_PLAY_IN_PROGRESS
:
247 *mode
= WM_CDM_PLAYING
;
249 *pos
= scd
.scd_position_data
.scp_absaddr
.msf
.m_units
* 60 * 75 +
250 scd
.scd_position_data
.scp_absaddr
.msf
.s_units
* 75 +
251 scd
.scd_position_data
.scp_absaddr
.msf
.f_units
;
252 *track
= scd
.scd_position_data
.scp_track_number
;
253 *index
= scd
.scd_position_data
.scp_index_number
;
257 if (oldmode
== WM_CDM_PLAYING
|| oldmode
== WM_CDM_PAUSED
)
259 *mode
= WM_CDM_PAUSED
;
263 *mode
= WM_CDM_STOPPED
;
266 case AS_PLAY_COMPLETED
:
267 *mode
= WM_CDM_TRACK_DONE
; /* waiting for next track. */
271 *mode
= WM_CDM_STOPPED
;
279 * scale_volume(vol, max)
281 * Return a volume value suitable for passing to the CD-ROM drive. "vol"
282 * is a volume slider setting; "max" is the slider's maximum value.
284 * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
285 * increases much faster toward the top end of the volume scale than it
286 * does at the bottom. To make up for this, we make the volume scale look
287 * sort of logarithmic (actually an upside-down inverse square curve) so
288 * that the volume value passed to the drive changes less and less as you
289 * approach the maximum slider setting. The actual formula looks like
291 * (max^2 - (max - vol)^2) * (max_volume - min_volume)
292 * v = --------------------------------------------------- + min_volume
295 * If your system's volume settings aren't broken in this way, something
296 * like the following should work:
298 * return ((vol * (max_volume - min_volume)) / max + min_volume);
300 scale_volume(vol
, max
)
303 return ((max
* max
- (max
- vol
) * (max
- vol
)) *
304 (max_volume
- min_volume
) / (max
* max
) + min_volume
);
308 * Set the volume level for the left and right channels. Their values
309 * range from 0 to 100.
312 gen_set_volume(d
, left
, right
)
316 struct cd_playback pb
;
317 struct cd_playback_status ps
;
318 struct cd_playback_control pc
;
320 left
= scale_volume(left
, 100);
321 right
= scale_volume(right
, 100);
323 bzero((char *)&pb
, sizeof(pb
));
324 bzero((char *)&ps
, sizeof(ps
));
325 bzero((char *)&pc
, sizeof(pc
));
327 pb
.pb_alloc_length
= sizeof(ps
);
328 pb
.pb_buffer
= (caddr_t
)&ps
;
330 if (ioctl(d
->fd
, CDROM_PLAYBACK_STATUS
, &pb
))
333 pc
.pc_chan0_select
= ps
.ps_chan0_select
;
334 pc
.pc_chan0_volume
= (left
< CDROM_MIN_VOLUME
) ?
335 CDROM_MIN_VOLUME
: (left
> CDROM_MAX_VOLUME
) ?
336 CDROM_MAX_VOLUME
: left
;
337 pc
.pc_chan1_select
= ps
.ps_chan1_select
;
338 pc
.pc_chan1_volume
= (right
< CDROM_MIN_VOLUME
) ?
339 CDROM_MIN_VOLUME
: (right
> CDROM_MAX_VOLUME
) ?
340 CDROM_MAX_VOLUME
: right
;
342 pb
.pb_alloc_length
= sizeof(pc
);
343 pb
.pb_buffer
= (caddr_t
)&pc
;
345 if (ioctl(d
->fd
, CDROM_PLAYBACK_CONTROL
, &pb
))
358 return (ioctl(d
->fd
, CDROM_PAUSE_PLAY
));
362 * Resume playing the CD (assuming it was paused.)
368 return (ioctl(d
->fd
, CDROM_RESUME_PLAY
));
378 return (ioctl(d
->fd
, SCSI_STOP_UNIT
));
382 * Play the CD from one position to another (both in frames.)
385 gen_play(d
, start
, end
)
389 struct cd_play_audio_msf msf
;
391 msf
.msf_starting_M_unit
= start
/ (60*75);
392 msf
.msf_starting_S_unit
= (start
% (60*75)) / 75;
393 msf
.msf_starting_F_unit
= start
% 75;
394 msf
.msf_ending_M_unit
= end
/ (60*75);
395 msf
.msf_ending_S_unit
= (end
% (60*75)) / 75;
396 msf
.msf_ending_F_unit
= end
% 75;
398 if (ioctl(d
->fd
, SCSI_START_UNIT
))
400 if (ioctl(d
->fd
, CDROM_PLAY_MSF
, &msf
))
407 * Eject the current CD, if there is one.
410 gen_eject(struct wm_drive
*d
)
412 /* On some systems, we can check to see if the CD is mounted. */
416 if (fstat(d
->fd
, &stbuf
) != 0)
419 /* Is this a mounted filesystem? */
420 if (ustat(stbuf
.st_rdev
, &ust
) == 0)
423 return (ioctl(d
->fd
, CDROM_EJECT_CADDY
));
426 /*----------------------------------------*
429 * Please edit and send changes to
430 * milliByte@DeathsDoor.com
431 *----------------------------------------*/
433 int gen_closetray(struct wm_drive
*d
)
439 return(wmcd_reopen(d
));
444 /* Always succeed if the drive can't close */
446 #endif /* CAN_CLOSE */
447 } /* gen_closetray() */
451 * unscale_volume(cd_vol, max)
453 * Given a value between min_volume and max_volume, return the volume slider
454 * value needed to achieve that value.
456 * Rather than perform floating-point calculations to reverse the above
457 * formula, we simply do a binary search of scale_volume()'s return values.
460 unscale_volume(cd_vol
, max
)
463 int vol
= 0, top
= max
, bot
= 0, scaled
;
467 vol
= (top
+ bot
) / 2;
468 scaled
= scale_volume(vol
, max
);
469 if (cd_vol
== scaled
)
486 * Read the initial volume from the drive, if available. Each channel
487 * ranges from 0 to 100, with -1 indicating data not available.
490 gen_get_volume(d
, left
, right
)
494 struct cd_playback pb
;
495 struct cd_playback_status ps
;
497 bzero((char *)&pb
, sizeof(pb
));
498 bzero((char *)&ps
, sizeof(ps
));
500 pb
.pb_alloc_length
= sizeof(ps
);
501 pb
.pb_buffer
= (caddr_t
)&ps
;
505 if (ioctl(d
->fd
, CDROM_PLAYBACK_STATUS
, &pb
))
509 *left
= unscale_volume(ps
.ps_chan0_volume
, 100);
510 *right
= unscale_volume(ps
.ps_chan1_volume
, 100);
520 * Send an arbitrary SCSI command to a device.
523 wm_scsi(d
, cdb
, cdblen
, retbuf
, retbuflen
, getreply
)
531 /* ULTRIX doesn't have a SCSI passthrough interface, does it? */
538 * Simulate fgets, but joining continued lines in the output of uerf.
541 #define BUF_SIZE 85 /* Max length of a (real) line */
547 static char *retval
= NULL
;
548 static char holdbuf
[BUF_SIZE
+ 1];
549 char tmp
[BUF_SIZE
+ 1];
553 retval
= malloc(BUF_SIZE
* 3); /* 3 lines can be joined */
561 strcpy(retval
, holdbuf
);
562 retval
[strlen(retval
)-1] = '\0';
563 memset(holdbuf
, 0, BUF_SIZE
+1);
565 while (fgets(tmp
, BUF_SIZE
, fp
)) {
566 stmp
= tmp
+ strspn(tmp
, " \t");
567 if (*stmp
== '_') { /* Continuation line */
568 retval
[strlen(retval
)-1] = '\0'; /* Trim off C/R */
569 strcat(retval
, stmp
+1);
572 strcpy(holdbuf
, tmp
);
573 holdbuf
[strlen(holdbuf
)-1] = -1;
575 } else { /* First line read, keep reading */
576 strcat(retval
, stmp
);
577 retval
[strlen(retval
)-1] = '\0';
585 * Open the CD device and figure out what kind of drive is attached.
592 static int warned
= 0;
594 if (d
->fd
>= 0) /* Device already open? */
597 if (cd_device
== NULL
)
600 d
->fd
= open(cd_device
, 0);
608 "As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device
,
609 "to give yourself permission to access the CD-ROM device.");
613 else if (errno
!= EINTR
)
619 /* No CD in drive. */
626 fprintf(stderr
, "Thank you.\n");
629 /* Now fill in the relevant parts of the wm_drive structure. */
631 *d
= *(find_drive_struct("", "", ""));
640 * Re-Open the device if it is open.
643 wmcd_reopen( struct wm_drive
*d
)
648 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "wmcd_reopen ");
649 if (d
->fd
>= 0) /* Device really open? */
651 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "closes the device and ");
652 status
= close( d
->fd
); /* close it! */
653 /* we know, that the file is closed, do we? */
657 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "calls wmcd_open()\n");
658 status
= wmcd_open( d
); /* open it as usual */
660 } while ( status
!= 0 );
662 } /* wmcd_reopen() */