2 * $Id: plat_openbsd.c,v 1.7 1999/03/07 08:36:40 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 * OpenBSD-specific drive control routines. (Based on plat_freebsd.c)
27 * Michael Shalayeff, 7/24/96
32 #if defined(__OpenBSD__)
34 static char plat_openbsd
[] = "$Id: plat_openbsd.c,v 1.7 1999/03/07 08:36:40 dirk Exp $";
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/mount.h>
42 #include <sys/param.h>
45 #include "include/wm_config.h"
47 /* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */
49 #include <sys/ustat.h>
54 #include <sys/ioctl.h>
56 #include <sys/scsiio.h>
57 #include <scsi/scsi_all.h>
58 #include <scsi/scsi_cd.h>
60 #include "include/wm_struct.h"
62 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
69 extern char *cd_device
;
73 * Initialize the drive. A no-op for the generic driver.
76 gen_init(struct wm_drive
*d
)
82 * Get the number of tracks on the CD.
85 gen_get_trackcount(struct wm_drive
*d
, int tracks
)
87 struct ioc_toc_header hdr
;
89 if (ioctl(d
->fd
, CDIOREADTOCHEADER
, &hdr
) == -1)
92 *tracks
= hdr
.ending_track
- hdr
.starting_track
+ 1;
98 * Get the start time and mode (data or audio) of a track.
100 * XXX - this should get cached, but that means keeping track of ejects.
103 gen_get_trackinfo(struct wm_drive
*d
, int track
, int *data
, int *startframe
)
105 struct ioc_read_toc_entry toc
;
106 struct cd_toc_entry toc_buffer
;
108 bzero((char *)&toc_buffer
, sizeof(toc_buffer
));
109 toc
.address_format
= CD_MSF_FORMAT
;
110 toc
.starting_track
= track
;
111 toc
.data_len
= sizeof(toc_buffer
);
112 toc
.data
= &toc_buffer
;
114 if (ioctl(d
->fd
, CDIOREADTOCENTRYS
, &toc
))
117 *data
= ((toc_buffer
.control
& 0x4) != 0);
119 *startframe
= toc_buffer
.addr
.msf
.minute
*60*75 +
120 toc_buffer
.addr
.msf
.second
* 75 +
121 toc_buffer
.addr
.msf
.frame
;
127 * Get the number of frames on the CD.
130 gen_get_cdlen(struct wm_drive
*d
, int *frames
)
133 struct ioc_toc_header hdr
;
136 #define LEADOUT 0xaa /* see scsi.c. what a hack! */
137 return gen_get_trackinfo(d
, LEADOUT
, &tmp
, frames
);
141 * Get the current status of the drive: the current play mode, the absolute
142 * position from start of disc (in frames), and the current track and index
143 * numbers if the CD is playing or paused.
146 gen_get_drive_status(struct wm_drive
*d
, enum wm_cd_modes oldmode
,
147 enum wm_cd_modes
*mode
, int *pos
, int *track
, int *index
)
149 struct ioc_read_subchannel sc
;
150 struct cd_sub_channel_info scd
;
152 /* If we can't get status, the CD is ejected, so default to that. */
153 *mode
= WM_CDM_EJECTED
;
155 sc
.address_format
= CD_MSF_FORMAT
;
156 sc
.data_format
= CD_CURRENT_POSITION
;
158 sc
.data_len
= sizeof(scd
);
159 sc
.data
= (struct cd_sub_channel_info
*)&scd
;
161 /* Is the device open? */
164 switch (wmcd_open(d
)) {
173 if (ioctl(d
->fd
, CDIOCREADSUBCHANNEL
, &sc
)) {
174 /* we need to release the device so the kernel will notice
178 return (0); /* ejected */
181 switch (scd
.header
.audio_status
) {
182 case CD_AS_PLAY_IN_PROGRESS
:
183 *mode
= WM_CDM_PLAYING
;
185 *pos
= scd
.what
.position
.absaddr
.msf
.minute
* 60 * 75 +
186 scd
.what
.position
.absaddr
.msf
.second
* 75 +
187 scd
.what
.position
.absaddr
.msf
.frame
;
188 *track
= scd
.what
.position
.track_number
;
189 *index
= scd
.what
.position
.index_number
;
192 case CD_AS_PLAY_PAUSED
:
193 if (oldmode
== WM_CDM_PLAYING
|| oldmode
== WM_CDM_PAUSED
)
195 *mode
= WM_CDM_PAUSED
;
199 *mode
= WM_CDM_STOPPED
;
202 case CD_AS_PLAY_COMPLETED
:
203 *mode
= WM_CDM_TRACK_DONE
; /* waiting for next track. */
206 case CD_AS_NO_STATUS
:
208 *mode
= WM_CDM_STOPPED
;
216 * scale_volume(vol, max)
218 * Return a volume value suitable for passing to the CD-ROM drive. "vol"
219 * is a volume slider setting; "max" is the slider's maximum value.
221 * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
222 * increases much faster toward the top end of the volume scale than it
223 * does at the bottom. To make up for this, we make the volume scale look
224 * sort of logarithmic (actually an upside-down inverse square curve) so
225 * that the volume value passed to the drive changes less and less as you
226 * approach the maximum slider setting. The actual formula looks like
228 * (max^2 - (max - vol)^2) * (max_volume - min_volume)
229 * v = --------------------------------------------------- + min_volume
232 * If your system's volume settings aren't broken in this way, something
233 * like the following should work:
235 * return ((vol * (max_volume - min_volume)) / max + min_volume);
238 scale_volume(int vol
, int max
)
240 return ((vol
* (max_volume
- min_volume
)) / max
+ min_volume
);
244 * Set the volume level for the left and right channels. Their values
245 * range from 0 to 100.
248 gen_set_volume(struct wm_drive
*d
, int left
, int right
)
252 if (left
< 0) /* don't laugh, I saw this happen once! */
256 left
= scale_volume(left
, 100);
257 right
= scale_volume(right
, 100);
259 bzero((char *)&vol
, sizeof(vol
));
261 vol
.vol
[LEFT_PORT
] = left
;
262 vol
.vol
[RIGHT_PORT
] = right
;
264 if (ioctl(d
->fd
, CDIOCSETVOL
, &vol
))
274 gen_pause(struct wm_drive
*d
)
276 return (ioctl(d
->fd
, CDIOCPAUSE
));
280 * Resume playing the CD (assuming it was paused.)
283 gen_resume(struct wm_drive
*d
)
285 return (ioctl(d
->fd
, CDIOCRESUME
));
292 gen_stop(struct wm_drive
*d
)
294 return (ioctl(d
->fd
, CDIOCSTOP
));
298 * Play the CD from one position to another (both in frames.)
301 gen_play(struct wm_drive
*d
, int start
, int end
)
303 struct ioc_play_msf msf
;
305 msf
.start_m
= start
/ (60*75);
306 msf
.start_s
= (start
% (60*75)) / 75;
307 msf
.start_f
= start
% 75;
308 msf
.end_m
= end
/ (60*75);
309 msf
.end_s
= (end
% (60*75)) / 75;
310 msf
.end_f
= end
% 75;
312 if (ioctl(d
->fd
, CDIOCSTART
))
315 if (ioctl(d
->fd
, CDIOCPLAYMSF
, &msf
))
322 * Eject the current CD, if there is one.
325 gen_eject(struct wm_drive
*d
)
327 /* On some systems, we can check to see if the CD is mounted. */
332 if (fstat(d
->fd
, &stbuf
) != 0)
335 /* Is this a mounted filesystem? */
336 if (fstatfs(stbuf
.st_rdev
, &buf
) == 0)
339 rval
= ioctl(d
->fd
, CDIOCALLOW
);
341 rval
= ioctl(d
->fd
, CDIOCEJECT
);
343 rval
= ioctl(d
->fd
, CDIOCPREVENT
);
352 /*----------------------------------------*
355 * Please edit and send changes to
356 * milliByte@DeathsDoor.com
357 *----------------------------------------*/
359 int gen_closetray(struct wm_drive
*d
)
365 return(wmcd_reopen(d
));
370 /* Always succeed if the drive can't close */
372 #endif /* CAN_CLOSE */
373 } /* gen_closetray() */
377 * unscale_volume(cd_vol, max)
379 * Given a value between min_volume and max_volume, return the volume slider
380 * value needed to achieve that value.
382 * Rather than perform floating-point calculations to reverse the above
383 * formula, we simply do a binary search of scale_volume()'s return values.
386 unscale_volume(int cd_vol
, int max
)
388 int vol
= 0, top
= max
, bot
= 0, scaled
;
392 vol
= (top
+ bot
) / 2;
393 scaled
= scale_volume(vol
, max
);
394 if (cd_vol
== scaled
)
411 * Read the initial volume from the drive, if available. Each channel
412 * ranges from 0 to 100, with -1 indicating data not available.
415 gen_get_volume(struct wm_drive
*d
, int *left
, int *right
)
421 bzero((char *)&vol
, sizeof(vol
));
423 if (ioctl(d
->fd
, CDIOCGETVOL
, &vol
))
427 *left
= unscale_volume(vol
.vol
[LEFT_PORT
], 100);
428 *right
= unscale_volume(vol
.vol
[RIGHT_PORT
], 100);
439 * Send an arbitrary SCSI command to a device.
443 wm_scsi(struct wm_drive
*d
, unsigned char *cdb
,
444 int cdblen
, void *retbuf
, int retbuflen
, int getreply
)
451 * Open the CD device and figure out what kind of drive is attached.
454 wmcd_open(struct wm_drive
*d
)
457 static int warned
= 0;
458 char vendor
[32] = WM_STR_GENVENDOR
;
459 char model
[32] = WM_STR_GENMODEL
;
460 char rev
[32] = WM_STR_GENREV
;
462 if (d
->fd
>= 0) /* Device already open? */
465 if (cd_device
== NULL
)
466 cd_device
= DEFAULT_CD_DEVICE
;
468 d
->fd
= open(cd_device
, 0);
476 "As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device
,
477 "to give yourself permission to access the CD-ROM device.");
482 /* No CD in drive. */
489 fprintf(stderr
, "Thank you.\n");
492 /* Now fill in the relevant parts of the wm_drive structure. */
495 *d
= *(find_drive_struct(vendor
, model
, rev
));
496 wm_drive_settype(vendor
, model
, rev
);
506 * Re-Open the device if it is open.
509 wmcd_reopen( struct wm_drive
*d
)
514 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "wmcd_reopen ");
515 if (d
->fd
>= 0) /* Device really open? */
517 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "closes the device and ");
518 status
= close( d
->fd
); /* close it! */
519 /* we know, that the file is closed, do we? */
523 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "calls wmcd_open()\n");
524 status
= wmcd_open( d
); /* open it as usual */
526 } while ( status
!= 0 );
528 } /* wmcd_reopen() */