1 /*
2  * $Id: plat_bsd386.c,v 1.5 1999/03/07 08:36:40 dirk Exp $
3  *
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
9  *
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.
14  *
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.
19  *
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
23  *
24  *
25  * BSD/386-specific drive control routines.
26  */
27 
28 #ifdef __bsdi__
29 
30 static char plat_bsd386_id[] = "$Id: plat_bsd386.c,v 1.5 1999/03/07 08:36:40 dirk Exp $";
31 
32 #include <errno.h>
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <sys/param.h>
38 #include <sys/stat.h>
39 
40 #include "include/wm_config.h"
41 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
42 
43 /*
44  * The following is included from the Linux module. However, I didn't
45  * see a check here if the CD to be ejected is mounted...
46  */
47 #if defined(BSD_MOUNTTEST)
48   #include <mntent.h>
49 #else
50   /*
51    * this is for glibc 2.x which defines ust structure in
52    * ustat.h not stat.h.
53    */
54   #ifdef __GLIBC__
55     #include <sys/ustat.h>
56   #endif
57 #endif
58 
59 
60 #include <sys/time.h>
61 #include <string.h>
62 #include <sys/cdrom.h>
63 #ifdef SOUNDBLASTER
64 # include <i386/isa/sblast.h>
65 #endif
66 
67 #include "include/wm_struct.h"
68 
69 /*
70  * Since we can't sense the drive type with libcdrom anyway, and since the
71  * library doesn't provide "pause" or "resume" functions, use the daux field
72  * to point to the frame number at which we paused.
73  */
74 struct pause_info {
75 	int	frame;
76 	int	endframe;
77 };
78 #define	PAUSE_FRAME	(((struct pause_info *) d->daux)->frame)
79 #define	END_FRAME	(((struct pause_info *) d->daux)->endframe)
80 #define CUR_CD		((struct cdinfo *) d->aux)
81 
82 void *malloc();
83 
84 extern char	*cd_device;
85 
86 #ifdef SOUNDBLASTER
87 	int	min_volume = 0;
88 	int	max_volume = 15;
89 	int	min_volume_drive = 10;	/* Toshiba drive does low values. */
90 	int	max_volume_drive = 255;
91 #else /* not SOUNDBLASTER, libcdrom only */
92 	int	min_volume = 10;
93 	int	max_volume = 255;
94 #endif
95 
96 /*--------------------------------------------------------*
97  * Initialize the drive.  A no-op for the generic driver.
98  *--------------------------------------------------------*/
99 int
gen_init(struct wm_drive * d)100 gen_init(struct wm_drive *d)
101 {
102 	return (0);
103 }
104 
105 /*-------------------------------------*
106  * Get the number of tracks on the CD.
107  *-------------------------------------*/
108 int
gen_get_trackcount(struct wm_drive * d,int * tracks)109 gen_get_trackcount(struct wm_drive *d, int *tracks)
110 {
111 	*tracks = CUR_CD->ntracks;
112 
113 	return (0);
114 }
115 
116 /*---------------------------------------------------------*
117  * Get the start time and mode (data or audio) of a track.
118  *---------------------------------------------------------*/
119 int
gen_get_trackinfo(struct wm_drive * d,int track,int * data,int * startframe)120 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
121 {
122 	*data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0;
123 	*startframe = CUR_CD->tracks[track - 1].start_frame;
124 
125 	return (0);
126 }
127 
128 /*-------------------------------------*
129  * Get the number of frames on the CD.
130  *-------------------------------------*/
131 int
gen_get_cdlen(struct wm_drive * d,int * frames)132 gen_get_cdlen(struct wm_drive *d, int *frames)
133 {
134 	*frames = CUR_CD->total_frames;
135 
136 	return (0);
137 }
138 
139 /*--------------------------------------------------------------------------*
140  * Get the current status of the drive: the current play mode, the absolute
141  * position from start of disc (in frames), and the current track and index
142  * numbers if the CD is playing or paused.
143  *--------------------------------------------------------------------------*/
144 int
gen_get_drive_status(struct wm_drive * d,enum wm_cd_modes oldmode,enum wm_cd_modes * mode,int * pos,int * track,int * index)145 gen_get_drive_status(struct wm_drive *d, enum wm_cd_modes oldmode,
146                      enum wm_cd_modes *mode, int *pos, int *track, int *index)
147 {
148 	struct cdstatus	status;
149 	extern enum wm_cd_modes cur_cdmode;
150 
151 	/* If we can't get status, the CD is ejected, so default to that. */
152 	*mode = WM_CDM_EJECTED;
153 
154 	/* Is the device open? */
155 	if (d->aux == NULL)
156 	{
157 		switch (wmcd_open(d)) {
158 		case -1:	/* error */
159 			return (-1);
160 
161 		case 1:		/* retry */
162 			return (0);
163 		}
164 	}
165 
166 	if (cdstatus (CUR_CD, &status) < 0)
167 	{
168 		*mode = WM_CDM_TRACK_DONE;	/* waiting for next track. */
169 		return (0);
170 	}
171 
172 #define DOPOS \
173 		*pos = status.abs_frame; \
174 		*track = status.track_num; \
175 		*index = status.index_num
176 
177 	switch (status.state) {
178 	case cdstate_playing:
179 		*mode = WM_CDM_PLAYING;
180 		DOPOS;
181 		break;
182 
183 	case cdstate_stopped:
184 		/* the MITSUMI drive doesn't have a "paused" state,
185 		   so it always comes here and not to the paused section.
186 		   The PAUSE_FRAME stuff below (in gen_pause())
187 		   fakes out the paused state. */
188 		if (oldmode == WM_CDM_PLAYING) {
189 		    *mode = WM_CDM_TRACK_DONE;
190 		    break;
191 		} else if (cur_cdmode != WM_CDM_PAUSED) {
192 		    *mode = WM_CDM_STOPPED;
193 		    DOPOS;
194 		    break;
195 		}
196 		/* fall through if paused */
197 
198 	case cdstate_paused:
199 		/* the SCSI2 code in the cdrom library only pauses with
200 		   cdstop(); it never truly stops a disc (until an in-progress
201 		   play reaches the end).  So it always comes here. */
202 		if (cur_cdmode == WM_CDM_STOPPED) {
203 		    *mode = WM_CDM_STOPPED;
204 		    DOPOS;
205 		    break;
206 		}
207 		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
208 			*mode = WM_CDM_PAUSED;
209 			DOPOS;
210 		} else {
211 		    *mode = WM_CDM_STOPPED;
212 		    DOPOS;
213 		}
214 		break;
215 
216 	default:
217 		*mode = WM_CDM_STOPPED;
218 	}
219 
220 	return (0);
221 }
222 
223 /*------------------------------------------------------------------------*
224  * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
225  * is a volume slider setting; "max" is the slider's maximum value.
226  *------------------------------------------------------------------------*/
227 static int
scale_volume(int vol,int max)228 scale_volume(int vol, int max)
229 {
230 	/* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */
231 	return ((vol * (max_volume - min_volume)) / max + min_volume);
232 }
233 
234 /*---------------------------------------------------------------------*
235  * Set the volume level for the left and right channels.  Their values
236  * range from 0 to 100.
237  *---------------------------------------------------------------------*/
238 int
gen_set_volume(struct wm_drive * d,int left,int right)239 gen_set_volume(struct wm_drive *d, int left, int right)
240 {
241 	left = scale_volume(left, 100);
242 	right = scale_volume(right, 100);
243 
244 #ifdef SOUNDBLASTER
245 	/* Send a Mixer IOCTL */
246 	if (d->fd >= 0) {
247 		struct sb_mixer_levels levels;
248 		if (ioctl(d->fd, MIXER_IOCTL_READ_LEVELS, &levels) == 0) {
249 			levels.cd.l = left < 0 ? 0 : left;
250 			levels.cd.r = right < 0 ? 0 : right;
251 			(void) ioctl(d->fd, MIXER_IOCTL_SET_LEVELS, &levels);
252 		} else
253 			perror("SoundBlaster mixer read failed");
254 	} else
255 #endif
256 	/* NOTE: the cdvolume2() call is an addition to the cdrom library.
257 	   Pick it up from the archives on bsdi.com */
258 	cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left,
259 		   right < 0 ? 0 : right > 255 ? 255 : right);
260 
261 	return (0);
262 }
263 
264 /*--------------------------------------------------------------------*
265  * Pause the CD.  This is a bit of a trick since there's no cdpause()
266  * function in the library.  We fake it by saving the frame number
267  * and stopping.
268  *--------------------------------------------------------------------*/
269 int
gen_pause(struct wm_drive * d)270 gen_pause(struct wm_drive *d)
271 {
272 	struct cdstatus	status;
273 
274 	if (cdstatus(d->aux, &status) < 0)
275 		return (-1);
276 	if (status.state != cdstate_playing)
277 		PAUSE_FRAME = CUR_CD->tracks[0].start_frame;
278 	else
279 		PAUSE_FRAME = status.abs_frame;
280 	if (cdstop(d->aux) < 0)
281 		return (-1);
282 
283 	return (0);
284 }
285 
286 /*-------------------------------------------------*
287  * Resume playing the CD (assuming it was paused.)
288  *-------------------------------------------------*/
289 int
gen_resume(struct wm_drive * d)290 gen_resume(struct wm_drive *d)
291 {
292 	int	status;
293 
294 	status = (d->play)(d, PAUSE_FRAME, END_FRAME);
295 	PAUSE_FRAME = 0;
296 	return (status);
297 }
298 
299 /*--------------*
300  * Stop the CD.
301  *--------------*/
302 int
gen_stop(struct wm_drive * d)303 gen_stop(struct wm_drive *d)
304 {
305     return cdstop(d->aux);
306 }
307 
308 /*------------------------------------------------------------*
309  * Play the CD from one position to another (both in frames.)
310  *------------------------------------------------------------*/
311 int
gen_play(struct wm_drive * d,int start,int end)312 gen_play(struct wm_drive *d, int start, int end)
313 {
314 	END_FRAME = end;
315 	if (cdplay(d->aux, start, end) < 0)
316 		return (-1);
317 	else
318 		return (0);
319 }
320 
321 /*----------------------------------------*
322  * Eject the current CD, if there is one.
323  *----------------------------------------*/
324 int
gen_eject(struct wm_drive * d)325 gen_eject(struct wm_drive *d)
326 {
327 	cdeject(d->aux);
328 	cdclose(d->aux);
329 	d->aux = NULL;
330 	free(d->daux);
331 	d->daux = NULL;
332 
333 	return (0);
334 }
335 
336 /*----------------------------------------*
337  * Close the CD tray
338  *----------------------------------------*/
gen_closetray(struct wm_drive * d)339 int gen_closetray(struct wm_drive *d)
340 {
341 #ifdef CAN_CLOSE
342 	if(!close(d->fd))
343 	{
344 		d->fd=-1;
345 		return(wmcd_open(d));
346 	} else {
347 		return(-1);
348 	}
349 #else
350 	/* Always succeed if the drive can't close */
351 	return(0);
352 #endif /* CAN_CLOSE */
353 } /* gen_closetray() */
354 
355 /*---------------------------------------------------------------------------*
356  * unscale_volume(cd_vol, max)
357  *
358  * Given a value between min_volume and max_volume, return the volume slider
359  * value needed to achieve that value.
360  *
361  * Rather than perform floating-point calculations to reverse the above
362  * formula, we simply do a binary search of scale_volume()'s return values.
363  *--------------------------------------------------------------------------*/
364 static int
unscale_volume(int cd_vol,int max)365 unscale_volume(int cd_vol, int max)
366 {
367 	int	vol = 0, top = max, bot = 0, scaled;
368 
369 	while (bot <= top)
370 	{
371 		vol = (top + bot) / 2;
372 		scaled = scale_volume(vol, max);
373 		if (cd_vol == scaled)
374 			break;
375 		if (cd_vol < scaled)
376 			top = vol - 1;
377 		else
378 			bot = vol + 1;
379 	}
380 
381 	if (vol < 0)
382 		vol = 0;
383 	else if (vol > max)
384 		vol = max;
385 
386 	return (vol);
387 }
388 
389 /*---------------------------------------------------------------------*
390  * Read the initial volume from the drive, if available.  Each channel
391  * ranges from 0 to 100, with -1 indicating data not available.
392  *---------------------------------------------------------------------*/
393 int
gen_get_volume(struct wm_drive * d,int * left,int * right)394 gen_get_volume(struct wm_drive *d, int *left, int *right)
395 {
396 	/* Most systems can't seem to do this... */
397 	*left = *right = -1;
398 
399 #ifdef SOUNDBLASTER
400 	/* Send a Mixer IOCTL */
401 	if (d->fd >= 0) {
402 	    struct sb_mixer_levels levels;
403 	    if (ioctl(d->fd, MIXER_IOCTL_READ_LEVELS, &levels) == 0) {
404 		*left = unscale_volume(levels.cd.l, 100);
405 		*right = unscale_volume(levels.cd.r, 100);
406 	    } else
407 		perror("SoundBlaster mixer read failed");
408 	}
409 #endif
410 
411 	return (0);
412 }
413 
414 /*---------------------------------------------*
415  * Send an arbitrary SCSI command to a device.
416  *---------------------------------------------*/
417 int
wm_scsi(struct wm_drive * d,unsigned char * cdb,int cdblen,void * retbuf,int retbuflen,int getreply)418 wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
419         void *retbuf, int retbuflen, int getreply)
420 {
421 	/* Don't know how to do SCSI passthrough... */
422 	return (-1);
423 }
424 
425 #ifdef SOUNDBLASTER
426 int	sb_fd = -3;			/* patchable from external programs */
427 #endif
428 
429 /*-----------------------------------------------------------------------*
430  * Open the CD device.  We can't determine the drive type under BSD/386.
431  *-----------------------------------------------------------------------*/
432 int
wmcd_open(struct wm_drvie * d)433 wmcd_open(struct wm_drvie *d)
434 {
435 	void	*aux = NULL, *daux = NULL;
436 	int fd = -1;
437 
438 	if (d->aux)	/* Device already open? */
439 		return (0);
440 
441 	if ((aux = cdopen(cd_device)) == NULL)
442 	{
443 		fprintf(stderr, "No cdrom found by libcdrom\n");
444 		exit(1);
445 	}
446 
447 	if ((daux = malloc(sizeof(struct pause_info))) == NULL)
448 		return (-1);
449 
450 #ifdef SOUNDBLASTER
451 	if (sb_fd != -3 && sb_fd < 0)
452 	    fd = open ("/dev/sb_mixer", O_RDWR, 0);
453 	if (fd < 0) {
454 	    if (sb_fd != -3)
455 		fprintf (stderr,"SoundBlaster mixer not found/disabled; will use direct CD volume control\n");
456 	    max_volume = max_volume_drive;
457 	    min_volume = min_volume_drive;
458 	}
459 #endif
460 
461 	/* Now fill in the relevant parts of the wm_drive structure. */
462 	*d = *(find_drive_struct("", "", ""));
463 	d->aux = aux;
464 	d->daux = daux;
465 	d->fd = fd;
466 	PAUSE_FRAME = 0;
467 	END_FRAME = 0;
468 
469 	(d->init)(d);
470 
471 	return (0);
472 } /* wmcd_open() */
473 
474 /*
475  * Re-Open the device if it is open.
476  */
477 int
wmcd_reopen(struct wm_drive * d)478 wmcd_reopen( struct wm_drive *d )
479 {
480 	int status;
481 
482 	do {
483         	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
484 		if (d->fd >= 0)		/* Device really open? */
485 		{
486         	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
487 		      status = close( d->fd );   /* close it! */
488 		      /* we know, that the file is closed, do we? */
489         	      d->fd = -1;
490 		}
491 		wm_susleep( 1000 );
492         	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
493 		status = wmcd_open( d ); /* open it as usual */
494 		wm_susleep( 1000 );
495 	} while ( status != 0 );
496         return status;
497 } /* wmcd_reopen() */
498 
499 
500 #endif /* __bsdi__ */
501