2 * $Id: plat_freebsd.c,v 1.8 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
27 * FreeBSD-specific drive control routines.
33 #if defined(__FreeBSD__) || defined(__NetBSD__)
35 static char freebsd_id
[] = "$Id: plat_freebsd.c,v 1.8 1999/03/07 08:36:40 dirk Exp $";
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/mount.h>
47 #include "include/wm_config.h"
50 #include <sys/ioctl.h>
52 #include <sys/scsiio.h>
54 #if defined(__NetBSD__)
55 # define MSF_MINUTES 1
56 # define MSF_SECONDS 2
58 # include "/sys/scsi/scsi_all.h"
59 # include "/sys/scsi/scsi_cd.h"
63 # if __FreeBSD_version < 300000
68 #include "include/wm_struct.h"
69 #include "include/wm_platform.h"
70 #include "include/wm_cdrom.h"
71 #include "include/wm_scsi.h"
72 #include "include/wm_helpers.h"
74 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
81 extern char *cd_device
;
84 /*--------------------------------------------------------*
85 * Initialize the drive. A no-op for the generic driver.
86 *--------------------------------------------------------*/
88 gen_init(struct wm_drive
*d
)
93 /*-------------------------------------*
94 * Get the number of tracks on the CD.
95 *-------------------------------------*/
97 gen_get_trackcount(struct wm_drive
*d
, int *tracks
)
99 struct ioc_toc_header hdr
;
101 if (ioctl(d
->fd
, CDIOREADTOCHEADER
, &hdr
) == -1)
104 *tracks
= hdr
.ending_track
- hdr
.starting_track
+ 1;
109 /*-----------------------------------------------------------------------*
110 * Get the start time and mode (data or audio) of a track.
112 * XXX - this should get cached, but that means keeping track of ejects.
113 *-----------------------------------------------------------------------*/
115 gen_get_trackinfo(struct wm_drive
*d
, int track
, int *data
, int *startframe
)
117 struct ioc_read_toc_entry toc
;
118 struct cd_toc_entry toc_buffer
;
120 bzero((char *)&toc_buffer
, sizeof(toc_buffer
));
121 toc
.address_format
= CD_MSF_FORMAT
;
122 toc
.starting_track
= track
;
123 toc
.data_len
= sizeof(toc_buffer
);
124 toc
.data
= &toc_buffer
;
126 if (ioctl(d
->fd
, CDIOREADTOCENTRYS
, &toc
))
129 *data
= ((toc_buffer
.control
& 0x4) != 0);
132 *startframe
= toc_buffer
.addr
[MSF_MINUTES
]*60*75 +
133 toc_buffer
.addr
[MSF_SECONDS
] * 75 +
134 toc_buffer
.addr
[MSF_FRAMES
];
136 *startframe
= toc_buffer
.addr
.msf
.minute
*60*75 +
137 toc_buffer
.addr
.msf
.second
* 75 +
138 toc_buffer
.addr
.msf
.frame
;
144 /*-------------------------------------*
145 * Get the number of frames on the CD.
146 *-------------------------------------*/
148 gen_get_cdlen(struct wm_drive
*d
, int *frames
)
151 struct ioc_toc_header hdr
;
154 #define LEADOUT 0xaa /* see scsi.c. what a hack! */
155 return gen_get_trackinfo(d
, LEADOUT
, &tmp
, frames
);
158 /*--------------------------------------------------------------------------*
159 * Get the current status of the drive: the current play mode, the absolute
160 * position from start of disc (in frames), and the current track and index
161 * numbers if the CD is playing or paused.
162 *--------------------------------------------------------------------------*/
164 gen_get_drive_status(struct wm_drive
*d
, enum wm_cd_modes oldmode
,
165 enum wm_cd_modes
*mode
, int *pos
, int *track
, int *index
)
167 struct ioc_read_subchannel sc
;
168 struct cd_sub_channel_info scd
;
170 /* If we can't get status, the CD is ejected, so default to that. */
171 *mode
= WM_CDM_EJECTED
;
173 sc
.address_format
= CD_MSF_FORMAT
;
174 sc
.data_format
= CD_CURRENT_POSITION
;
176 sc
.data_len
= sizeof(scd
);
177 sc
.data
= (struct cd_sub_channel_info
*)&scd
;
179 /* Is the device open? */
182 switch (wmcd_open(d
)) {
191 if (ioctl(d
->fd
, CDIOCREADSUBCHANNEL
, &sc
)) {
195 * Denis Bourez <denis@rsn.fdn.fr> told me, that closing the
196 * device is mandatory for FreeBSD, too.
198 /* we need to release the device so the kernel will notice
205 return (0); /* ejected */
208 switch (scd
.header
.audio_status
) {
209 case CD_AS_PLAY_IN_PROGRESS
:
210 *mode
= WM_CDM_PLAYING
;
213 *pos
= scd
.what
.position
.absaddr
[MSF_MINUTES
] * 60 * 75 +
214 scd
.what
.position
.absaddr
[MSF_SECONDS
] * 75 +
215 scd
.what
.position
.absaddr
[MSF_FRAMES
];
217 *pos
= scd
.what
.position
.absaddr
.msf
.minute
* 60 * 75 +
218 scd
.what
.position
.absaddr
.msf
.second
* 75 +
219 scd
.what
.position
.absaddr
.msf
.frame
;
221 *track
= scd
.what
.position
.track_number
;
222 *index
= scd
.what
.position
.index_number
;
225 case CD_AS_PLAY_PAUSED
:
226 if (oldmode
== WM_CDM_PLAYING
|| oldmode
== WM_CDM_PAUSED
)
228 *mode
= WM_CDM_PAUSED
;
232 *mode
= WM_CDM_STOPPED
;
235 case CD_AS_PLAY_COMPLETED
:
236 *mode
= WM_CDM_TRACK_DONE
; /* waiting for next track. */
239 case CD_AS_NO_STATUS
:
241 *mode
= WM_CDM_STOPPED
;
248 /*---------------------------------------------------------------------------*
249 * scale_volume(vol, max)
251 * Return a volume value suitable for passing to the CD-ROM drive. "vol"
252 * is a volume slider setting; "max" is the slider's maximum value.
254 * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
255 * increases much faster toward the top end of the volume scale than it
256 * does at the bottom. To make up for this, we make the volume scale look
257 * sort of logarithmic (actually an upside-down inverse square curve) so
258 * that the volume value passed to the drive changes less and less as you
259 * approach the maximum slider setting. The actual formula looks like
261 * (max^2 - (max - vol)^2) * (max_volume - min_volume)
262 * v = --------------------------------------------------- + min_volume
265 * If your system's volume settings aren't broken in this way, something
266 * like the following should work:
268 * return ((vol * (max_volume - min_volume)) / max + min_volume);
269 *---------------------------------------------------------------------------*/
271 scale_volume(int vol
, int max
)
273 return ((vol
* (max_volume
- min_volume
)) / max
+ min_volume
);
276 /*---------------------------------------------------------------------*
277 * Set the volume level for the left and right channels. Their values
278 * range from 0 to 100.
279 *---------------------------------------------------------------------*/
281 gen_set_volume(struct wm_drive
*d
, int left
, int right
)
285 if (left
< 0) /* don't laugh, I saw this happen once! */
289 left
= scale_volume(left
, 100);
290 right
= scale_volume(right
, 100);
292 bzero((char *)&vol
, sizeof(vol
));
294 vol
.vol
[LEFT_PORT
] = left
;
295 vol
.vol
[RIGHT_PORT
] = right
;
297 if (ioctl(d
->fd
, CDIOCSETVOL
, &vol
))
307 gen_pause( struct wm_drive
*d
)
309 return (ioctl(d
->fd
, CDIOCPAUSE
));
312 /*-------------------------------------------------*
313 * Resume playing the CD (assuming it was paused.)
314 *-------------------------------------------------*/
316 gen_resume( struct wm_drive
*d
)
318 return (ioctl(d
->fd
, CDIOCRESUME
));
325 gen_stop( struct wm_drive
*d
)
327 return (ioctl(d
->fd
, CDIOCSTOP
));
330 /*------------------------------------------------------------*
331 * Play the CD from one position to another (both in frames.)
332 *------------------------------------------------------------*/
334 gen_play(struct wm_drive
*d
, int start
, int end
)
336 struct ioc_play_msf msf
;
338 msf
.start_m
= start
/ (60*75);
339 msf
.start_s
= (start
% (60*75)) / 75;
340 msf
.start_f
= start
% 75;
341 msf
.end_m
= end
/ (60*75);
342 msf
.end_s
= (end
% (60*75)) / 75;
343 msf
.end_f
= end
% 75;
345 if (ioctl(d
->fd
, CDIOCSTART
))
348 if (ioctl(d
->fd
, CDIOCPLAYMSF
, &msf
))
354 /*----------------------------------------*
355 * Eject the current CD, if there is one.
356 *----------------------------------------*/
358 gen_eject( struct wm_drive
*d
)
360 /* On some systems, we can check to see if the CD is mounted. */
365 if (fstat(d
->fd
, &stbuf
) != 0)
368 /* Is this a mounted filesystem? */
369 if (fstatfs(stbuf
.st_rdev
, &buf
) == 0)
372 rval
= ioctl(d
->fd
, CDIOCALLOW
);
375 rval
= ioctl(d
->fd
, CDIOCEJECT
);
378 rval
= ioctl(d
->fd
, CDIOCPREVENT
);
385 /*----------------------------------------*
387 *----------------------------------------*/
389 int gen_closetray(struct wm_drive
*d
)
395 return(wmcd_reopen(d
));
400 /* Always succeed if the drive can't close */
402 #endif /* CAN_CLOSE */
403 } /* gen_closetray() */
406 /*---------------------------------------------------------------------------*
407 * unscale_volume(cd_vol, max)
409 * Given a value between min_volume and max_volume, return the volume slider
410 * value needed to achieve that value.
412 * Rather than perform floating-point calculations to reverse the above
413 * formula, we simply do a binary search of scale_volume()'s return values.
414 *--------------------------------------------------------------------------*/
416 unscale_volume( int cd_vol
, int max
)
418 int vol
= 0, top
= max
, bot
= 0, scaled
;
422 vol
= (top
+ bot
) / 2;
423 scaled
= scale_volume(vol
, max
);
424 if (cd_vol
== scaled
)
440 /*---------------------------------------------------------------------*
441 * Read the initial volume from the drive, if available. Each channel
442 * ranges from 0 to 100, with -1 indicating data not available.
443 *---------------------------------------------------------------------*/
445 gen_get_volume( struct wm_drive
*d
, int *left
, int *right
)
451 bzero((char *)&vol
, sizeof(vol
));
453 if (ioctl(d
->fd
, CDIOCGETVOL
, &vol
))
457 *left
= unscale_volume(vol
.vol
[LEFT_PORT
], 100);
458 *right
= unscale_volume(vol
.vol
[RIGHT_PORT
], 100);
468 /*---------------------------------------------*
469 * Send an arbitrary SCSI command to a device.
471 *---------------------------------------------*/
473 wm_scsi(d
, cdb
, cdblen
, retbuf
, retbuflen
, getreply
)
485 /*-------------------------------------------------------------------*
486 * Open the CD device and figure out what kind of drive is attached.
487 *-------------------------------------------------------------------*/
489 wmcd_open( struct wm_drive
*d
)
492 static int warned
= 0;
493 char vendor
[32] = WM_STR_GENVENDOR
;
494 char model
[32] = WM_STR_GENMODEL
;
495 char rev
[32] = WM_STR_GENREV
;
497 if (d
->fd
>= 0) /* Device already open? */
500 if (cd_device
== NULL
)
501 cd_device
= DEFAULT_CD_DEVICE
;
503 d
->fd
= open(cd_device
, 0);
511 "As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device
,
512 "to give yourself permission to access the CD-ROM device.");
517 /* No CD in drive. */
524 fprintf(stderr
, "Thank you.\n");
527 /* Now fill in the relevant parts of the wm_drive structure. */
530 *d
= *(find_drive_struct(vendor
, model
, rev
));
531 wm_drive_settype(vendor
, model
, rev
);
541 * Re-Open the device if it is open.
544 wmcd_reopen( struct wm_drive
*d
)
549 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "wmcd_reopen ");
550 if (d
->fd
>= 0) /* Device really open? */
552 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "closes the device and ");
553 status
= close( d
->fd
); /* close it! */
554 /* we know, that the file is closed, do we? */
558 wm_lib_message(WM_MSG_LEVEL_DEBUG
|WM_MSG_CLASS
, "calls wmcd_open()\n");
559 status
= wmcd_open( d
); /* open it as usual */
561 } while ( status
!= 0 );
563 } /* wmcd_reopen() */