2 * This file is part of WorkMan, the civilized CD player library
3 * (c) 1991-1997 by Steven Grimm (original author)
4 * (c) by Dirk Försterling (current 'author' = maintainer)
5 * The maintainer can be contacted by his e-mail address:
6 * milliByte@DeathsDoor.com
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * OSF drive control routines.
28 static char plat_osf_id
[] = "$Id: plat_osf1.c,v 1.6 1999/03/07 08:36:40 dirk Exp $";
32 #include <sys/types.h>
34 #include <sys/param.h>
39 /* #include <sys/rzdisk.h>
40 #include <sys/cdrom.h> */
41 #include <io/cam/rzdisk.h>
42 #include <io/cam/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.
78 char *data
, *fgetline();
82 extern char *getenv();
86 cd_device
= getenv("CDROM");
87 if (cd_device
!= 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
;
281 * scale_volume(vol, max)
283 * Return a volume value suitable for passing to the CD-ROM drive. "vol"
284 * is a volume slider setting; "max" is the slider's maximum value.
286 * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
287 * increases much faster toward the top end of the volume scale than it
288 * does at the bottom. To make up for this, we make the volume scale look
289 * sort of logarithmic (actually an upside-down inverse square curve) so
290 * that the volume value passed to the drive changes less and less as you
291 * approach the maximum slider setting. The actual formula looks like
293 * (max^2 - (max - vol)^2) * (max_volume - min_volume)
294 * v = --------------------------------------------------- + min_volume
297 * If your system's volume settings aren't broken in this way, something
298 * like the following should work:
300 * return ((vol * (max_volume - min_volume)) / max + min_volume);
302 scale_volume(vol
, max
)
305 return ((max
* max
- (max
- vol
) * (max
- vol
)) *
306 (max_volume
- min_volume
) / (max
* max
) + min_volume
);
310 * Set the volume level for the left and right channels. Their values
311 * range from 0 to 100.
314 gen_set_volume(d
, left
, right
)
318 struct cd_playback pb
;
319 struct cd_playback_status ps
;
320 struct cd_playback_control pc
;
322 left
= scale_volume(left
, 100);
323 right
= scale_volume(right
, 100);
325 bzero((char *)&pb
, sizeof(pb
));
326 bzero((char *)&ps
, sizeof(ps
));
327 bzero((char *)&pc
, sizeof(pc
));
329 pb
.pb_alloc_length
= sizeof(ps
);
330 pb
.pb_buffer
= (caddr_t
)&ps
;
332 if (ioctl(d
->fd
, CDROM_PLAYBACK_STATUS
, &pb
))
335 pc
.pc_chan0_select
= ps
.ps_chan0_select
;
336 pc
.pc_chan0_volume
= (left
< CDROM_MIN_VOLUME
) ?
337 CDROM_MIN_VOLUME
: (left
> CDROM_MAX_VOLUME
) ?
338 CDROM_MAX_VOLUME
: left
;
339 pc
.pc_chan1_select
= ps
.ps_chan1_select
;
340 pc
.pc_chan1_volume
= (right
< CDROM_MIN_VOLUME
) ?
341 CDROM_MIN_VOLUME
: (right
> CDROM_MAX_VOLUME
) ?
342 CDROM_MAX_VOLUME
: right
;
344 pb
.pb_alloc_length
= sizeof(pc
);
345 pb
.pb_buffer
= (caddr_t
)&pc
;
347 if (ioctl(d
->fd
, CDROM_PLAYBACK_CONTROL
, &pb
))
360 return (ioctl(d
->fd
, CDROM_PAUSE_PLAY
, 0));
364 * Resume playing the CD (assuming it was paused.)
370 return (ioctl(d
->fd
, CDROM_RESUME_PLAY
, 0));
380 return (ioctl(d
->fd
, SCSI_STOP_UNIT
, 0));
384 * Play the CD from one position to another (both in frames.)
387 gen_play(d
, start
, end
)
391 struct cd_play_audio_msf msf
;
393 msf
.msf_starting_M_unit
= start
/ (60*75);
394 msf
.msf_starting_S_unit
= (start
% (60*75)) / 75;
395 msf
.msf_starting_F_unit
= start
% 75;
396 msf
.msf_ending_M_unit
= end
/ (60*75);
397 msf
.msf_ending_S_unit
= (end
% (60*75)) / 75;
398 msf
.msf_ending_F_unit
= end
% 75;
400 if (ioctl(d
->fd
, SCSI_START_UNIT
))
402 if (ioctl(d
->fd
, CDROM_PLAY_AUDIO_MSF
, &msf
))
409 * Eject the current CD, if there is one.
412 gen_eject(struct wm_drive
*d
)
414 /* On some systems, we can check to see if the CD is mounted. */
418 if (fstat(d
->fd
, &stbuf
) != 0)
421 /* Is this a mounted filesystem? */
422 if (ustat(stbuf
.st_rdev
, &ust
) == 0)
425 return (ioctl(d
->fd
, CDROM_EJECT_CADDY
, 0));
428 /*----------------------------------------*
431 * Please edit and send changes to
432 * milliByte@DeathsDoor.com
433 *----------------------------------------*/
435 int gen_closetray(struct wm_drive
*d
)
441 return(wmcd_reopen(d
));
446 /* Always succeed if the drive can't close */
448 #endif /* CAN_CLOSE */
449 } /* gen_closetray() */
453 * unscale_volume(cd_vol, max)
455 * Given a value between min_volume and max_volume, return the volume slider
456 * value needed to achieve that value.
458 * Rather than perform floating-point calculations to reverse the above
459 * formula, we simply do a binary search of scale_volume()'s return values.
462 unscale_volume(cd_vol
, max
)
465 int vol
= 0, top
= max
, bot
= 0, scaled
;
469 vol
= (top
+ bot
) / 2;
470 scaled
= scale_volume(vol
, max
);
471 if (cd_vol
== scaled
)
488 * Read the initial volume from the drive, if available. Each channel
489 * ranges from 0 to 100, with -1 indicating data not available.
492 gen_get_volume(d
, left
, right
)
496 struct cd_playback pb
;
497 struct cd_playback_status ps
;
499 bzero((char *)&pb
, sizeof(pb
));
500 bzero((char *)&ps
, sizeof(ps
));
502 pb
.pb_alloc_length
= sizeof(ps
);
503 pb
.pb_buffer
= (caddr_t
)&ps
;
507 if (ioctl(d
->fd
, CDROM_PLAYBACK_STATUS
, &pb
))
511 *left
= unscale_volume(ps
.ps_chan0_volume
, 100);
512 *right
= unscale_volume(ps
.ps_chan1_volume
, 100);
522 * Send an arbitrary SCSI command to a device.
525 wm_scsi(d
, cdb
, cdblen
, retbuf
, retbuflen
, getreply
)
533 /* ULTRIX doesn't have a SCSI passthrough interface, does it? */
540 * Simulate fgets, but joining continued lines in the output of uerf.
543 #define BUF_SIZE 85 /* Max length of a (real) line */
549 static char *retval
= NULL
;
550 static char holdbuf
[BUF_SIZE
+ 1];
551 char tmp
[BUF_SIZE
+ 1];
555 retval
= malloc(BUF_SIZE
* 3); /* 3 lines can be joined */
563 strcpy(retval
, holdbuf
);
564 retval
[strlen(retval
)-1] = '\0';
565 memset(holdbuf
, 0, BUF_SIZE
+1);
567 while (fgets(tmp
, BUF_SIZE
, fp
)) {
568 stmp
= tmp
+ strspn(tmp
, " \t");
569 if (*stmp
== '_') { /* Continuation line */
570 retval
[strlen(retval
)-1] = '\0'; /* Trim off C/R */
571 strcat(retval
, stmp
+1);
574 strcpy(holdbuf
, tmp
);
575 holdbuf
[strlen(holdbuf
)-1] = -1;
577 } else { /* First line read, keep reading */
578 strcat(retval
, stmp
);
579 retval
[strlen(retval
)-1] = '\0';
587 * Open the CD device and figure out what kind of drive is attached.
594 static int warned
= 0;
596 if (d
->fd
>= 0) /* Device already open? */
599 if (cd_device
== NULL
)
602 d
->fd
= open(cd_device
, O_RDWR
);
610 "As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device
,
611 "to give yourself permission to access the CD-ROM device.");
615 else if (errno
!= EINTR
)
621 /* No CD in drive. */
628 fprintf(stderr
, "Thank you.\n");
631 /* Now fill in the relevant parts of the wm_drive structure. */
633 *d
= *(find_drive_struct("", "", ""));
642 * Re-Open the device if it is open.
645 wmcd_reopen( struct wm_drive
*d
)
650 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "wmcd_reopen ");
651 if (d
->fd
>= 0) /* Device really open? */
653 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "closes the device and ");
654 status
= close( d
->fd
); /* close it! */
655 /* we know, that the file is closed, do we? */
659 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "calls wmcd_open()\n");
660 status
= wmcd_open( d
); /* open it as usual */
662 } while ( status
!= 0 );
664 } /* wmcd_reopen() */