1 /*
2  *   libdi - CD Audio Device Interface Library
3  *
4  *   Copyright (C) 1993-2004  Ti Kan
5  *   E-mail: xmcd@amb.org
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 /*
23  * FreeBSD/NetBSD/OpenBSD ioctl method module
24  *
25  * Contributing author: Gennady B. Sorokopud
26  * E-mail: gena@NetVision.net.il
27  *
28  * Older NetBSD systems may need -DNETBSD_OLDIOC to compile.
29  */
30 
31 #ifndef lint
32 static char *_fbioc_c_ident_ = "@(#)fbioc.c	6.199 04/04/08";
33 #endif
34 
35 #include "common_d/appenv.h"
36 #include "common_d/util.h"
37 #include "libdi_d/libdi.h"
38 #include "libdi_d/fbioc.h"
39 #include "cdda_d/cdda.h"
40 
41 #if defined(DI_FBIOC) && !defined(DEMO_ONLY)
42 
43 extern appdata_t	app_data;
44 extern FILE		*errfp;
45 extern di_client_t	*di_clinfo;
46 extern sword32_t	di_clip_frames;
47 
48 
49 STATIC bool_t	fbioc_pause_resume(bool_t),
50 		fbioc_run_ab(curstat_t *),
51 		fbioc_run_sample(curstat_t *),
52 		fbioc_run_prog(curstat_t *),
53 		fbioc_run_repeat(curstat_t *),
54 		fbioc_disc_ready(curstat_t *),
55 		fbioc_disc_present(bool_t);
56 STATIC int	fbioc_cfg_vol(int, curstat_t *, bool_t);
57 STATIC void	fbioc_stat_poll(curstat_t *),
58 		fbioc_insert_poll(curstat_t *);
59 
60 
61 STATIC di_dev_t	*fbioc_devp = NULL;		/* CD device descriptor */
62 STATIC int	fbioc_stat_interval,		/* Status poll interval */
63 		fbioc_ins_interval;		/* Insert poll interval */
64 STATIC long	fbioc_stat_id,			/* Play status poll timer id */
65 		fbioc_insert_id,		/* Disc insert poll timer id */
66 		fbioc_search_id;		/* FF/REW timer id */
67 STATIC byte_t	fbioc_tst_status = MOD_NODISC;	/* Playback status on load */
68 STATIC bool_t	fbioc_not_open = TRUE,		/* Device not opened yet */
69 		fbioc_stat_polling,		/* Polling play status */
70 		fbioc_insert_polling,		/* Polling disc insert */
71 		fbioc_new_progshuf,		/* New program/shuffle seq */
72 		fbioc_start_search,		/* Start FF/REW play segment */
73 		fbioc_idx_pause,		/* Prev/next index pausing */
74 		fbioc_fake_stop,		/* Force a completion status */
75 		fbioc_playing,			/* Currently playing */
76 		fbioc_paused,			/* Currently paused */
77 		fbioc_bcd_hack,			/* Track numbers in BCD hack */
78 		fbioc_override_ap,		/* Override auto-play */
79 		fbioc_use_pread;		/* Use pread(2) to read CDDA */
80 STATIC sword32_t fbioc_sav_end_addr;		/* Err recov saved end addr */
81 STATIC word32_t	fbioc_next_sam;			/* Next SAMPLE track */
82 STATIC msf_t	fbioc_sav_end_msf;		/* Err recov saved end MSF */
83 STATIC byte_t	fbioc_sav_end_fmt;		/* Err recov saved end fmt */
84 STATIC cdda_client_t fbioc_cdda_client;		/* CDDA client struct */
85 
86 
87 /* FreeBSD/NetBSD/OpenBSD CDROM ioctl names */
88 STATIC iocname_t iname[] = {
89 	{ CDIOCREADSUBCHANNEL,	"CDIOCREADSUBCHANNEL"	},
90 	{ CDIOREADTOCHEADER,	"CDIOREADTOCHEADER"	},
91 	{ CDIOREADTOCENTRYS,	"CDIOREADTOCENTRYS"	},
92 	{ CDIOCEJECT,		"CDIOCEJECT"		},
93 	{ CDIOCSTART,		"CDIOCSTART"		},
94 	{ CDIOCSTOP,		"CDIOCSTOP"		},
95 	{ CDIOCPAUSE,		"CDIOCPAUSE"		},
96 	{ CDIOCRESUME,		"CDIOCRESUME"		},
97 	{ CDIOCGETVOL,		"CDIOCGETVOL"		},
98 	{ CDIOCSETVOL,		"CDIOCSETVOL"		},
99 	{ CDIOCPLAYTRACKS,	"CDIOCPLAYTRACKS"	},
100 	{ CDIOCPLAYMSF,		"CDIOCPLAYMSF"		},
101 	{ CDIOCALLOW,		"CDIOCALLOW"		},
102 	{ CDIOCPREVENT,		"CDIOCPREVENT"		},
103 	{ CDIOCREADAUDIO,	"CDIOCREADAUDIO"	},
104 	{ 0,			NULL			},
105 };
106 
107 
108 /***********************
109  *  internal routines  *
110  ***********************/
111 
112 
113 /*
114  * fbioc_close
115  *	Close CD device
116  *
117  * Args:
118  *	Nothing.
119  *
120  * Return:
121  *	Nothing.
122  */
123 STATIC void
fbioc_close(void)124 fbioc_close(void)
125 {
126 	if (fbioc_devp != NULL) {
127 		DBGPRN(DBG_DEVIO)(errfp, "\nClose device: %s\n",
128 				  fbioc_devp->path);
129 
130 		if (fbioc_devp->fd > 0)
131 			(void) close(fbioc_devp->fd);
132 		di_devfree(fbioc_devp);
133 		fbioc_devp = NULL;
134 	}
135 }
136 
137 
138 /*
139  * fbioc_open
140  *	Open CD device
141  *
142  * Args:
143  *	path - device path name string
144  *
145  * Return:
146  *	TRUE - open successful
147  *	FALSE - open failed
148  */
149 STATIC bool_t
fbioc_open(char * path)150 fbioc_open(char *path)
151 {
152 	struct stat	stbuf;
153 	char		errstr[ERR_BUF_SZ];
154 
155 	DBGPRN(DBG_DEVIO)(errfp, "\nOpen device: %s\n", path);
156 
157 	/* Check for validity of device node */
158 	if (stat(path, &stbuf) < 0) {
159 		(void) sprintf(errstr, app_data.str_staterr, path);
160 		DI_FATAL(errstr);
161 		return FALSE;
162 	}
163 
164 	if (!S_ISCHR(stbuf.st_mode)) {
165 		(void) sprintf(errstr, app_data.str_noderr, path);
166 		DI_FATAL(errstr);
167 		return FALSE;
168 	}
169 
170 	if ((fbioc_devp = di_devalloc(path)) == NULL) {
171 		DI_FATAL(app_data.str_nomemory);
172 		return FALSE;
173 	}
174 
175 	if ((fbioc_devp->fd = open(path, O_RDONLY | O_EXCL)) < 0) {
176 		DBGPRN(DBG_DEVIO)(errfp,
177 			"Cannot open %s: errno=%d\n", path, errno);
178 		di_devfree(fbioc_devp);
179 		fbioc_devp = NULL;
180 		return FALSE;
181 	}
182 
183 	return TRUE;
184 }
185 
186 
187 /*
188  * fbioc_init_vol
189  *	Initialize volume, balance and channel routing controls
190  *	to match the settings in the hardware, or to the preset
191  *	value, if specified.
192  *
193  * Args:
194  *	s      - Pointer to the curstat_t structure.
195  *	preset - If a preset value is configured, whether to force to
196  *               the preset.
197  *
198  * Return:
199  *	Nothing.
200  */
201 STATIC void
fbioc_init_vol(curstat_t * s,bool_t preset)202 fbioc_init_vol(curstat_t *s, bool_t preset)
203 {
204 	int	vol;
205 
206 	/* Query current volume/balance settings */
207 	if ((vol = fbioc_cfg_vol(0, s, TRUE)) >= 0)
208 		s->level = (byte_t) vol;
209 	else
210 		s->level = 0;
211 
212 	/* Set volume to preset value, if so configured */
213 	if (app_data.startup_vol > 0 && preset) {
214 		s->level_left = s->level_right = 100;
215 
216 		if ((vol = fbioc_cfg_vol(app_data.startup_vol, s, FALSE)) >= 0)
217 			s->level = (byte_t) vol;
218 	}
219 
220 	/* Initialize sliders */
221 	SET_VOL_SLIDER(s->level);
222 	SET_BAL_SLIDER((int) (s->level_right - s->level_left) / 2);
223 
224 	/* Set up channel routing */
225 	fbioc_route(s);
226 }
227 
228 
229 /*
230  * fbioc_rdsubq
231  *	Send Read Subchannel command to the device
232  *
233  * Args:
234  *	sp - Pointer to the caller-supplied cdstat_t return structure
235  *
236  * Return:
237  *	TRUE - success
238  *	FALSE - failure
239  */
240 STATIC bool_t
fbioc_rdsubq(cdstat_t * sp)241 fbioc_rdsubq(cdstat_t *sp)
242 {
243 	struct ioc_read_subchannel	subchnl;
244 	struct cd_sub_channel_info	sub;
245 	bool_t				ret;
246 
247 	memset((byte_t *) &sub, 0, sizeof(struct cd_sub_channel_info));
248 
249 	subchnl.address_format = app_data.subq_lba ?
250 		CD_LBA_FORMAT : CD_MSF_FORMAT;
251 	subchnl.data_format = CD_CURRENT_POSITION;
252 	subchnl.track = 0;
253 	subchnl.data_len = sizeof(struct cd_sub_channel_info);
254 	subchnl.data = &sub;
255 
256 	DBGDUMP(DBG_DEVIO)("ioc_read_subchannel data bytes",
257 		(byte_t *) &subchnl, sizeof(struct ioc_read_subchannel));
258 
259 	ret = fbioc_send(DI_ROLE_MAIN, CDIOCREADSUBCHANNEL, &subchnl, TRUE);
260 
261 	if (!ret)
262 		return FALSE;
263 
264 	DBGDUMP(DBG_DEVIO)("cd_sub_channel_info data bytes", (byte_t *) &sub,
265 		sizeof(struct cd_sub_channel_info));
266 
267 	/* Hack: to work around firmware anomalies in some CD drives. */
268 	if (sub.what.position.track_number >= MAXTRACK &&
269 	    sub.what.position.track_number != LEAD_OUT_TRACK) {
270 		sp->status = CDSTAT_NOSTATUS;
271 		return TRUE;
272 	}
273 
274 	/* Map the subchannel data into cdstat_t form */
275 
276 	switch (sub.header.audio_status) {
277 	case CD_AS_PLAY_IN_PROGRESS:
278 		sp->status = CDSTAT_PLAYING;
279 		break;
280 	case CD_AS_PLAY_PAUSED:
281 		sp->status = CDSTAT_PAUSED;
282 		break;
283 	case CD_AS_PLAY_COMPLETED:
284 		sp->status = CDSTAT_COMPLETED;
285 		break;
286 	case CD_AS_PLAY_ERROR:
287 		sp->status = CDSTAT_FAILED;
288 		break;
289 	case CD_AS_AUDIO_INVALID:
290 	case CD_AS_NO_STATUS:
291 	default:
292 		sp->status = CDSTAT_NOSTATUS;
293 		break;
294 	}
295 
296 	if (fbioc_bcd_hack) {
297 		/* Hack: BUGLY CD drive firmware */
298 		sp->track = (int) util_bcdtol(sub.what.position.track_number);
299 		sp->index = (int) util_bcdtol(sub.what.position.index_number);
300 	}
301 	else {
302 		sp->track = (int) sub.what.position.track_number;
303 		sp->index = (int) sub.what.position.index_number;
304 	}
305 
306 	if (app_data.subq_lba) {
307 		/* LBA mode */
308 #ifdef NETBSD_OLDIOC
309 		sp->abs_addr.addr = util_xlate_blk((sword32_t)
310 			(sub.what.position.absaddr[3] << 24) |
311 			(sub.what.position.absaddr[2] << 16) |
312 			(sub.what.position.absaddr[1] << 8) |
313 			sub.what.position.absaddr[0]
314 		);
315 		sp->rel_addr.addr = util_xlate_blk((sword32_t)
316 			(sub.what.position.reladdr[3] << 24) |
317 			(sub.what.position.reladdr[2] << 16) |
318 			(sub.what.position.reladdr[1] << 8) |
319 			sub.what.position.reladdr[0]
320 		);
321 #else
322 		sp->abs_addr.addr = util_xlate_blk((sword32_t)
323 			util_bswap32(sub.what.position.absaddr.lba)
324 		);
325 		sp->rel_addr.addr = util_xlate_blk((sword32_t)
326 			util_bswap32(sub.what.position.reladdr.lba)
327 		);
328 #endif
329 		util_blktomsf(
330 			sp->abs_addr.addr,
331 			&sp->abs_addr.min,
332 			&sp->abs_addr.sec,
333 			&sp->abs_addr.frame,
334 			MSF_OFFSET
335 		);
336 		util_blktomsf(
337 			sp->rel_addr.addr,
338 			&sp->rel_addr.min,
339 			&sp->rel_addr.sec,
340 			&sp->rel_addr.frame,
341 			0
342 		);
343 	}
344 	else {
345 		/* MSF mode */
346 #ifdef NETBSD_OLDIOC
347 		sp->abs_addr.min = sub.what.position.absaddr[1];
348 		sp->abs_addr.sec = sub.what.position.absaddr[2];
349 		sp->abs_addr.frame = sub.what.position.absaddr[3];
350 
351 		sp->rel_addr.min = sub.what.position.reladdr[1];
352 		sp->rel_addr.sec = sub.what.position.reladdr[2];
353 		sp->rel_addr.frame = sub.what.position.reladdr[3];
354 #else
355 		sp->abs_addr.min = sub.what.position.absaddr.msf.minute;
356 		sp->abs_addr.sec = sub.what.position.absaddr.msf.second;
357 		sp->abs_addr.frame = sub.what.position.absaddr.msf.frame;
358 
359 		sp->rel_addr.min = sub.what.position.reladdr.msf.minute;
360 		sp->rel_addr.sec = sub.what.position.reladdr.msf.second;
361 		sp->rel_addr.frame = sub.what.position.reladdr.msf.frame;
362 #endif
363 		util_msftoblk(
364 			sp->abs_addr.min,
365 			sp->abs_addr.sec,
366 			sp->abs_addr.frame,
367 			&sp->abs_addr.addr,
368 			MSF_OFFSET
369 		);
370 		util_msftoblk(
371 			sp->rel_addr.min,
372 			sp->rel_addr.sec,
373 			sp->rel_addr.frame,
374 			&sp->rel_addr.addr,
375 			0
376 		);
377 	}
378 
379 	return (ret);
380 }
381 
382 
383 /*
384  * fbioc_rdtoc
385  *	Send Read TOC command to the device
386  *
387  * Args:
388  *	buf - Pointer to the return data toc header
389  *	h - address of pointer to array of toc entrys will be allocated
390  *	    by this routine
391  *	start - Starting track number for which the TOC data is returned
392  *	msf - Whether to use MSF or logical block address data format
393  *
394  * Return:
395  *	TRUE - success
396  *	FALSE - failure
397  */
398 STATIC bool_t
fbioc_rdtoc(struct ioc_toc_header * h,struct cd_toc_entry ** e,int start,bool_t msf)399 fbioc_rdtoc(struct ioc_toc_header *h, struct cd_toc_entry **e,
400 	    int start, bool_t msf)
401 {
402 	int				i,
403 					j,
404 					k,
405 					allocsz;
406 	struct cd_toc_entry		*t;
407 	struct ioc_read_toc_entry	tocentry;
408 
409 	/* Read the TOC header first */
410 	if (!fbioc_send(DI_ROLE_MAIN, CDIOREADTOCHEADER, h, TRUE))
411 		return FALSE;
412 
413 	DBGDUMP(DBG_DEVIO)("ioc_toc_header data bytes", (byte_t *) h,
414 		sizeof(struct ioc_toc_header));
415 
416 	if (start == 0)
417 		start = h->starting_track;
418 
419 	if (start > (int) h->ending_track)
420 		return FALSE;
421 
422 	allocsz = (h->ending_track - start + 2) * sizeof(struct cd_toc_entry);
423 
424 	*e = (struct cd_toc_entry *)(void *) MEM_ALLOC(
425 		"cd_toc_entry",
426 		allocsz
427 	);
428 	t = (struct cd_toc_entry *)(void *) MEM_ALLOC(
429 		"cd_toc_entry",
430 		allocsz
431 	);
432 
433 	if (*e == NULL || t == NULL) {
434 		DI_FATAL(app_data.str_nomemory);
435 		return FALSE;
436 	}
437 
438 	(void) memset((byte_t *) *e, 0, allocsz);
439 	(void) memset((byte_t *) t, 0, allocsz);
440 	tocentry.starting_track = start;
441 	tocentry.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
442 	tocentry.data_len = allocsz;
443 	tocentry.data = t;
444 
445 	DBGDUMP(DBG_DEVIO)("ioc_read_toc_entry data bytes",
446 		(byte_t *) &tocentry, sizeof( struct ioc_read_toc_entry));
447 
448 	if (!fbioc_send(DI_ROLE_MAIN, CDIOREADTOCENTRYS, &tocentry, TRUE)) {
449 		MEM_FREE(*e);
450 		MEM_FREE(t);
451 		return FALSE;
452 	}
453 
454 	DBGDUMP(DBG_DEVIO)("cd_toc_entry data bytes", (byte_t *) t, allocsz);
455 
456 	for (i = start; i <= (int) (h->ending_track + 1); i++) {
457 		j = i - start;
458 
459 		/* Hack: workaround CD drive firmware bug
460 		 * Some CD drives return track numbers in BCD
461 		 * rather than binary.
462 		 */
463 #ifdef NETBSD_OLDIOC
464 		if ((int) (t[j].track & 0xf) > 0x9 &&
465 		    (int) (t[j].track & 0xf) < 0x10 &&
466 		    t[j].addr[0] == 0 && t[j].addr[1] == 0 &&
467 		    t[j].addr[2] == 0 && t[j].addr[3] == 0)
468 #else
469 		if ((int) (t[j].track & 0xf) > 0x9 &&
470 		    (int) (t[j].track & 0xf) < 0x10 &&
471 		    t[j].addr.lba == 0)
472 #endif
473 		{
474 			/* BUGLY CD drive firmware detected! */
475 			fbioc_bcd_hack = TRUE;
476 		}
477 
478 		/* Sanity check */
479 #ifdef NETBSD_OLDIOC
480 		if (t[j].track == LEAD_OUT_TRACK &&
481 		    t[j].addr[0] == t[0].addr[0] &&
482 		    t[j].addr[1] == t[1].addr[1] &&
483 		    t[j].addr[2] == t[2].addr[2] &&
484 		    t[j].addr[3] == t[3].addr[3])
485 #else
486 		if (t[j].track == LEAD_OUT_TRACK &&
487 		    t[j].addr.lba == t[0].addr.lba)
488 #endif
489 		{
490 			MEM_FREE(*e);
491 			MEM_FREE(t);
492 			return FALSE;
493 		}
494 	}
495 
496 	/* Fix up TOC data */
497 	for (i = start; i <= (int) (h->ending_track + 1); i++) {
498 		if (fbioc_bcd_hack && (i & 0xf) > 0x9 && (i & 0xf) < 0x10)
499 			continue;
500 
501 		j = i - start;
502 		k = (fbioc_bcd_hack ? util_bcdtol(i) : i) - start;
503 
504 		if (i == (h->ending_track + 1)) {
505 			/* FreeBSD HACK: Fix up lead-out track number */
506 			t[j].track = LEAD_OUT_TRACK;
507 		}
508 
509 		(*e)[k].control = t[j].control;
510 		(*e)[k].addr_type = t[j].addr_type;
511 #ifdef NETBSD_OLDIOC
512 		(*e)[k].addr[0] = t[j].addr[0];
513 		(*e)[k].addr[1] = t[j].addr[1];
514 		(*e)[k].addr[2] = t[j].addr[2];
515 		(*e)[k].addr[3] = t[j].addr[3];
516 #else
517 		(*e)[k].addr.lba = t[j].addr.lba;
518 #endif
519 		if (t[j].track == LEAD_OUT_TRACK) {
520 			(*e)[k].track = t[j].track;
521 			break;
522 		}
523 		else {
524 			if (fbioc_bcd_hack)
525 				(*e)[k].track = (byte_t)
526 					util_bcdtol(t[j].track);
527 			else
528 				(*e)[k].track = t[j].track;
529 		}
530 	}
531 
532 	if (fbioc_bcd_hack)
533 		h->ending_track = (byte_t) util_bcdtol(h->ending_track);
534 
535 	MEM_FREE(t);
536 
537 	return TRUE;
538 }
539 
540 
541 /*
542  * fbioc_disc_present
543  *	Check if a CD is loaded.
544  *
545  * Args:
546  *	savstat - Whether to save start-up status in fbioc_tst_status.
547  *
548  * Return:
549  *	TRUE - success
550  *	FALSE - failure (drive not ready)
551  */
552 STATIC bool_t
fbioc_disc_present(bool_t savstat)553 fbioc_disc_present(bool_t savstat)
554 {
555 	int				allocsz;
556 	sword32_t			a1,
557 					a2;
558 	struct cd_toc_entry		*e;
559 	struct cd_sub_channel_info	sub;
560 	struct ioc_read_subchannel	subchnl;
561 	struct ioc_toc_header		h;
562 	struct ioc_read_toc_entry	tocentry;
563 	static int			tot_trks = 0;
564 	static sword32_t		sav_a1 = 0,
565 					sav_a2 = 0;
566 
567 	if (savstat)
568 		fbioc_tst_status = MOD_NODISC;
569 
570 	/* Fake it with CDIOCREADSUBCHANNEL */
571 	(void) memset((byte_t *) &sub, 0, sizeof(struct cd_sub_channel_info));
572 	subchnl.address_format = CD_MSF_FORMAT;
573 	subchnl.data_format = CD_CURRENT_POSITION;
574 	subchnl.track = 0;
575 	subchnl.data_len = sizeof(struct cd_sub_channel_info);
576 	subchnl.data = &sub;
577 
578 	if (!fbioc_send(DI_ROLE_MAIN, CDIOCREADSUBCHANNEL, &subchnl,
579 			(bool_t) ((app_data.debug & DBG_DEVIO) != 0)))
580 		return FALSE;
581 
582 	switch (sub.header.audio_status) {
583 	case CD_AS_PLAY_IN_PROGRESS:
584 		if (savstat) {
585 			DBGPRN(DBG_DEVIO)(errfp,
586 				"\nstatus=CD_AS_PLAY_IN_PROGRESS\n");
587 			fbioc_tst_status = MOD_PLAY;
588 			return TRUE;
589 		}
590 		break;
591 	case CD_AS_PLAY_PAUSED:
592 		if (savstat) {
593 			DBGPRN(DBG_DEVIO)(errfp,
594 				"\nstatus=CD_AS_PLAY_PAUSED\n");
595 			fbioc_tst_status = MOD_PAUSE;
596 			return TRUE;
597 		}
598 		break;
599 	case CD_AS_PLAY_ERROR:
600 		DBGPRN(DBG_DEVIO)(errfp, "\nstatus=CD_AS_PLAY_ERROR\n");
601 		break;
602 	case CD_AS_PLAY_COMPLETED:
603 		DBGPRN(DBG_DEVIO)(errfp, "\nstatus=CD_AS_PLAY_COMPLETED\n");
604 		break;
605 	case CD_AS_NO_STATUS:
606 		DBGPRN(DBG_DEVIO)(errfp, "\nstatus=CD_AS_NO_STATUS\n");
607 		break;
608 	case CD_AS_AUDIO_INVALID:
609 		DBGPRN(DBG_DEVIO)(errfp, "\nstatus=CD_AS_AUDIO_INVALID\n");
610 		break;
611 	default:
612 		DBGPRN(DBG_DEVIO)(errfp, "\nstatus=unknown (%d)\n",
613 			sub.header.audio_status);
614 		return FALSE;
615 	}
616 
617 	if (savstat)
618 		fbioc_tst_status = MOD_STOP;
619 
620 	/* CDIOCREADSUBCHANNEL didn't give useful info.
621 	 * Try CDIOREADTOCHEADER and CDIOREADTOCENTRYS.
622 	 */
623 	(void) memset((byte_t *) &h, 0, sizeof(struct ioc_toc_header));
624 
625 	if (!fbioc_send(DI_ROLE_MAIN, CDIOREADTOCHEADER, &h,
626 			(bool_t) ((app_data.debug & DBG_DEVIO) != 0)))
627 		return FALSE;
628 
629 	if (h.starting_track == 0 && h.ending_track == 0)
630 		return FALSE;
631 
632 	if ((h.ending_track - h.starting_track + 1) != tot_trks) {
633 		/* Disc changed */
634 		tot_trks = h.ending_track - h.starting_track + 1;
635 		return FALSE;
636 	}
637 
638 	allocsz = (tot_trks + 1) * sizeof(struct cd_toc_entry);
639 	e = (struct cd_toc_entry *)(void *) MEM_ALLOC("cd_toc_entry", allocsz);
640 	if (e == NULL) {
641 		DI_FATAL(app_data.str_nomemory);
642 		return FALSE;
643 	}
644 
645 	(void) memset((byte_t *) e, 0, allocsz);
646 	tocentry.starting_track = (unsigned char) h.starting_track;
647 	tocentry.address_format = CD_MSF_FORMAT;
648 	tocentry.data_len = allocsz;
649 	tocentry.data = e;
650 
651 	if (!fbioc_send(DI_ROLE_MAIN, CDIOREADTOCENTRYS, &tocentry,
652 			(bool_t) ((app_data.debug & DBG_DEVIO) != 0))) {
653 		MEM_FREE(e);
654 		return FALSE;
655 	}
656 
657 #ifdef NETBSD_OLDIOC
658 	a1 = (sword32_t) (
659 		(e[h.starting_track - 1].addr[3] << 24) |
660 		(e[h.starting_track - 1].addr[2] << 16) |
661 		(e[h.starting_track - 1].addr[1] << 8) |
662 		(e[h.starting_track - 1].addr[0])
663 	);
664 	a2 = (sword32_t) (
665 		(e[h.ending_track - 1].addr[3] << 24) |
666 		(e[h.ending_track - 1].addr[2] << 16) |
667 		(e[h.ending_track - 1].addr[1] << 8) |
668 		(e[h.ending_track - 1].addr[0])
669 	);
670 #else
671 	a1 = (sword32_t) e[h.starting_track - 1].addr.lba;
672 	a2 = (sword32_t) e[h.ending_track - 1].addr.lba;
673 #endif
674 
675 	DBGPRN(DBG_DEVIO)(errfp, "\na1=0x%x a2=0x%x\n", a1, a2);
676 	MEM_FREE(e);
677 
678 	if (a1 != sav_a1 || a2 != sav_a2) {
679 		/* Disc changed */
680 		sav_a1 = a1;
681 		sav_a2 = a2;
682 		return FALSE;
683 	}
684 
685 	if (tot_trks > 1 && a1 == a2)
686 		return FALSE;
687 
688 	return TRUE;
689 }
690 
691 
692 /*
693  * fbioc_playmsf
694  *	Send Play Audio MSF command to the device
695  *
696  * Args:
697  *	start - Pointer to the starting position MSF data
698  *	end - Pointer to the ending position MSF data
699  *
700  * Return:
701  *	TRUE - success
702  *	FALSE - failure
703  */
704 STATIC bool_t
fbioc_playmsf(msf_t * start,msf_t * end)705 fbioc_playmsf(msf_t *start, msf_t *end)
706 {
707 	struct ioc_play_msf	m;
708 
709 	/* If the start or end positions are less than the minimum
710 	 * position, patch them to the minimum positions.
711 	 */
712 	if (start->min == 0 && start->sec < 2) {
713 		m.start_m = 0;
714 		m.start_s = 2;
715 		m.start_f = 0;
716 	}
717 	else {
718 		m.start_m = start->min;
719 		m.start_s = start->sec;
720 		m.start_f = start->frame;
721 	}
722 
723 	if (end->min == 0 && end->sec < 2) {
724 		m.end_m = 0;
725 		m.end_s = 2;
726 		m.end_f = 0;
727 	}
728 	else {
729 		m.end_m = end->min;
730 		m.end_s = end->sec;
731 		m.end_f = end->frame;
732 	}
733 
734 	/* If start == end, just return success */
735 	if (m.start_m == m.end_m &&
736 	    m.start_s == m.end_s &&
737 	    m.start_f == m.end_f)
738 		return TRUE;
739 
740 	DBGDUMP(DBG_DEVIO)("ioc_play_msf data bytes", (byte_t *) &m,
741 		sizeof(struct ioc_play_msf));
742 
743 	return (fbioc_send(DI_ROLE_MAIN, CDIOCPLAYMSF, &m, TRUE));
744 }
745 
746 
747 /*
748  * fbioc_play_trkidx
749  *	Send Play Audio Track/Index command to the device
750  *
751  * Args:
752  *	start_trk - Starting track number
753  *	start_idx - Starting index number
754  *	end_trk - Ending track number
755  *	end_idx - Ending index number
756  *
757  * Return:
758  *	TRUE - success
759  *	FALSE - failure
760  */
761 bool_t
fbioc_play_trkidx(int start_trk,int start_idx,int end_trk,int end_idx)762 fbioc_play_trkidx(int start_trk, int start_idx, int end_trk, int end_idx)
763 {
764 	struct ioc_play_track	t;
765 
766 	if (fbioc_bcd_hack) {
767 		/* Hack: BUGLY CD drive firmware */
768 		t.start_track = util_ltobcd(start_trk);
769 		t.start_index = util_ltobcd(start_idx);
770 		t.end_track = util_ltobcd(end_trk);
771 		t.end_index = util_ltobcd(end_idx);
772 	}
773 	else {
774 		t.start_track = start_trk;
775 		t.start_index = start_idx;
776 		t.end_track = end_trk;
777 		t.end_index = end_idx;
778 	}
779 
780 	DBGDUMP(DBG_DEVIO)("ioc_play_track data bytes", (byte_t *) &t,
781 		sizeof(struct ioc_play_track));
782 
783 	return (fbioc_send(DI_ROLE_MAIN, CDIOCPLAYTRACKS, &t, TRUE));
784 }
785 
786 
787 /*
788  * fbioc_start_stop
789  *	Send Start/Stop Unit command to the device
790  *
791  * Args:
792  *	start - Whether to start unit or stop unit
793  *	loej - Whether caddy load/eject operation should be performed
794  *
795  * Return:
796  *	TRUE - success
797  *	FALSE - failure
798  */
799 STATIC bool_t
fbioc_start_stop(bool_t start,bool_t loej)800 fbioc_start_stop(bool_t start, bool_t loej)
801 {
802 	bool_t		ret;
803 	curstat_t	*s = di_clinfo->curstat_addr();
804 
805 	if (app_data.strict_pause_resume && fbioc_paused)
806 		(void) fbioc_pause_resume(TRUE);
807 
808 	if (start) {
809 		if (loej)
810 			ret = FALSE;
811 		else
812 			ret = fbioc_send(DI_ROLE_MAIN, CDIOCSTART, NULL, TRUE);
813 	}
814 	else {
815 		fbioc_playing = FALSE;
816 
817 		if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
818 			(void) cdda_stop(fbioc_devp, s);
819 
820 			if (!fbioc_is_enabled(DI_ROLE_MAIN)) {
821 				/* Enable I/O from current process */
822 				fbioc_enable(DI_ROLE_MAIN);
823 			}
824 		}
825 
826 		if (loej)
827 			ret = fbioc_send(DI_ROLE_MAIN, CDIOCEJECT, NULL, TRUE);
828 		else
829 			ret = fbioc_send(DI_ROLE_MAIN, CDIOCSTOP, NULL, TRUE);
830 	}
831 
832 	/* Delay a bit to let the CD load or eject.  This is a hack to
833 	 * work around firmware bugs in some CD drives.  These drives
834 	 * don't handle new commands well when the CD is loading/ejecting
835 	 * with the IMMED bit set in the Start/Stop Unit command.
836 	 */
837 	if (ret) {
838 		if (loej) {
839 			int	n;
840 
841 			n = (app_data.ins_interval + 1000 - 1) / 1000;
842 			if (start)
843 				n *= 2;
844 
845 			util_delayms(n * 1000);
846 		}
847 		else if (start && app_data.spinup_interval > 0)
848 			util_delayms(app_data.spinup_interval * 1000);
849 	}
850 
851 	return (ret);
852 
853 }
854 
855 
856 /*
857  * fbioc_pause_resume
858  *	Send Pause/Resume command to the device
859  *
860  * Args:
861  *	resume - Whether to resume or pause
862  *
863  * Return:
864  *	TRUE - success
865  *	FALSE - failure
866  */
867 STATIC bool_t
fbioc_pause_resume(bool_t resume)868 fbioc_pause_resume(bool_t resume)
869 {
870 	bool_t		ret;
871 	curstat_t	*s = di_clinfo->curstat_addr();
872 
873 	if (!app_data.pause_supp)
874 		return FALSE;
875 
876 	if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
877 		ret = cdda_pause_resume(fbioc_devp, s, resume);
878 	}
879 	else {
880 		ret = fbioc_send(
881 			DI_ROLE_MAIN, resume ? CDIOCRESUME : CDIOCPAUSE,
882 			NULL, TRUE
883 		);
884 	}
885 	if (ret)
886 		fbioc_paused = !resume;
887 
888 	return (ret);
889 }
890 
891 
892 /*
893  * fbioc_do_playaudio
894  *	General top-level play audio function
895  *
896  * Args:
897  *	addr_fmt - The address formats specified:
898  *		ADDR_BLK: logical block address (not supported)
899  *		ADDR_MSF: MSF address
900  *		ADDR_TRKIDX: Track/index numbers
901  *		ADDR_OPTEND: Ending address can be ignored
902  *	start_addr - Starting logical block address (not supported)
903  *	end_addr - Ending logical block address (not supported)
904  *	start_msf - Pointer to start address MSF data
905  *	end_msf - Pointer to end address MSF data
906  *	trk - Starting track number
907  *	idx - Starting index number
908  *
909  * Return:
910  *	TRUE - success
911  *	FALSE - failure
912  */
913 STATIC bool_t
fbioc_do_playaudio(byte_t addr_fmt,sword32_t start_addr,sword32_t end_addr,msf_t * start_msf,msf_t * end_msf,byte_t trk,byte_t idx)914 fbioc_do_playaudio(
915 	byte_t		addr_fmt,
916 	sword32_t	start_addr,
917 	sword32_t	end_addr,
918 	msf_t		*start_msf,
919 	msf_t		*end_msf,
920 	byte_t		trk,
921 	byte_t		idx
922 )
923 {
924 	msf_t		emsf,
925 			*emsfp = NULL;
926 	sword32_t	tmp_saddr,
927 			tmp_eaddr;
928 	bool_t		ret = FALSE,
929 			do_playmsf,
930 			do_play10,
931 			do_play12,
932 			do_playti;
933 	curstat_t	*s = di_clinfo->curstat_addr();
934 
935 	/* Fix addresses: Some CD drives will only allow playing to
936 	 * the last frame minus a few frames.
937 	 */
938 	if ((addr_fmt & ADDR_MSF) && end_msf != NULL) {
939 		emsf = *end_msf;	/* Structure copy */
940 		emsfp = &emsf;
941 
942 		util_msftoblk(
943 			start_msf->min,
944 			start_msf->sec,
945 			start_msf->frame,
946 			&tmp_saddr,
947 			MSF_OFFSET
948 		);
949 
950 		util_msftoblk(
951 			emsfp->min,
952 			emsfp->sec,
953 			emsfp->frame,
954 			&tmp_eaddr,
955 			MSF_OFFSET
956 		);
957 
958 		if (tmp_eaddr > s->discpos_tot.addr)
959 			tmp_eaddr = s->discpos_tot.addr;
960 
961 		if (tmp_eaddr >= di_clip_frames)
962 			tmp_eaddr -= di_clip_frames;
963 		else
964 			tmp_eaddr = 0;
965 
966 		util_blktomsf(
967 			tmp_eaddr,
968 			&emsfp->min,
969 			&emsfp->sec,
970 			&emsfp->frame,
971 			MSF_OFFSET
972 		);
973 
974 		if (tmp_saddr >= tmp_eaddr)
975 			return FALSE;
976 
977 		emsfp->res = start_msf->res = 0;
978 
979 		/* Save end address for error recovery */
980 		fbioc_sav_end_msf = *end_msf;
981 	}
982 	if (addr_fmt & ADDR_BLK) {
983 		if (end_addr > s->discpos_tot.addr)
984 			end_addr = s->discpos_tot.addr;
985 
986 		if (end_addr >= di_clip_frames)
987 			end_addr -= di_clip_frames;
988 		else
989 			end_addr = 0;
990 
991 		if (start_addr >= end_addr)
992 			return FALSE;
993 
994 		/* Save end address for error recovery */
995 		fbioc_sav_end_addr = end_addr;
996 	}
997 
998 	/* Save end address format for error recovery */
999 	fbioc_sav_end_fmt = addr_fmt;
1000 
1001 	do_playmsf = (addr_fmt & ADDR_MSF) && app_data.playmsf_supp;
1002 	do_play10 = (addr_fmt & ADDR_BLK) && app_data.play10_supp;
1003 	do_play12 = (addr_fmt & ADDR_BLK) && app_data.play12_supp;
1004 	do_playti = (addr_fmt & ADDR_TRKIDX) && app_data.playti_supp;
1005 
1006 	if (do_playmsf || do_playti) {
1007 		if (fbioc_paused) {
1008 			if (app_data.strict_pause_resume) {
1009 				/* Resume first */
1010 				(void) fbioc_pause_resume(TRUE);
1011 			}
1012 		}
1013 		else if (fbioc_playing) {
1014 			if (app_data.play_pause_play) {
1015 				/* Pause first */
1016 				(void) fbioc_pause_resume(FALSE);
1017 			}
1018 		}
1019 		else {
1020 			/* Spin up CD */
1021 			(void) fbioc_start_stop(TRUE, FALSE);
1022 		}
1023 	}
1024 
1025 	if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
1026 		if (do_play12 || do_play10) {
1027 			if (fbioc_is_enabled(DI_ROLE_MAIN)) {
1028 				/* Disable I/O from current process */
1029 				fbioc_disable(DI_ROLE_MAIN);
1030 			}
1031 
1032 			ret = cdda_play(fbioc_devp, s, start_addr, end_addr);
1033 		}
1034 		else if (do_playmsf) {
1035 			util_msftoblk(
1036 				start_msf->min,
1037 				start_msf->sec,
1038 				start_msf->frame,
1039 				&start_addr,
1040 				MSF_OFFSET
1041 			);
1042 			util_msftoblk(
1043 				emsfp->min,
1044 				emsfp->sec,
1045 				emsfp->frame,
1046 				&end_addr,
1047 				MSF_OFFSET
1048 			);
1049 
1050 			ret = cdda_play(fbioc_devp, s, start_addr, end_addr);
1051 		}
1052 	}
1053 	else {
1054 		if (do_playmsf)
1055 			ret = fbioc_playmsf(start_msf, emsfp);
1056 
1057 		if (!ret && do_playti)
1058 			ret = fbioc_play_trkidx(trk, idx, trk, idx);
1059 	}
1060 
1061 	if (ret) {
1062 		fbioc_playing = TRUE;
1063 		fbioc_paused = FALSE;
1064 	}
1065 
1066 	return (ret);
1067 }
1068 
1069 
1070 /*
1071  * fbioc_play_recov
1072  *	Playback interruption recovery handler: Restart playback after
1073  *	skipping some frames.
1074  *
1075  * Args:
1076  *	blk   - Interruption frame address
1077  *	iserr - Whether interruption is due to an error
1078  *
1079  * Return:
1080  *	TRUE - success
1081  *	FALSE - failure
1082  */
1083 STATIC bool_t
fbioc_play_recov(sword32_t blk,bool_t iserr)1084 fbioc_play_recov(sword32_t blk, bool_t iserr)
1085 {
1086 	msf_t		recov_start_msf;
1087 	sword32_t	recov_start_addr;
1088 	bool_t		ret;
1089 
1090 	ret = TRUE;
1091 
1092 	recov_start_addr = blk + ERR_SKIPBLKS;
1093 	util_blktomsf(
1094 		recov_start_addr,
1095 		&recov_start_msf.min,
1096 		&recov_start_msf.sec,
1097 		&recov_start_msf.frame,
1098 		MSF_OFFSET
1099 	);
1100 
1101 	/* Check to see if we have skipped past
1102 	 * the end.
1103 	 */
1104 	if (recov_start_msf.min > fbioc_sav_end_msf.min)
1105 		ret = FALSE;
1106 	else if (recov_start_msf.min == fbioc_sav_end_msf.min) {
1107 		if (recov_start_msf.sec > fbioc_sav_end_msf.sec)
1108 			ret = FALSE;
1109 		else if ((recov_start_msf.sec ==
1110 			  fbioc_sav_end_msf.sec) &&
1111 			 (recov_start_msf.frame >
1112 			  fbioc_sav_end_msf.frame)) {
1113 			ret = FALSE;
1114 		}
1115 	}
1116 	if (recov_start_addr >= fbioc_sav_end_addr)
1117 		ret = FALSE;
1118 
1119 	if (ret) {
1120 		/* Restart playback */
1121 		if (iserr) {
1122 			(void) fprintf(errfp,
1123 				"CD audio: %s (%02u:%02u.%02u)\n",
1124 				app_data.str_recoverr,
1125 				recov_start_msf.min,
1126 				recov_start_msf.sec,
1127 				recov_start_msf.frame
1128 			);
1129 		}
1130 
1131 		ret = fbioc_do_playaudio(
1132 			fbioc_sav_end_fmt,
1133 			recov_start_addr, fbioc_sav_end_addr,
1134 			&recov_start_msf, &fbioc_sav_end_msf,
1135 			0, 0
1136 		);
1137 	}
1138 
1139 	return (ret);
1140 }
1141 
1142 
1143 /*
1144  * fbioc_get_playstatus
1145  *	Obtain and update current playback status information
1146  *
1147  * Args:
1148  *	s - Pointer to the curstat_t structure
1149  *
1150  * Return:
1151  *	TRUE - Audio playback is in progress
1152  *	FALSE - Audio playback stopped or command failure
1153  */
1154 STATIC bool_t
fbioc_get_playstatus(curstat_t * s)1155 fbioc_get_playstatus(curstat_t *s)
1156 {
1157 	cdstat_t		cdstat;
1158 	word32_t		curtrk,
1159 				curidx;
1160 	bool_t			ret,
1161 				done;
1162 	static int		errcnt = 0,
1163 				nostatcnt = 0;
1164 	static sword32_t	errblk = 0;
1165 	static bool_t		in_fbioc_get_playstatus = FALSE;
1166 
1167 	/* Lock this routine from multiple entry */
1168 	if (in_fbioc_get_playstatus)
1169 		return TRUE;
1170 
1171 	in_fbioc_get_playstatus = TRUE;
1172 
1173 	if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
1174 		ret = cdda_getstatus(fbioc_devp, s, &cdstat);
1175 		if (ret &&
1176 		    (cdstat.level != s->level ||
1177 		     cdstat.level_left  != s->level_left ||
1178 		     cdstat.level_right != s->level_right)) {
1179 			int	vol;
1180 
1181 			/* Update volume & balance level */
1182 			s->level_left = cdstat.level_left;
1183 			s->level_right = cdstat.level_right;
1184 			vol = fbioc_cfg_vol((int) cdstat.level, s, FALSE);
1185 			if (vol >= 0) {
1186 				s->level = vol;
1187 				SET_VOL_SLIDER(s->level);
1188 				SET_BAL_SLIDER((int)
1189 					(s->level_right - s->level_left) / 2
1190 				);
1191 			}
1192 		}
1193 	}
1194 	else
1195 		ret = fbioc_rdsubq(&cdstat);
1196 
1197 	if (!ret) {
1198 		/* Check to see if the disc had been manually ejected */
1199 		if (!fbioc_disc_ready(s)) {
1200 			fbioc_sav_end_addr = 0;
1201 			fbioc_sav_end_msf.min = 0;
1202 			fbioc_sav_end_msf.sec = 0;
1203 			fbioc_sav_end_msf.frame = 0;
1204 			fbioc_sav_end_fmt = 0;
1205 			errcnt = 0;
1206 			errblk = 0;
1207 
1208 			in_fbioc_get_playstatus = FALSE;
1209 			return FALSE;
1210 		}
1211 
1212 		/* The read subchannel command failed for some
1213 		 * unknown reason.  Just return success and
1214 		 * hope the next poll succeeds.  We don't want
1215 		 * to return FALSE here because that would stop
1216 		 * the poll.
1217 		 */
1218 		in_fbioc_get_playstatus = FALSE;
1219 		return TRUE;
1220 	}
1221 
1222 	curtrk = cdstat.track;
1223 	curidx = cdstat.index;
1224 	s->curpos_tot = cdstat.abs_addr;	/* structure copy */
1225 	s->curpos_trk = cdstat.rel_addr;	/* structure copy */
1226 
1227 	s->tot_frm = cdstat.tot_frm;
1228 	s->frm_played = cdstat.frm_played;
1229 	s->frm_per_sec = cdstat.frm_per_sec;
1230 
1231 	/* Update time display */
1232 	DPY_TIME(s, FALSE);
1233 
1234 	if (curtrk != s->cur_trk) {
1235 		s->cur_trk = curtrk;
1236 		/* Update track number display */
1237 		DPY_TRACK(s);
1238 	}
1239 
1240 	if (curidx != s->cur_idx) {
1241 		s->cur_idx = curidx;
1242 		s->sav_iaddr = s->curpos_tot.addr;
1243 		/* Update index number display */
1244 		DPY_INDEX(s);
1245 	}
1246 
1247 	/* Update play mode display */
1248 	DPY_PLAYMODE(s, FALSE);
1249 
1250 	/* Hack: to work around the fact that some CD drives return
1251 	 * CD_AS_PLAY_PAUSED status after issuing a Stop Unit command.
1252 	 * Just treat the status as completed if we get a paused status
1253 	 * and we don't expect the drive to be paused.
1254 	 */
1255 	if (cdstat.status == CDSTAT_PAUSED && s->mode != MOD_PAUSE &&
1256 	    !fbioc_idx_pause)
1257 		cdstat.status = CDSTAT_COMPLETED;
1258 
1259 	/* Force completion status */
1260 	if (fbioc_fake_stop)
1261 		cdstat.status = CDSTAT_COMPLETED;
1262 
1263 	/* Deal with playback status */
1264 	switch (cdstat.status) {
1265 	case CDSTAT_PLAYING:
1266 	case CDSTAT_PAUSED:
1267 		nostatcnt = 0;
1268 		done = FALSE;
1269 
1270 		/* If we haven't encountered an error for a while, then
1271 		 * clear the error count.
1272 		 */
1273 		if (errcnt > 0 &&
1274 		    (s->curpos_tot.addr - errblk) > ERR_CLRTHRESH)
1275 			errcnt = 0;
1276 		break;
1277 
1278 	case CDSTAT_FAILED:
1279 		nostatcnt = 0;
1280 		/* Check to see if the disc had been manually ejected */
1281 		if (!fbioc_disc_ready(s)) {
1282 			fbioc_sav_end_addr = 0;
1283 			fbioc_sav_end_msf.min = 0;
1284 			fbioc_sav_end_msf.sec = 0;
1285 			fbioc_sav_end_msf.frame = 0;
1286 			fbioc_sav_end_fmt = 0;
1287 			errcnt = 0;
1288 			errblk = 0;
1289 
1290 			in_fbioc_get_playstatus = FALSE;
1291 			return FALSE;
1292 		}
1293 
1294 		/* Audio playback stopped due to a disc error.  We will
1295 		 * try to restart the playback by skipping a few frames
1296 		 * and continuing.  This will cause a glitch in the sound
1297 		 * but is better than just stopping.
1298 		 */
1299 		done = FALSE;
1300 
1301 		/* Check for max errors limit */
1302 		if (++errcnt > MAX_RECOVERR) {
1303 			done = TRUE;
1304 			(void) fprintf(errfp, "CD audio: %s\n",
1305 				       app_data.str_maxerr);
1306 		}
1307 
1308 		errblk = s->curpos_tot.addr;
1309 
1310 		if (!done && fbioc_play_recov(errblk, TRUE)) {
1311 			in_fbioc_get_playstatus = FALSE;
1312 			return TRUE;
1313 		}
1314 
1315 		/*FALLTHROUGH*/
1316 
1317 	case CDSTAT_COMPLETED:
1318 	case CDSTAT_NOSTATUS:
1319 	default:
1320 		if (cdstat.status == CDSTAT_NOSTATUS && nostatcnt++ < 20) {
1321 			/* Allow 20 occurrences of nostatus, then stop */
1322 			done = FALSE;
1323 			break;
1324 		}
1325 		nostatcnt = 0;
1326 		done = TRUE;
1327 
1328 		if (!fbioc_fake_stop)
1329 			fbioc_playing = FALSE;
1330 
1331 		fbioc_fake_stop = FALSE;
1332 
1333 		switch (s->mode) {
1334 		case MOD_SAMPLE:
1335 			done = !fbioc_run_sample(s);
1336 			break;
1337 
1338 		case MOD_PLAY:
1339 		case MOD_PAUSE:
1340 			s->curpos_trk.addr = 0;
1341 			s->curpos_trk.min = 0;
1342 			s->curpos_trk.sec = 0;
1343 			s->curpos_trk.frame = 0;
1344 
1345 			if (s->shuffle || s->program)
1346 				done = !fbioc_run_prog(s);
1347 
1348 			if (s->repeat)
1349 				done = !fbioc_run_repeat(s);
1350 
1351 			if (s->repeat && s->segplay == SEGP_AB)
1352 				done = !fbioc_run_ab(s);
1353 
1354 			break;
1355 		}
1356 
1357 		break;
1358 	}
1359 
1360 	if (done) {
1361 		/* Reset states */
1362 		di_reset_curstat(s, FALSE, FALSE);
1363 		s->mode = MOD_STOP;
1364 
1365 		/* Cancel a->? if the user didn't select an end point */
1366 		if (s->segplay == SEGP_A) {
1367 			s->segplay = SEGP_NONE;
1368 			DPY_PROGMODE(s, FALSE);
1369 		}
1370 
1371 		fbioc_sav_end_addr = 0;
1372 		fbioc_sav_end_msf.min = fbioc_sav_end_msf.sec =
1373 			fbioc_sav_end_msf.frame = 0;
1374 		fbioc_sav_end_fmt = 0;
1375 		errcnt = 0;
1376 		errblk = 0;
1377 		s->rptcnt = 0;
1378 		DPY_ALL(s);
1379 
1380 		if (app_data.done_eject) {
1381 			/* Eject the disc */
1382 			fbioc_load_eject(s);
1383 		}
1384 		else {
1385 			/* Spin down the disc */
1386 			(void) fbioc_start_stop(FALSE, FALSE);
1387 		}
1388 		if (app_data.done_exit) {
1389 			/* Exit */
1390 			di_clinfo->quit(s);
1391 		}
1392 
1393 		in_fbioc_get_playstatus = FALSE;
1394 		return FALSE;
1395 	}
1396 
1397 	in_fbioc_get_playstatus = FALSE;
1398 	return TRUE;
1399 }
1400 
1401 
1402 /*
1403  * fbioc_cfg_vol
1404  *	Audio volume control function
1405  *
1406  * Args:
1407  *	vol - Logical volume value to set to
1408  *	s - Pointer to the curstat_t structure
1409  *	query - If TRUE, query current volume only
1410  *
1411  * Return:
1412  *	The current logical volume value, or -1 on failure.
1413  */
1414 STATIC int
fbioc_cfg_vol(int vol,curstat_t * s,bool_t query)1415 fbioc_cfg_vol(int vol, curstat_t *s, bool_t query)
1416 {
1417 	int		vol1,
1418 			vol2;
1419 	struct ioc_vol	volctrl;
1420 	static bool_t	first = TRUE;
1421 
1422 	if (!app_data.mselvol_supp)
1423 		return 0;
1424 
1425 	if (s->mode == MOD_BUSY)
1426 		return -1;
1427 
1428 	if (PLAYMODE_IS_CDDA(app_data.play_mode))
1429 		return (cdda_vol(fbioc_devp, s, vol, query));
1430 
1431 	(void) memset((byte_t *) &volctrl, 0, sizeof(struct ioc_vol));
1432 
1433 	if (query) {
1434 		if (first) {
1435 			first = FALSE;
1436 
1437 			/* Try using the CDIOCGETVOL ioctl */
1438 			if (fbioc_send(DI_ROLE_MAIN, CDIOCGETVOL,
1439 				       &volctrl, FALSE)) {
1440 				DBGDUMP(DBG_DEVIO)("cdrom_volctrl data bytes",
1441 					(byte_t *) &volctrl,
1442 					sizeof(struct ioc_vol));
1443 				vol1 = util_untaper_vol(
1444 				    util_unscale_vol((int) volctrl.vol[0])
1445 				);
1446 				vol2 = util_untaper_vol(
1447 				    util_unscale_vol((int) volctrl.vol[1])
1448 				);
1449 
1450 				if (vol1 == vol2) {
1451 					s->level_left = s->level_right = 100;
1452 					vol = vol1;
1453 				}
1454 				else if (vol1 > vol2) {
1455 					s->level_left = 100;
1456 					s->level_right =
1457 						(byte_t)((vol2 * 100) / vol1);
1458 					vol = vol1;
1459 				}
1460 				else {
1461 					s->level_left =
1462 						(byte_t) ((vol1 * 100) / vol2);
1463 					s->level_right = 100;
1464 					vol = vol2;
1465 				}
1466 
1467 				return (vol);
1468 			}
1469 
1470 			/* There is no way to read volume setting via
1471 			 * CDROM ioctl.  Force the setting to maximum.
1472 			 */
1473 			vol = 100;
1474 			s->level_left = s->level_right = 100;
1475 
1476 			(void) fbioc_cfg_vol(vol, s, FALSE);
1477 		}
1478 		return (vol);
1479 	}
1480 	else {
1481 		volctrl.vol[0] = util_scale_vol(
1482 			util_taper_vol(vol * (int) s->level_left / 100)
1483 		);
1484 		volctrl.vol[1] = util_scale_vol(
1485 			util_taper_vol(vol * (int) s->level_right / 100)
1486 		);
1487 
1488 		DBGDUMP(DBG_DEVIO)("ioc_vol data bytes", (byte_t *) &volctrl,
1489 			sizeof(struct ioc_vol));
1490 
1491 		if (fbioc_send(DI_ROLE_MAIN, CDIOCSETVOL, &volctrl, TRUE))
1492 			return (vol);
1493 		else if (volctrl.vol[0] != volctrl.vol[1]) {
1494 			/* Set the balance to the center
1495 			 * and retry.
1496 			 */
1497 			volctrl.vol[0] = volctrl.vol[1] =
1498 				util_scale_vol(util_taper_vol(vol));
1499 
1500 			DBGDUMP(DBG_DEVIO)("ioc_vol data bytes",
1501 				(byte_t *) &volctrl, sizeof(struct ioc_vol));
1502 
1503 			if (fbioc_send(DI_ROLE_MAIN, CDIOCSETVOL,
1504 				       &volctrl, TRUE)) {
1505 				/* Success: Warp balance control */
1506 				s->level_left = s->level_right = 100;
1507 				SET_BAL_SLIDER(0);
1508 
1509 				return (vol);
1510 			}
1511 
1512 			/* Still failed: just drop through */
1513 		}
1514 	}
1515 
1516 	return -1;
1517 }
1518 
1519 
1520 /*
1521  * fbioc_vendor_model
1522  *	Query and update CD drive vendor/model/revision information
1523  *
1524  * Args:
1525  *	s - Pointer to the curstat_t structure
1526  *
1527  * Return:
1528  *	Nothing.
1529  */
1530 STATIC void
fbioc_vendor_model(curstat_t * s)1531 fbioc_vendor_model(curstat_t *s)
1532 {
1533 	/*
1534 	 * There is currently no way to get this info,
1535 	 * so just fill in some default info.
1536 	 */
1537 	(void) strcpy(s->vendor, "standard");
1538 	(void) strcpy(s->prod, "CD-ROM drive");
1539 	s->revnum[0] = '\0';
1540 }
1541 
1542 
1543 /*
1544  * fbioc_fix_toc
1545  *	CD Table Of Contents post-processing function.  This is to patch
1546  *	the end-of-audio position to handle "enhanced CD" or "CD Extra"
1547  *	multisession CDs.
1548  *
1549  * Args:
1550  *	s - Pointer to the curstat_t structure
1551  *
1552  * Return:
1553  *	Nothing.
1554  */
1555 STATIC void
fbioc_fix_toc(curstat_t * s)1556 fbioc_fix_toc(curstat_t *s)
1557 {
1558 	int	i;
1559 
1560 	/*
1561 	 * Set the end-of-audio to the first data track after the first
1562 	 * track, minus 02:32:00, if applicable.  The 02:32:00 is the
1563 	 * inter-session lead-out and lead-in time plus the 2-second
1564 	 * pre-gap for the last session.
1565 	 */
1566 	for (i = 1; i < (int) s->tot_trks; i++) {
1567 		if (s->trkinfo[i].type == TYP_DATA) {
1568 			s->discpos_tot.addr = s->trkinfo[i].addr;
1569 			s->discpos_tot.addr -= 11400;
1570 			util_blktomsf(
1571 				s->discpos_tot.addr,
1572 				&s->discpos_tot.min,
1573 				&s->discpos_tot.sec,
1574 				&s->discpos_tot.frame,
1575 				MSF_OFFSET
1576 			);
1577 			break;
1578 		}
1579 	}
1580 }
1581 
1582 
1583 /*
1584  * fbioc_get_toc
1585  *	Query and update the CD Table Of Contents
1586  *
1587  * Args:
1588  *	s - Pointer to the curstat_t structure
1589  *
1590  * Return:
1591  *	TRUE - success
1592  *	FALSE - failure
1593  */
1594 STATIC bool_t
fbioc_get_toc(curstat_t * s)1595 fbioc_get_toc(curstat_t *s)
1596 {
1597 	struct ioc_toc_header	h;
1598 	struct cd_toc_entry	*e,
1599 				*p;
1600 	int			i,
1601 				ntrks;
1602 
1603 	(void) memset((byte_t *) &h, 0, sizeof(struct ioc_toc_header));
1604 	if (!fbioc_rdtoc(&h, &e, 0, !app_data.toc_lba))
1605 		return FALSE;
1606 
1607 	/* Fill curstat structure with TOC data */
1608 	s->first_trk = h.starting_track;
1609 	ntrks = (int) (h.ending_track - h.starting_track) + 1;
1610 
1611 	p = e;
1612 
1613 	for (i = 0; i <= ntrks; i++) {
1614 		s->trkinfo[i].trkno = p->track;
1615 		s->trkinfo[i].type =
1616 			(p->control & 0x04) ? TYP_DATA : TYP_AUDIO;
1617 
1618 		if (app_data.toc_lba) {
1619 			/* LBA mode */
1620 #ifdef NETBSD_OLDIOC
1621 			s->trkinfo[i].addr = util_xlate_blk((sword32_t)
1622 				(p->addr[3] << 24) | (p->addr[2] << 16) |
1623 				(p->addr[2] << 8)  | p->addr[0]
1624 			);
1625 #else
1626 			s->trkinfo[i].addr = util_xlate_blk((sword32_t)
1627 				util_bswap32(p->addr.lba)
1628 			);
1629 #endif
1630 			util_blktomsf(
1631 				s->trkinfo[i].addr,
1632 				&s->trkinfo[i].min,
1633 				&s->trkinfo[i].sec,
1634 				&s->trkinfo[i].frame,
1635 				MSF_OFFSET
1636 			);
1637 		}
1638 		else {
1639 			/* MSF mode */
1640 #ifdef NETBSD_OLDIOC
1641 			s->trkinfo[i].min = p->addr[1];
1642 			s->trkinfo[i].sec = p->addr[2];
1643 			s->trkinfo[i].frame = p->addr[3];
1644 #else
1645 			s->trkinfo[i].min = p->addr.msf.minute;
1646 			s->trkinfo[i].sec = p->addr.msf.second;
1647 			s->trkinfo[i].frame = p->addr.msf.frame;
1648 #endif
1649 			util_msftoblk(
1650 				s->trkinfo[i].min,
1651 				s->trkinfo[i].sec,
1652 				s->trkinfo[i].frame,
1653 				&s->trkinfo[i].addr,
1654 				MSF_OFFSET
1655 			);
1656 		}
1657 
1658 		if (p->track == LEAD_OUT_TRACK || i == (MAXTRACK - 1)) {
1659 			s->discpos_tot.min = s->trkinfo[i].min;
1660 			s->discpos_tot.sec = s->trkinfo[i].sec;
1661 			s->discpos_tot.frame = s->trkinfo[i].frame;
1662 			s->tot_trks = (byte_t) i;
1663 			s->discpos_tot.addr = s->trkinfo[i].addr;
1664 			s->last_trk = s->trkinfo[i-1].trkno;
1665 
1666 			break;
1667 		}
1668 
1669 		p++;
1670 	}
1671 
1672 	MEM_FREE(e);
1673 
1674 	fbioc_fix_toc(s);
1675 	return TRUE;
1676 }
1677 
1678 
1679 /*
1680  * fbioc_start_stat_poll
1681  *	Start polling the drive for current playback status
1682  *
1683  * Args:
1684  *	s - Pointer to the curstat_t structure
1685  *
1686  * Return:
1687  *	Nothing.
1688  */
1689 STATIC void
fbioc_start_stat_poll(curstat_t * s)1690 fbioc_start_stat_poll(curstat_t *s)
1691 {
1692 	/* Start poll timer */
1693 	if (di_clinfo->timeout != NULL) {
1694 		fbioc_stat_id = di_clinfo->timeout(
1695 			fbioc_stat_interval,
1696 			fbioc_stat_poll,
1697 			(byte_t *) s
1698 		);
1699 
1700 		if (fbioc_stat_id != 0)
1701 			fbioc_stat_polling = TRUE;
1702 	}
1703 }
1704 
1705 
1706 /*
1707  * fbioc_stop_stat_poll
1708  *	Stop polling the drive for current playback status
1709  *
1710  * Args:
1711  *	Nothing.
1712  *
1713  * Return:
1714  *	Nothing.
1715  */
1716 STATIC void
fbioc_stop_stat_poll(void)1717 fbioc_stop_stat_poll(void)
1718 {
1719 	if (fbioc_stat_polling) {
1720 		/* Stop poll timer */
1721 		if (di_clinfo->untimeout != NULL)
1722 			di_clinfo->untimeout(fbioc_stat_id);
1723 
1724 		fbioc_stat_polling = FALSE;
1725 	}
1726 }
1727 
1728 
1729 /*
1730  * fbioc_start_insert_poll
1731  *	Start polling the drive for disc insertion
1732  *
1733  * Args:
1734  *	s - Pointer to the curstat_t structure
1735  *
1736  * Return:
1737  *	Nothing.
1738  */
1739 STATIC void
fbioc_start_insert_poll(curstat_t * s)1740 fbioc_start_insert_poll(curstat_t *s)
1741 {
1742 	int	delay;
1743 	bool_t	first = TRUE;
1744 
1745 	if (fbioc_insert_polling || app_data.ins_disable ||
1746 	    (s->mode != MOD_BUSY && s->mode != MOD_NODISC))
1747 		return;
1748 
1749 	if (app_data.numdiscs > 1 && app_data.multi_play)
1750 		fbioc_ins_interval =
1751 			app_data.ins_interval / app_data.numdiscs;
1752 	else
1753 		fbioc_ins_interval = app_data.ins_interval;
1754 
1755 	if (fbioc_ins_interval < 500)
1756 		fbioc_ins_interval = 500;
1757 
1758 	if (first) {
1759 		first = FALSE;
1760 		delay = 50;
1761 	}
1762 	else
1763 		delay = fbioc_ins_interval;
1764 
1765 	/* Start poll timer */
1766 	if (di_clinfo->timeout != NULL) {
1767 		fbioc_insert_id = di_clinfo->timeout(
1768 			delay,
1769 			fbioc_insert_poll,
1770 			(byte_t *) s
1771 		);
1772 
1773 		if (fbioc_insert_id != 0)
1774 			fbioc_insert_polling = TRUE;
1775 	}
1776 }
1777 
1778 
1779 /*
1780  * fbioc_stat_poll
1781  *	The playback status polling function
1782  *
1783  * Args:
1784  *	s - Pointer to the curstat_t structure
1785  *
1786  * Return:
1787  *	Nothing.
1788  */
1789 STATIC void
fbioc_stat_poll(curstat_t * s)1790 fbioc_stat_poll(curstat_t *s)
1791 {
1792 	if (!fbioc_stat_polling)
1793 		return;
1794 
1795 	/* Get current audio playback status */
1796 	if (fbioc_get_playstatus(s)) {
1797 		/* Register next poll interval */
1798 		if (di_clinfo->timeout != NULL) {
1799 			fbioc_stat_id = di_clinfo->timeout(
1800 				fbioc_stat_interval,
1801 				fbioc_stat_poll,
1802 				(byte_t *) s
1803 			);
1804 		}
1805 	}
1806 	else
1807 		fbioc_stat_polling = FALSE;
1808 }
1809 
1810 
1811 /*
1812  * fbioc_insert_poll
1813  *	The disc insertion polling function
1814  *
1815  * Args:
1816  *	s - Pointer to the curstat_t structure
1817  *
1818  * Return:
1819  *	Nothing.
1820  */
1821 STATIC void
fbioc_insert_poll(curstat_t * s)1822 fbioc_insert_poll(curstat_t *s)
1823 {
1824 	/* Check to see if a disc is inserted */
1825 	if (!fbioc_disc_ready(s)) {
1826 		/* Register next poll interval */
1827 		if (di_clinfo->timeout != NULL) {
1828 			fbioc_insert_id = di_clinfo->timeout(
1829 				fbioc_ins_interval,
1830 				fbioc_insert_poll,
1831 				(byte_t *) s
1832 			);
1833 		}
1834 	}
1835 	else
1836 		fbioc_insert_polling = FALSE;
1837 }
1838 
1839 
1840 /*
1841  * fbioc_disc_ready
1842  *	Check if the disc is loaded and ready for use, and update
1843  *	curstat table.
1844  *
1845  * Args:
1846  *	s - Pointer to the curstat_t structure
1847  *
1848  * Return:
1849  *	TRUE - Disc is ready
1850  *	FALSE - Disc is not ready
1851  */
1852 STATIC bool_t
fbioc_disc_ready(curstat_t * s)1853 fbioc_disc_ready(curstat_t *s)
1854 {
1855 	bool_t		no_disc;
1856 	static bool_t	first_open = TRUE,
1857 			in_fbioc_disc_ready = FALSE;
1858 
1859 	/* Lock this routine from multiple entry */
1860 	if (in_fbioc_disc_ready)
1861 		return TRUE;
1862 
1863 	in_fbioc_disc_ready = TRUE;
1864 
1865 	/* If device has not been opened, attempt to open it */
1866 	if (fbioc_not_open) {
1867 		/* Check for another copy of the CD player running on
1868 		 * the specified device.
1869 		 */
1870 		if (!s->devlocked && !di_devlock(s, app_data.device)) {
1871 			s->mode = MOD_BUSY;
1872 			DPY_TIME(s, FALSE);
1873 			fbioc_start_insert_poll(s);
1874 			in_fbioc_disc_ready = FALSE;
1875 			return FALSE;
1876 		}
1877 
1878 		s->devlocked = TRUE;
1879 		s->mode = MOD_NODISC;
1880 
1881 		if (fbioc_open(s->curdev)) {
1882 			fbioc_not_open = FALSE;
1883 
1884 			if (!fbioc_is_enabled(DI_ROLE_MAIN)) {
1885 				/* Enable device for I/O */
1886 				fbioc_enable(DI_ROLE_MAIN);
1887 			}
1888 
1889 			/* Check if a disc is loaded and ready */
1890 			no_disc = !fbioc_disc_present(first_open);
1891 		}
1892 		else {
1893 			DBGPRN(DBG_DEVIO)(errfp,
1894 				"Open of %s failed\n", s->curdev);
1895 			no_disc = TRUE;
1896 		}
1897 	}
1898 	else {
1899 		/* Just return success if we're playing CDDA */
1900 		if (!fbioc_is_enabled(DI_ROLE_MAIN)) {
1901 			no_disc = FALSE;
1902 		}
1903 		else if ((no_disc = !fbioc_disc_present(FALSE)) == TRUE) {
1904 			/* The disc was manually ejected */
1905 			s->mode = MOD_NODISC;
1906 			di_clear_cdinfo(s, FALSE);
1907 		}
1908 	}
1909 
1910 	if (!no_disc) {
1911 		if (first_open) {
1912 			first_open = FALSE;
1913 
1914 			/* Fill in inquiry data */
1915 			fbioc_vendor_model(s);
1916 
1917 			/* Initialize volume/balance/routing controls */
1918 			fbioc_init_vol(s, TRUE);
1919 		}
1920 		else {
1921 			/* Force to current settings */
1922 			(void) fbioc_cfg_vol(s->level, s, FALSE);
1923 
1924 			/* Set up channel routing */
1925 			fbioc_route(s);
1926 		}
1927 	}
1928 
1929 	/* Read disc table of contents if a new disc was detected */
1930 	if (fbioc_not_open || no_disc ||
1931 	    (s->mode == MOD_NODISC && !fbioc_get_toc(s))) {
1932 		if (fbioc_devp != NULL && app_data.eject_close) {
1933 			fbioc_close();
1934 			fbioc_not_open = TRUE;
1935 		}
1936 
1937 		di_reset_curstat(s, TRUE, TRUE);
1938 		DPY_ALL(s);
1939 
1940 		fbioc_start_insert_poll(s);
1941 		in_fbioc_disc_ready = FALSE;
1942 		return FALSE;
1943 	}
1944 
1945 	if (s->mode != MOD_NODISC) {
1946 		in_fbioc_disc_ready = FALSE;
1947 		return TRUE;
1948 	}
1949 
1950 	/* Load saved track program, if any */
1951 	PROGGET(s);
1952 
1953 	s->mode = MOD_STOP;
1954 	DPY_ALL(s);
1955 
1956 	/* Set caddy lock configuration */
1957 	if (app_data.caddylock_supp)
1958 		fbioc_lock(s, app_data.caddy_lock);
1959 
1960 	if (app_data.load_play) {
1961 		/* Start auto-play */
1962 		if (!fbioc_override_ap)
1963 			fbioc_play_pause(s);
1964 	}
1965 	else if (app_data.load_spindown) {
1966 		/* Spin down disc in case the user isn't going to
1967 		 * play anything for a while.  This reduces wear and
1968 		 * tear on the drive.
1969 		 */
1970 		(void) fbioc_start_stop(FALSE, FALSE);
1971 	}
1972 	else {
1973 		switch (fbioc_tst_status) {
1974 		case MOD_PLAY:
1975 		case MOD_PAUSE:
1976 			/* Drive is current playing audio or paused:
1977 			 * act appropriately.
1978 			 */
1979 			s->mode = fbioc_tst_status;
1980 			(void) fbioc_get_playstatus(s);
1981 			DPY_ALL(s);
1982 			if (s->mode == MOD_PLAY)
1983 				fbioc_start_stat_poll(s);
1984 			break;
1985 		default:
1986 			/* Drive is stopped: do nothing */
1987 			break;
1988 		}
1989 	}
1990 
1991 	in_fbioc_disc_ready = FALSE;
1992 
1993 	/* Load CD information for this disc.
1994 	 * This operation has to be done outside the scope of
1995 	 * in_fbioc_disc_ready because it may recurse
1996 	 * back into this function.
1997 	 */
1998 	(void) di_get_cdinfo(s);
1999 
2000 	return TRUE;
2001 }
2002 
2003 
2004 /*
2005  * fbioc_run_rew
2006  *	Run search-rewind operation
2007  *
2008  * Args:
2009  *	s - Pointer to the curstat_t structure
2010  *
2011  * Return:
2012  *	Nothing.
2013  */
2014 STATIC void
fbioc_run_rew(curstat_t * s)2015 fbioc_run_rew(curstat_t *s)
2016 {
2017 	int			i,
2018 				skip_blks;
2019 	sword32_t		addr,
2020 				end_addr;
2021 	msf_t			smsf,
2022 				emsf;
2023 	static sword32_t	start_addr,
2024 				seq;
2025 
2026 	/* Find out where we are */
2027 	if (!fbioc_get_playstatus(s)) {
2028 		DO_BEEP();
2029 		return;
2030 	}
2031 
2032 	skip_blks = app_data.skip_blks;
2033 	addr = s->curpos_tot.addr;
2034 
2035 	if (fbioc_start_search) {
2036 		fbioc_start_search = FALSE;
2037 		seq = 0;
2038 		i = (int) (addr - skip_blks);
2039 	}
2040 	else {
2041 		if (app_data.skip_spdup > 0 && seq > app_data.skip_spdup)
2042 			/* Speed up search */
2043 			skip_blks *= 3;
2044 
2045 		i = (int) (start_addr - skip_blks);
2046 	}
2047 
2048 	start_addr = (sword32_t) ((i > di_clip_frames) ? i : di_clip_frames);
2049 
2050 	seq++;
2051 
2052 	if (s->shuffle || s->program) {
2053 		if ((i = di_curtrk_pos(s)) < 0)
2054 			i = 0;
2055 
2056 		if (start_addr < s->trkinfo[i].addr)
2057 			start_addr = s->trkinfo[i].addr;
2058 	}
2059 	else if (s->segplay == SEGP_AB && start_addr < s->bp_startpos_tot.addr)
2060 		start_addr = s->bp_startpos_tot.addr;
2061 
2062 	end_addr = start_addr + MAX_SRCH_BLKS;
2063 
2064 	util_blktomsf(
2065 		start_addr,
2066 		&smsf.min,
2067 		&smsf.sec,
2068 		&smsf.frame,
2069 		MSF_OFFSET
2070 	);
2071 	util_blktomsf(
2072 		end_addr,
2073 		&emsf.min,
2074 		&emsf.sec,
2075 		&emsf.frame,
2076 		MSF_OFFSET
2077 	);
2078 
2079 	/* Play next search interval */
2080 	(void) fbioc_do_playaudio(
2081 		ADDR_BLK | ADDR_MSF | ADDR_OPTEND,
2082 		start_addr, end_addr,
2083 		&smsf, &emsf,
2084 		0, 0
2085 	);
2086 
2087 	if (di_clinfo->timeout != NULL) {
2088 		fbioc_search_id = di_clinfo->timeout(
2089 			app_data.skip_pause,
2090 			fbioc_run_rew,
2091 			(byte_t *) s
2092 		);
2093 	}
2094 }
2095 
2096 
2097 /*
2098  * fbioc_stop_rew
2099  *	Stop search-rewind operation
2100  *
2101  * Args:
2102  *	s - Pointer to the curstat_t structure
2103  *
2104  * Return:
2105  *	Nothing.
2106  */
2107 /*ARGSUSED*/
2108 STATIC void
fbioc_stop_rew(curstat_t * s)2109 fbioc_stop_rew(curstat_t *s)
2110 {
2111 	if (di_clinfo->untimeout != NULL)
2112 		di_clinfo->untimeout(fbioc_search_id);
2113 }
2114 
2115 
2116 /*
2117  * fbioc_run_ff
2118  *	Run search-fast-forward operation
2119  *
2120  * Args:
2121  *	s - Pointer to the curstat_t structure
2122  *
2123  * Return:
2124  *	Nothing.
2125  */
2126 STATIC void
fbioc_run_ff(curstat_t * s)2127 fbioc_run_ff(curstat_t *s)
2128 {
2129 	int			i,
2130 				skip_blks;
2131 	sword32_t		addr,
2132 				end_addr;
2133 	msf_t			smsf,
2134 				emsf;
2135 	static sword32_t	start_addr,
2136 				seq;
2137 
2138 	/* Find out where we are */
2139 	if (!fbioc_get_playstatus(s)) {
2140 		DO_BEEP();
2141 		return;
2142 	}
2143 
2144 	skip_blks = app_data.skip_blks;
2145 	addr = s->curpos_tot.addr;
2146 
2147 	if (fbioc_start_search) {
2148 		fbioc_start_search = FALSE;
2149 		seq = 0;
2150 		start_addr = addr + skip_blks;
2151 	}
2152 	else {
2153 		if (app_data.skip_spdup > 0 && seq > app_data.skip_spdup)
2154 			/* Speed up search */
2155 			skip_blks *= 3;
2156 
2157 		start_addr += skip_blks;
2158 	}
2159 
2160 	seq++;
2161 
2162 	if (s->shuffle || s->program) {
2163 		if ((i = di_curtrk_pos(s)) < 0)
2164 			i = s->tot_trks - 1;
2165 		else if (s->cur_idx == 0)
2166 			/* We're in the lead-in: consider this to be
2167 			 * within the previous track.
2168 			 */
2169 			i--;
2170 	}
2171 	else
2172 		i = s->tot_trks - 1;
2173 
2174 	end_addr = start_addr + MAX_SRCH_BLKS;
2175 
2176 	if (end_addr >= s->trkinfo[i+1].addr) {
2177 		end_addr = s->trkinfo[i+1].addr;
2178 		start_addr = end_addr - skip_blks;
2179 	}
2180 
2181 	if (s->segplay == SEGP_AB && end_addr > s->bp_endpos_tot.addr) {
2182 		end_addr = s->bp_endpos_tot.addr;
2183 		start_addr = end_addr - skip_blks;
2184 	}
2185 
2186 	util_blktomsf(
2187 		start_addr,
2188 		&smsf.min,
2189 		&smsf.sec,
2190 		&smsf.frame,
2191 		MSF_OFFSET
2192 	);
2193 	util_blktomsf(
2194 		end_addr,
2195 		&emsf.min,
2196 		&emsf.sec,
2197 		&emsf.frame,
2198 		MSF_OFFSET
2199 	);
2200 
2201 	/* Play next search interval */
2202 	(void) fbioc_do_playaudio(
2203 		ADDR_BLK | ADDR_MSF | ADDR_OPTEND,
2204 		start_addr, end_addr,
2205 		&smsf, &emsf,
2206 		0, 0
2207 	);
2208 
2209 	if (di_clinfo->timeout != NULL) {
2210 		fbioc_search_id = di_clinfo->timeout(
2211 			app_data.skip_pause,
2212 			fbioc_run_ff,
2213 			(byte_t *) s
2214 		);
2215 	}
2216 }
2217 
2218 
2219 /*
2220  * fbioc_stop_ff
2221  *	Stop search-fast-forward operation
2222  *
2223  * Args:
2224  *	s - Pointer to the curstat_t structure
2225  *
2226  * Return:
2227  *	Nothing.
2228  */
2229 /*ARGSUSED*/
2230 STATIC void
fbioc_stop_ff(curstat_t * s)2231 fbioc_stop_ff(curstat_t *s)
2232 {
2233 	if (di_clinfo->untimeout != NULL)
2234 		di_clinfo->untimeout(fbioc_search_id);
2235 }
2236 
2237 
2238 /*
2239  * fbioc_run_ab
2240  *	Run a->b segment play operation
2241  *
2242  * Args:
2243  *	s - Pointer to the curstat_t structure
2244  *
2245  * Return:
2246  *	TRUE - success
2247  *	FALSE - failure
2248  */
2249 /*ARGSUSED*/
2250 STATIC bool_t
fbioc_run_ab(curstat_t * s)2251 fbioc_run_ab(curstat_t *s)
2252 {
2253 	msf_t	start_msf,
2254 		end_msf;
2255 
2256 	if ((s->bp_startpos_tot.addr + app_data.min_playblks) >=
2257 	    s->bp_endpos_tot.addr) {
2258 		DO_BEEP();
2259 		return FALSE;
2260 	}
2261 
2262 	start_msf.min = s->bp_startpos_tot.min;
2263 	start_msf.sec = s->bp_startpos_tot.sec;
2264 	start_msf.frame = s->bp_startpos_tot.frame;
2265 	end_msf.min = s->bp_endpos_tot.min;
2266 	end_msf.sec = s->bp_endpos_tot.sec;
2267 	end_msf.frame = s->bp_endpos_tot.frame;
2268 	s->mode = MOD_PLAY;
2269 	DPY_ALL(s);
2270 	fbioc_start_stat_poll(s);
2271 
2272 	return (
2273 		fbioc_do_playaudio(
2274 			ADDR_BLK | ADDR_MSF,
2275 			s->bp_startpos_tot.addr, s->bp_endpos_tot.addr,
2276 			&start_msf, &end_msf,
2277 			0, 0
2278 		)
2279 	);
2280 }
2281 
2282 
2283 /*
2284  * fbioc_run_sample
2285  *	Run sample play operation
2286  *
2287  * Args:
2288  *	s - Pointer to the curstat_t structure
2289  *
2290  * Return:
2291  *	TRUE - success
2292  *	FALSE - failure
2293  */
2294 STATIC bool_t
fbioc_run_sample(curstat_t * s)2295 fbioc_run_sample(curstat_t *s)
2296 {
2297 	sword32_t	saddr,
2298 			eaddr;
2299 	msf_t		smsf,
2300 			emsf;
2301 
2302 	if (fbioc_next_sam < s->tot_trks) {
2303 		saddr = s->trkinfo[fbioc_next_sam].addr;
2304 		eaddr = saddr + app_data.sample_blks,
2305 
2306 		util_blktomsf(
2307 			saddr,
2308 			&smsf.min,
2309 			&smsf.sec,
2310 			&smsf.frame,
2311 			MSF_OFFSET
2312 		);
2313 		util_blktomsf(
2314 			eaddr,
2315 			&emsf.min,
2316 			&emsf.sec,
2317 			&emsf.frame,
2318 			MSF_OFFSET
2319 		);
2320 
2321 		if (s->trkinfo[fbioc_next_sam].type != TYP_AUDIO ||
2322 		    fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
2323 				       saddr, eaddr, &smsf, &emsf, 0, 0)) {
2324 			fbioc_next_sam++;
2325 			return TRUE;
2326 		}
2327 	}
2328 
2329 	fbioc_next_sam = 0;
2330 	return FALSE;
2331 }
2332 
2333 
2334 /*
2335  * fbioc_run_prog
2336  *	Run program/shuffle play operation
2337  *
2338  * Args:
2339  *	s - Pointer to the curstat_t structure
2340  *
2341  * Return:
2342  *	TRUE - success
2343  *	FALSE - failure
2344  */
2345 STATIC bool_t
fbioc_run_prog(curstat_t * s)2346 fbioc_run_prog(curstat_t *s)
2347 {
2348 	sword32_t	i,
2349 			start_addr,
2350 			end_addr;
2351 	msf_t		start_msf,
2352 			end_msf;
2353 	bool_t		hasaudio,
2354 			ret;
2355 
2356 	if (!s->shuffle && !s->program)
2357 		return FALSE;
2358 
2359 	if (fbioc_new_progshuf) {
2360 		fbioc_new_progshuf = FALSE;
2361 
2362 		if (s->shuffle)
2363 			/* New shuffle sequence needed */
2364 			di_reset_shuffle(s);
2365 		else
2366 			/* Program play: simply reset the count */
2367 			s->prog_cnt = 0;
2368 
2369 		/* Do not allow a program that contains only data tracks */
2370 		hasaudio = FALSE;
2371 		for (i = 0; i < (int) s->prog_tot; i++) {
2372 			if (s->trkinfo[s->trkinfo[i].playorder].type ==
2373 			    TYP_AUDIO) {
2374 				hasaudio = TRUE;
2375 				break;
2376 			}
2377 		}
2378 
2379 		if (!hasaudio) {
2380 			DO_BEEP();
2381 			return FALSE;
2382 		}
2383 	}
2384 
2385 	if (s->prog_cnt >= s->prog_tot)
2386 		/* Done with program/shuffle play cycle */
2387 		return FALSE;
2388 
2389 	if ((i = di_curprog_pos(s)) < 0)
2390 		return FALSE;
2391 
2392 	if (s->trkinfo[i].trkno == LEAD_OUT_TRACK)
2393 		return FALSE;
2394 
2395 	s->prog_cnt++;
2396 	s->cur_trk = s->trkinfo[i].trkno;
2397 	s->cur_idx = 1;
2398 
2399 	start_addr = s->trkinfo[i].addr + s->curpos_trk.addr;
2400 	util_blktomsf(
2401 		start_addr,
2402 		&s->curpos_tot.min,
2403 		&s->curpos_tot.sec,
2404 		&s->curpos_tot.frame,
2405 		MSF_OFFSET
2406 	);
2407 	start_msf.min = s->curpos_tot.min;
2408 	start_msf.sec = s->curpos_tot.sec;
2409 	start_msf.frame = s->curpos_tot.frame;
2410 
2411 	end_addr = s->trkinfo[i+1].addr;
2412 	end_msf.min = s->trkinfo[i+1].min;
2413 	end_msf.sec = s->trkinfo[i+1].sec;
2414 	end_msf.frame = s->trkinfo[i+1].frame;
2415 
2416 	s->curpos_tot.addr = start_addr;
2417 
2418 	if (s->mode != MOD_PAUSE)
2419 		s->mode = MOD_PLAY;
2420 
2421 	DPY_ALL(s);
2422 
2423 	if (s->trkinfo[i].type == TYP_DATA)
2424 		/* Data track: just fake it */
2425 		return TRUE;
2426 
2427 	ret = fbioc_do_playaudio(
2428 		ADDR_BLK | ADDR_MSF,
2429 		start_addr, end_addr,
2430 		&start_msf, &end_msf,
2431 		0, 0
2432 	);
2433 
2434 	if (s->mode == MOD_PAUSE) {
2435 		(void) fbioc_pause_resume(FALSE);
2436 
2437 		/* Restore volume */
2438 		fbioc_mute_off(s);
2439 	}
2440 
2441 	return (ret);
2442 }
2443 
2444 
2445 /*
2446  * fbioc_run_repeat
2447  *	Run repeat play operation
2448  *
2449  * Args:
2450  *	s - Pointer to the curstat_t structure
2451  *
2452  * Return:
2453  *	TRUE - success
2454  *	FALSE - failure
2455  */
2456 STATIC bool_t
fbioc_run_repeat(curstat_t * s)2457 fbioc_run_repeat(curstat_t *s)
2458 {
2459 	msf_t	start_msf,
2460 		end_msf;
2461 	bool_t	ret;
2462 
2463 	if (!s->repeat)
2464 		return FALSE;
2465 
2466 	if (s->shuffle || s->program) {
2467 		ret = TRUE;
2468 
2469 		if (s->prog_cnt < s->prog_tot)
2470 			/* Not done with program/shuffle sequence yet */
2471 			return (ret);
2472 
2473 		fbioc_new_progshuf = TRUE;
2474 		s->rptcnt++;
2475 	}
2476 	else {
2477 		s->cur_trk = s->first_trk;
2478 		s->cur_idx = 1;
2479 
2480 		s->curpos_tot.addr = 0;
2481 		s->curpos_tot.min = 0;
2482 		s->curpos_tot.sec = 0;
2483 		s->curpos_tot.frame = 0;
2484 		s->rptcnt++;
2485 		DPY_ALL(s);
2486 
2487 		start_msf.min = s->trkinfo[0].min;
2488 		start_msf.sec = s->trkinfo[0].sec;
2489 		start_msf.frame = s->trkinfo[0].frame;
2490 		end_msf.min = s->discpos_tot.min;
2491 		end_msf.sec = s->discpos_tot.sec;
2492 		end_msf.frame = s->discpos_tot.frame;
2493 
2494 		ret = fbioc_do_playaudio(
2495 			ADDR_BLK | ADDR_MSF,
2496 			s->trkinfo[0].addr, s->discpos_tot.addr,
2497 			&start_msf, &end_msf, 0, 0
2498 		);
2499 
2500 		if (s->mode == MOD_PAUSE) {
2501 			(void) fbioc_pause_resume(FALSE);
2502 
2503 			/* Restore volume */
2504 			fbioc_mute_off(s);
2505 		}
2506 
2507 	}
2508 
2509 	return (ret);
2510 }
2511 
2512 
2513 /***********************
2514  *   public routines   *
2515  ***********************/
2516 
2517 
2518 /*
2519  * fbioc_enable
2520  *	Enable device in this process for I/O
2521  *
2522  * Args:
2523  *	role - Role id for which I/O is to be enabled
2524  *
2525  * Return:
2526  *	Nothing.
2527  */
2528 void
fbioc_enable(int role)2529 fbioc_enable(int role)
2530 {
2531 	DBGPRN(DBG_DEVIO)(errfp, "Enable device: %s role: %d\n",
2532 			  fbioc_devp->path, role);
2533 	fbioc_devp->role = role;
2534 }
2535 
2536 
2537 /*
2538  * fbioc_disable
2539  *	Disable device in this process for I/O
2540  *
2541  * Args:
2542  *	role - Role id for which I/O is to be disabled
2543  *
2544  * Return:
2545  *	Nothing.
2546  */
2547 void
fbioc_disable(int role)2548 fbioc_disable(int role)
2549 {
2550 	if (fbioc_devp->role != role) {
2551 		DBGPRN(DBG_DEVIO)(errfp, "fbioc_disable: invalid role: %d\n",
2552 				  role);
2553 		return;
2554 	}
2555 
2556 	DBGPRN(DBG_DEVIO)(errfp, "Disable device: %s role: %d\n",
2557 			  fbioc_devp->path, role);
2558 	fbioc_devp->role = 0;
2559 }
2560 
2561 
2562 /*
2563  * fbioc_is_enabled
2564  *	Check whether device is enabled for I/O in this process
2565  *
2566  * Args:
2567  *	role - Role id for which to check
2568  *
2569  * Return:
2570  *	TRUE  - enabled
2571  *	FALSE - disabled
2572  */
2573 bool_t
fbioc_is_enabled(int role)2574 fbioc_is_enabled(int role)
2575 {
2576 	return ((bool_t) (fbioc_devp->role == role));
2577 }
2578 
2579 
2580 /*
2581  * fbioc_send
2582  *	Issue ioctl command.
2583  *
2584  * Args:
2585  *	role - role id for which command is to be sent
2586  *	cmd - ioctl command
2587  *	arg - ioctl argument
2588  *	prnerr - whether an error message is to be displayed if the ioctl fails
2589  *
2590  * Return:
2591  *	TRUE - ioctl successful
2592  *	FALSE - ioctl failed
2593  */
2594 bool_t
fbioc_send(int role,unsigned int cmd,void * arg,bool_t prnerr)2595 fbioc_send(int role, unsigned int cmd, void *arg, bool_t prnerr)
2596 {
2597 	int		i,
2598 			ret;
2599 	curstat_t	*s = di_clinfo->curstat_addr();
2600 
2601 	if (fbioc_devp == NULL || fbioc_devp->fd <= 0)
2602 		return FALSE;
2603 
2604 	if (fbioc_devp->role != role) {
2605 		DBGPRN(DBG_DEVIO)(errfp,
2606 			"NOTICE: fbioc_send: disabled role! (%d)\n",
2607 			role);
2608 		return FALSE;
2609 	}
2610 
2611 	if (cmd == CDIOCREADAUDIO && fbioc_use_pread) {
2612 		struct ioc_read_audio	*p = (struct ioc_read_audio *) arg;
2613 		int			offset,
2614 					nbytes;
2615 
2616 		offset = p->address.lba * CDDA_BLKSZ;
2617 		nbytes = p->nframes * CDDA_BLKSZ;
2618 
2619 		DBGPRN(DBG_DEVIO)(errfp, "\nPREAD: offset=%d nbytes=%d\n",
2620 				  offset, nbytes);
2621 
2622 		ret = pread(fbioc_devp->fd, p->buffer, nbytes, offset);
2623 		if (ret == nbytes)
2624 			return TRUE;
2625 
2626 		if (prnerr) {
2627 			(void) fprintf(errfp,
2628 				"PREAD error on %s: ret=%d, errno=%d\n",
2629 				s->curdev, ret, errno
2630 			);
2631 		}
2632 		return FALSE;
2633 	}
2634 
2635 	if (app_data.debug & DBG_DEVIO) {
2636 		for (i = 0; iname[i].name != NULL; i++) {
2637 			if (iname[i].cmd == cmd) {
2638 				(void) fprintf(errfp, "\nIOCTL: %s arg=0x%lx ",
2639 					       iname[i].name, (long) arg);
2640 				break;
2641 			}
2642 		}
2643 		if (iname[i].name == NULL)
2644 			(void) fprintf(errfp, "\nIOCTL: 0x%x arg=0x%lx ",
2645 				       cmd, (long) arg);
2646 	}
2647 
2648 	ret = ioctl(fbioc_devp->fd, cmd, arg);
2649 
2650 	DBGPRN(DBG_DEVIO)(errfp, "ret=%d\n", ret);
2651 
2652 	if (ret < 0) {
2653 		if (prnerr) {
2654 			(void) fprintf(errfp, "CD audio: ioctl error on %s: ",
2655 				       s->curdev);
2656 
2657 			for (i = 0; iname[i].name != NULL; i++) {
2658 				if (iname[i].cmd == cmd) {
2659 					(void) fprintf(errfp,
2660 						       "cmd=%s errno=%d\n",
2661 						       iname[i].name, errno);
2662 					break;
2663 				}
2664 			}
2665 			if (iname[i].name == NULL)
2666 				(void) fprintf(errfp, "cmd=0x%x errno=%d\n",
2667 					       cmd, errno);
2668 		}
2669 		return FALSE;
2670 	}
2671 
2672 	return TRUE;
2673 }
2674 
2675 
2676 /*
2677  * fbioc_init
2678  *	Top-level function to initialize the FreeBSD/NetBSD/OpenBSD
2679  *	ioctl method.
2680  *
2681  * Args:
2682  *	s - Pointer to the curstat_t structure
2683  *
2684  * Return:
2685  *	Nothing.
2686  */
2687 void
fbioc_init(curstat_t * s,di_tbl_t * dt)2688 fbioc_init(curstat_t *s, di_tbl_t *dt)
2689 {
2690 	char	*cp;
2691 	int	kver,
2692 		len;
2693 
2694 	if (app_data.di_method != DI_FBIOC)
2695 		/* FreeBSD/NetBSD/OpenBSD ioctl method not configured */
2696 		return;
2697 
2698 	/* Set the default CDDA read method based on running kernel version */
2699 	len = sizeof(kver);
2700 	if (sysctlbyname("kern.osreldate", &kver, &len, NULL, 0) < 0 ||
2701 	    kver < 501106)
2702 		fbioc_use_pread = FALSE;
2703 	else
2704 		fbioc_use_pread = TRUE;
2705 
2706 	/* Allow the user to override the default CDDA read method via the
2707 	 * CDDA_USE_PREAD environment variable
2708 	 */
2709 	if ((cp = getenv("CDDA_USE_PREAD")) != NULL)
2710 		fbioc_use_pread = (bool_t) (atoi(cp) > 0);
2711 
2712 	/* Initialize libdi calling table */
2713 	dt->load_cdtext = NULL;
2714 	dt->playmode = fbioc_playmode;
2715 	dt->check_disc = fbioc_check_disc;
2716 	dt->status_upd = fbioc_status_upd;
2717 	dt->lock = fbioc_lock;
2718 	dt->repeat = fbioc_repeat;
2719 	dt->shuffle = fbioc_shuffle;
2720 	dt->load_eject = fbioc_load_eject;
2721 	dt->ab = fbioc_ab;
2722 	dt->sample = fbioc_sample;
2723 	dt->level = fbioc_level;
2724 	dt->play_pause = fbioc_play_pause;
2725 	dt->stop = fbioc_stop;
2726 	dt->chgdisc = fbioc_chgdisc;
2727 	dt->prevtrk = fbioc_prevtrk;
2728 	dt->nexttrk = fbioc_nexttrk;
2729 	dt->previdx = fbioc_previdx;
2730 	dt->nextidx = fbioc_nextidx;
2731 	dt->rew = fbioc_rew;
2732 	dt->ff = fbioc_ff;
2733 	dt->warp = fbioc_warp;
2734 	dt->route = fbioc_route;
2735 	dt->mute_on = fbioc_mute_on;
2736 	dt->mute_off = fbioc_mute_off;
2737 	dt->cddajitter = fbioc_cddajitter;
2738 	dt->debug = fbioc_debug;
2739 	dt->start = fbioc_start;
2740 	dt->icon = fbioc_icon;
2741 	dt->halt = fbioc_halt;
2742 	dt->methodstr = fbioc_methodstr;
2743 
2744 	/* Hardwire some unsupported features */
2745 	app_data.chroute_supp = FALSE;
2746 
2747 	/* Initalize FreeBSD/NetBSD/OpenBSD ioctl method */
2748 	fbioc_stat_polling = FALSE;
2749 	fbioc_stat_interval = app_data.stat_interval;
2750 	fbioc_insert_polling = FALSE;
2751 	fbioc_next_sam = FALSE;
2752 	fbioc_new_progshuf = FALSE;
2753 	fbioc_sav_end_addr = 0;
2754 	fbioc_sav_end_msf.min = fbioc_sav_end_msf.sec =
2755 		fbioc_sav_end_msf.frame = 0;
2756 	fbioc_sav_end_fmt = 0;
2757 
2758 	/* Initialize curstat structure */
2759 	di_reset_curstat(s, TRUE, TRUE);
2760 
2761 	if (app_data.numdiscs > 1) {
2762 		/* There is currently no changer support */
2763 		DBGPRN(DBG_DEVIO)(errfp, "CD changer not supported:\n%s\n",
2764 			"Setting to single disc mode.");
2765 
2766 		app_data.numdiscs = 1;
2767 		app_data.chg_method = CHG_NONE;
2768 		app_data.multi_play = FALSE;
2769 		app_data.reverse = FALSE;
2770 		s->first_disc = s->last_disc = s->cur_disc = 1;
2771 	}
2772 
2773 #ifdef __FreeBSD__
2774 	DBGPRN(DBG_DEVIO)(errfp, "libdi: FreeBSD ioctl method\n");
2775 #endif
2776 #ifdef __NetBSD__
2777 	DBGPRN(DBG_DEVIO)(errfp, "libdi: NetBSD ioctl method\n");
2778 #endif
2779 #ifdef __OpenBSD__
2780 	DBGPRN(DBG_DEVIO)(errfp, "libdi: OpenBSD ioctl method\n");
2781 #endif
2782 }
2783 
2784 
2785 /*
2786  * fbioc_playmode
2787  *	Init/halt CDDA mode
2788  *
2789  * Args:
2790  *	s - Pointer to the curstat_t structure
2791  *
2792  * Return:
2793  *	TRUE - success
2794  *	FALSE - failure
2795  */
2796 bool_t
fbioc_playmode(curstat_t * s)2797 fbioc_playmode(curstat_t *s)
2798 {
2799 	bool_t		ret,
2800 			cdda;
2801 	static bool_t	prev_cdda = FALSE;
2802 
2803 	cdda = (bool_t) PLAYMODE_IS_CDDA(app_data.play_mode);
2804 
2805 	if (cdda == prev_cdda)
2806 		return TRUE;	/* No change */
2807 
2808 	if (cdda) {
2809 		fbioc_cdda_client.curstat_addr = di_clinfo->curstat_addr;
2810 		fbioc_cdda_client.fatal_msg = di_clinfo->fatal_msg;
2811 		fbioc_cdda_client.warning_msg = di_clinfo->warning_msg;
2812 		fbioc_cdda_client.info_msg = di_clinfo->info_msg;
2813 		fbioc_cdda_client.info2_msg = di_clinfo->info2_msg;
2814 
2815 		ret = cdda_init(s, &fbioc_cdda_client);
2816 	}
2817 	else {
2818 		cdda_halt(fbioc_devp, s);
2819 		ret = TRUE;
2820 
2821 		/* Initialize volume/balance/routing controls */
2822 		fbioc_init_vol(s, FALSE);
2823 	}
2824 
2825 	if (ret)
2826 		prev_cdda = cdda;
2827 
2828 	return (ret);
2829 }
2830 
2831 
2832 /*
2833  * fbioc_check_disc
2834  *	Check if disc is ready for use
2835  *
2836  * Args:
2837  *	s - Pointer to the curstat_t structure
2838  *
2839  * Return:
2840  *	TRUE - success
2841  *	FALSE - failure
2842  */
2843 bool_t
fbioc_check_disc(curstat_t * s)2844 fbioc_check_disc(curstat_t *s)
2845 {
2846 	return (fbioc_disc_ready(s));
2847 }
2848 
2849 
2850 /*
2851  * fbioc_status_upd
2852  *	Force update of playback status
2853  *
2854  * Args:
2855  *	s - Pointer to the curstat_t structure
2856  *
2857  * Return:
2858  *	Nothing.
2859  */
2860 void
fbioc_status_upd(curstat_t * s)2861 fbioc_status_upd(curstat_t *s)
2862 {
2863 	(void) fbioc_get_playstatus(s);
2864 }
2865 
2866 
2867 /*
2868  * fbioc_lock
2869  *	Caddy lock function
2870  *
2871  * Args:
2872  *	s - Pointer to the curstat_t structure
2873  *	enable - whether to enable/disable caddy lock
2874  *
2875  * Return:
2876  *	Nothing.
2877  */
2878 void
fbioc_lock(curstat_t * s,bool_t enable)2879 fbioc_lock(curstat_t *s, bool_t enable)
2880 {
2881 	if (s->mode == MOD_BUSY || s->mode == MOD_NODISC) {
2882 		SET_LOCK_BTN(FALSE);
2883 		return;
2884 	}
2885 	else if (s->mode != MOD_STOP) {
2886 		/* Only allow changing lock status when stopped */
2887 		DO_BEEP();
2888 		SET_LOCK_BTN((bool_t) !enable);
2889 		return;
2890 	}
2891 
2892 	if (!fbioc_send(DI_ROLE_MAIN, enable ? CDIOCPREVENT : CDIOCALLOW,
2893 			NULL, TRUE)) {
2894 		/* Cannot lock/unlock caddy */
2895 		DO_BEEP();
2896 		SET_LOCK_BTN((bool_t) !enable);
2897 		return;
2898 	}
2899 
2900 	s->caddy_lock = enable;
2901 	SET_LOCK_BTN(enable);
2902 }
2903 
2904 
2905 /*
2906  * fbioc_repeat
2907  *	Repeat mode function
2908  *
2909  * Args:
2910  *	s - Pointer to the curstat_t structure
2911  *	enable - whether to enable/disable repeat mode
2912  *
2913  * Return:
2914  *	Nothing.
2915  */
2916 void
fbioc_repeat(curstat_t * s,bool_t enable)2917 fbioc_repeat(curstat_t *s, bool_t enable)
2918 {
2919 	s->repeat = enable;
2920 
2921 	if (!enable && fbioc_new_progshuf) {
2922 		fbioc_new_progshuf = FALSE;
2923 		if (s->rptcnt > 0)
2924 			s->rptcnt--;
2925 	}
2926 	DPY_RPTCNT(s);
2927 }
2928 
2929 
2930 /*
2931  * fbioc_shuffle
2932  *	Shuffle mode function
2933  *
2934  * Args:
2935  *	s - Pointer to the curstat_t structure
2936  *	enable - whether to enable/disable shuffle mode
2937  *
2938  * Return:
2939  *	Nothing.
2940  */
2941 void
fbioc_shuffle(curstat_t * s,bool_t enable)2942 fbioc_shuffle(curstat_t *s, bool_t enable)
2943 {
2944 	if (s->segplay == SEGP_A) {
2945 		/* Can't set shuffle during a->? mode */
2946 		DO_BEEP();
2947 		SET_SHUFFLE_BTN((bool_t) !enable);
2948 		return;
2949 	}
2950 
2951 	switch (s->mode) {
2952 	case MOD_STOP:
2953 	case MOD_BUSY:
2954 	case MOD_NODISC:
2955 		if (s->program) {
2956 			/* Currently in program mode: can't enable shuffle */
2957 			DO_BEEP();
2958 			SET_SHUFFLE_BTN((bool_t) !enable);
2959 			return;
2960 		}
2961 		break;
2962 	default:
2963 		if (enable) {
2964 			/* Can't enable shuffle unless when stopped */
2965 			DO_BEEP();
2966 			SET_SHUFFLE_BTN((bool_t) !enable);
2967 			return;
2968 		}
2969 		break;
2970 	}
2971 
2972 	s->segplay = SEGP_NONE;	/* Cancel a->b mode */
2973 	DPY_PROGMODE(s, FALSE);
2974 
2975 	s->shuffle = enable;
2976 	if (!s->shuffle)
2977 		s->prog_tot = 0;
2978 }
2979 
2980 
2981 /*
2982  * fbioc_load_eject
2983  *	CD caddy load and eject function.  If disc caddy is not
2984  *	loaded, it will attempt to load it.  Otherwise, it will be
2985  *	ejected.
2986  *
2987  * Args:
2988  *	s - Pointer to the curstat_t structure
2989  *
2990  * Return:
2991  *	Nothing.
2992  */
2993 void
fbioc_load_eject(curstat_t * s)2994 fbioc_load_eject(curstat_t *s)
2995 {
2996 	bool_t	ret = FALSE;
2997 
2998 	if (fbioc_devp == NULL)
2999 		return;
3000 
3001 	if (fbioc_is_enabled(DI_ROLE_MAIN) && !fbioc_disc_present(FALSE)) {
3002 		/* No disc */
3003 		if (app_data.load_supp) {
3004 			/* Try loading the disc */
3005 			ret = fbioc_start_stop(TRUE, TRUE);
3006 		}
3007 
3008 		if (!ret && app_data.eject_supp) {
3009 			/* Cannot load, maybe the tray is already closed
3010 			 * but empty.
3011 			 */
3012 
3013 			/* Unlock caddy if supported */
3014 			if (app_data.caddylock_supp)
3015 				fbioc_lock(s, FALSE);
3016 
3017 			/* Try opening the tray */
3018 			if (fbioc_start_stop(FALSE, TRUE))
3019 				DO_BEEP();
3020 		}
3021 
3022 		fbioc_stop_stat_poll();
3023 		di_reset_curstat(s, TRUE, TRUE);
3024 		s->mode = MOD_NODISC;
3025 
3026 		di_clear_cdinfo(s, FALSE);
3027 		DPY_ALL(s);
3028 
3029 		if (fbioc_devp != NULL && app_data.eject_close) {
3030 			fbioc_close();
3031 			fbioc_not_open = TRUE;
3032 		}
3033 
3034 		fbioc_start_insert_poll(s);
3035 		return;
3036 	}
3037 
3038 	/* Eject the disc */
3039 
3040 	/* Spin down the CD */
3041 	(void) fbioc_start_stop(FALSE, FALSE);
3042 
3043 	if (!app_data.eject_supp) {
3044 		DO_BEEP();
3045 
3046 		fbioc_stop_stat_poll();
3047 		di_reset_curstat(s, TRUE, TRUE);
3048 		s->mode = MOD_NODISC;
3049 
3050 		di_clear_cdinfo(s, FALSE);
3051 		DPY_ALL(s);
3052 
3053 		if (fbioc_devp != NULL && app_data.eject_close) {
3054 			fbioc_close();
3055 			fbioc_not_open = TRUE;
3056 		}
3057 
3058 		fbioc_start_insert_poll(s);
3059 		return;
3060 	}
3061 
3062 	/* Unlock caddy if supported */
3063 	if (app_data.caddylock_supp)
3064 		fbioc_lock(s, FALSE);
3065 
3066 	fbioc_stop_stat_poll();
3067 	di_reset_curstat(s, TRUE, TRUE);
3068 	s->mode = MOD_NODISC;
3069 
3070 	di_clear_cdinfo(s, FALSE);
3071 	DPY_ALL(s);
3072 
3073 	/* Eject the CD */
3074 	(void) fbioc_start_stop(FALSE, TRUE);
3075 
3076 	if (app_data.eject_exit)
3077 		di_clinfo->quit(s);
3078 	else {
3079 		if (fbioc_devp != NULL && app_data.eject_close) {
3080 			fbioc_close();
3081 			fbioc_not_open = TRUE;
3082 		}
3083 
3084 		fbioc_start_insert_poll(s);
3085 	}
3086 }
3087 
3088 
3089 /*
3090  * fbioc_ab
3091  *	A->B segment play mode function
3092  *
3093  * Args:
3094  *	s - Pointer to the curstat_t structure
3095  *
3096  * Return:
3097  *	Nothing.
3098  */
3099 void
fbioc_ab(curstat_t * s)3100 fbioc_ab(curstat_t *s)
3101 {
3102 	if (!fbioc_run_ab(s))
3103 		DO_BEEP();
3104 }
3105 
3106 
3107 /*
3108  * fbioc_sample
3109  *	Sample play mode function
3110  *
3111  * Args:
3112  *	s - Pointer to the curstat_t structure
3113  *
3114  * Return:
3115  *	Nothing.
3116  */
3117 void
fbioc_sample(curstat_t * s)3118 fbioc_sample(curstat_t *s)
3119 {
3120 	int	i;
3121 
3122 	if (!fbioc_disc_ready(s)) {
3123 		DO_BEEP();
3124 		return;
3125 	}
3126 
3127 	if (s->shuffle || s->program || s->segplay != SEGP_NONE) {
3128 		/* Sample is not supported in program/shuffle or a->b modes */
3129 		DO_BEEP();
3130 		return;
3131 	}
3132 
3133 	switch (s->mode) {
3134 	case MOD_STOP:
3135 		fbioc_start_stat_poll(s);
3136 		/*FALLTHROUGH*/
3137 	case MOD_PLAY:
3138 		/* If already playing a track, start sampling the track after
3139 		 * the current one.  Otherwise, sample from the beginning.
3140 		 */
3141 		if (s->cur_trk > 0 && s->cur_trk != s->last_trk) {
3142 			i = di_curtrk_pos(s) + 1;
3143 			s->cur_trk = s->trkinfo[i].trkno;
3144 			fbioc_next_sam = (byte_t) i;
3145 		}
3146 		else {
3147 			s->cur_trk = s->first_trk;
3148 			fbioc_next_sam = 0;
3149 		}
3150 
3151 		s->cur_idx = 1;
3152 
3153 		s->mode = MOD_SAMPLE;
3154 		DPY_ALL(s);
3155 
3156 		if (!fbioc_run_sample(s))
3157 			return;
3158 
3159 		break;
3160 
3161 	case MOD_SAMPLE:
3162 		/* Currently doing Sample playback, just call fbioc_play_pause
3163 		 * to resume normal playback.
3164 		 */
3165 		fbioc_play_pause(s);
3166 		break;
3167 
3168 	default:
3169 		DO_BEEP();
3170 		break;
3171 	}
3172 }
3173 
3174 
3175 /*
3176  * fbioc_level
3177  *	Audio volume control function
3178  *
3179  * Args:
3180  *	s - Pointer to the curstat_t structure
3181  *	level - The volume level to set to
3182  *	drag - Whether this is an update due to the user dragging the
3183  *		volume control slider thumb.  If this is FALSE, then
3184  *		a final volume setting has been found.
3185  *
3186  * Return:
3187  *	Nothing.
3188  */
3189 /*ARGSUSED*/
3190 void
fbioc_level(curstat_t * s,byte_t level,bool_t drag)3191 fbioc_level(curstat_t *s, byte_t level, bool_t drag)
3192 {
3193 	int	actual;
3194 
3195 	/* Set volume level */
3196 	if ((actual = fbioc_cfg_vol((int) level, s, FALSE)) >= 0)
3197 		s->level = (byte_t) actual;
3198 }
3199 
3200 
3201 /*
3202  * fbioc_play_pause
3203  *	Audio playback and pause function
3204  *
3205  * Args:
3206  *	s - Pointer to the curstat_t structure
3207  *
3208  * Return:
3209  *	Nothing.
3210  */
3211 void
fbioc_play_pause(curstat_t * s)3212 fbioc_play_pause(curstat_t *s)
3213 {
3214 	sword32_t	i,
3215 			start_addr;
3216 	msf_t		start_msf,
3217 			end_msf;
3218 
3219 	fbioc_override_ap = TRUE;
3220 
3221 	if (!fbioc_disc_ready(s)) {
3222 		fbioc_override_ap = FALSE;
3223 		DO_BEEP();
3224 		return;
3225 	}
3226 
3227 	fbioc_override_ap = FALSE;
3228 
3229 	if (s->mode == MOD_NODISC)
3230 		s->mode = MOD_STOP;
3231 
3232 	switch (s->mode) {
3233 	case MOD_PLAY:
3234 		/* Currently playing: go to pause mode */
3235 
3236 		if (!fbioc_pause_resume(FALSE)) {
3237 			DO_BEEP();
3238 			return;
3239 		}
3240 		fbioc_stop_stat_poll();
3241 		s->mode = MOD_PAUSE;
3242 		DPY_PLAYMODE(s, FALSE);
3243 		break;
3244 
3245 	case MOD_PAUSE:
3246 		/* Currently paused: resume play */
3247 
3248 		if (!fbioc_pause_resume(TRUE)) {
3249 			DO_BEEP();
3250 			return;
3251 		}
3252 		s->mode = MOD_PLAY;
3253 		DPY_PLAYMODE(s, FALSE);
3254 		fbioc_start_stat_poll(s);
3255 		break;
3256 
3257 	case MOD_STOP:
3258 		/* Currently stopped: start play */
3259 
3260 		if (!di_prepare_cdda(s))
3261 			return;
3262 
3263 		if (s->shuffle || s->program) {
3264 			fbioc_new_progshuf = TRUE;
3265 
3266 			/* Start shuffle/program play */
3267 			if (!fbioc_run_prog(s))
3268 				return;
3269 		}
3270 		else if (s->segplay == SEGP_AB) {
3271 			/* Play defined segment */
3272 			if (!fbioc_run_ab(s))
3273 				return;
3274 		}
3275 		else {
3276 			s->segplay = SEGP_NONE;	/* Cancel a->b mode */
3277 
3278 			/* Start normal play */
3279 			if ((i = di_curtrk_pos(s)) < 0 || s->cur_trk <= 0) {
3280 				/* Start play from the beginning */
3281 				i = 0;
3282 				s->cur_trk = s->first_trk;
3283 				start_addr = s->trkinfo[0].addr +
3284 					     s->curpos_trk.addr;
3285 				util_blktomsf(
3286 					start_addr,
3287 					&start_msf.min,
3288 					&start_msf.sec,
3289 					&start_msf.frame,
3290 					MSF_OFFSET
3291 				);
3292 			}
3293 			else {
3294 				/* User has specified a starting track */
3295 				start_addr = s->trkinfo[i].addr +
3296 					     s->curpos_trk.addr;
3297 			}
3298 
3299 			util_blktomsf(
3300 				start_addr,
3301 				&start_msf.min,
3302 				&start_msf.sec,
3303 				&start_msf.frame,
3304 				MSF_OFFSET
3305 			);
3306 
3307 			end_msf.min = s->discpos_tot.min;
3308 			end_msf.sec = s->discpos_tot.sec;
3309 			end_msf.frame = s->discpos_tot.frame;
3310 
3311 			if (s->trkinfo[i].type == TYP_DATA) {
3312 				DPY_TRACK(s);
3313 				DPY_TIME(s, FALSE);
3314 				DO_BEEP();
3315 				return;
3316 			}
3317 
3318 			s->cur_idx = 1;
3319 			s->mode = MOD_PLAY;
3320 
3321 			if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
3322 					  start_addr, s->discpos_tot.addr,
3323 					  &start_msf, &end_msf, 0, 0)) {
3324 				DO_BEEP();
3325 				s->mode = MOD_STOP;
3326 				return;
3327 			}
3328 		}
3329 
3330 		DPY_ALL(s);
3331 		fbioc_start_stat_poll(s);
3332 		break;
3333 
3334 	case MOD_SAMPLE:
3335 		/* Force update of curstat */
3336 		if (!fbioc_get_playstatus(s)) {
3337 			DO_BEEP();
3338 			return;
3339 		}
3340 
3341 		/* Currently doing a->b or sample playback: just resume play */
3342 		if (s->shuffle || s->program) {
3343 			if ((i = di_curtrk_pos(s)) < 0 ||
3344 			    s->trkinfo[i].trkno == LEAD_OUT_TRACK)
3345 				return;
3346 
3347 			start_msf.min = s->curpos_tot.min;
3348 			start_msf.sec = s->curpos_tot.sec;
3349 			start_msf.frame = s->curpos_tot.frame;
3350 			end_msf.min = s->trkinfo[i+1].min;
3351 			end_msf.sec = s->trkinfo[i+1].sec;
3352 			end_msf.frame = s->trkinfo[i+1].frame;
3353 
3354 			if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
3355 					  s->curpos_tot.addr,
3356 					  s->trkinfo[i+1].addr,
3357 					  &start_msf, &end_msf, 0, 0)) {
3358 				DO_BEEP();
3359 				return;
3360 			}
3361 		}
3362 		else {
3363 			start_msf.min = s->curpos_tot.min;
3364 			start_msf.sec = s->curpos_tot.sec;
3365 			start_msf.frame = s->curpos_tot.frame;
3366 			end_msf.min = s->discpos_tot.min;
3367 			end_msf.sec = s->discpos_tot.sec;
3368 			end_msf.frame = s->discpos_tot.frame;
3369 
3370 			if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
3371 					  s->curpos_tot.addr,
3372 					  s->discpos_tot.addr,
3373 					  &start_msf, &end_msf, 0, 0)) {
3374 				DO_BEEP();
3375 				return;
3376 			}
3377 		}
3378 		s->mode = MOD_PLAY;
3379 		DPY_PLAYMODE(s, FALSE);
3380 		break;
3381 
3382 	default:
3383 		DO_BEEP();
3384 		break;
3385 	}
3386 }
3387 
3388 
3389 /*
3390  * fbioc_stop
3391  *	Stop function
3392  *
3393  * Args:
3394  *	s - Pointer to the curstat_t structure
3395  *	stop_disc - Whether to actually spin down the disc or just
3396  *		update status.
3397  *
3398  * Return:
3399  *	Nothing.
3400  */
3401 void
fbioc_stop(curstat_t * s,bool_t stop_disc)3402 fbioc_stop(curstat_t *s, bool_t stop_disc)
3403 {
3404 	/* The stop_disc parameter will cause the disc to spin down.
3405 	 * This is usually set to TRUE, but can be FALSE if the caller
3406 	 * just wants to set the current state to stop but will
3407 	 * immediately go into play state again.  Not spinning down
3408 	 * the drive makes things a little faster...
3409 	 */
3410 	if (!fbioc_disc_ready(s))
3411 		return;
3412 
3413 	switch (s->mode) {
3414 	case MOD_PLAY:
3415 	case MOD_PAUSE:
3416 	case MOD_SAMPLE:
3417 	case MOD_STOP:
3418 		/* Currently playing or paused: stop */
3419 
3420 		if (stop_disc && !fbioc_start_stop(FALSE, FALSE)) {
3421 			DO_BEEP();
3422 			return;
3423 		}
3424 		fbioc_stop_stat_poll();
3425 
3426 		di_reset_curstat(s, FALSE, FALSE);
3427 		s->mode = MOD_STOP;
3428 		s->rptcnt = 0;
3429 
3430 		DPY_ALL(s);
3431 		break;
3432 
3433 	default:
3434 		break;
3435 	}
3436 }
3437 
3438 
3439 /*
3440  * fbioc_chgdisc
3441  *	Change disc function
3442  *
3443  * Args:
3444  *	s - Pointer to the curstat_t structure
3445  *
3446  * Return:
3447  *	Nothing.
3448  */
3449 /*ARGSUSED*/
3450 void
fbioc_chgdisc(curstat_t * s)3451 fbioc_chgdisc(curstat_t *s)
3452 {
3453 	/* Disc change is not implemented in this module */
3454 	DO_BEEP();
3455 }
3456 
3457 
3458 /*
3459  * fbioc_prevtrk
3460  *	Previous track function
3461  *
3462  * Args:
3463  *	s - Pointer to the curstat_t structure
3464  *
3465  * Return:
3466  *	Nothing.
3467  */
3468 void
fbioc_prevtrk(curstat_t * s)3469 fbioc_prevtrk(curstat_t *s)
3470 {
3471 	sword32_t	i,
3472 			start_addr;
3473 	msf_t		start_msf,
3474 			end_msf;
3475 	bool_t		go_prev;
3476 
3477 	if (!fbioc_disc_ready(s)) {
3478 		DO_BEEP();
3479 		return;
3480 	}
3481 
3482 	switch (s->mode) {
3483 	case MOD_SAMPLE:
3484 		s->mode = MOD_PLAY;
3485 		DPY_PLAYMODE(s, FALSE);
3486 		/*FALLTHROUGH*/
3487 	case MOD_PLAY:
3488 	case MOD_PAUSE:
3489 		/* Find appropriate track to start */
3490 		if (s->shuffle || s->program) {
3491 			if (s->prog_cnt > 0) {
3492 				s->prog_cnt--;
3493 				fbioc_new_progshuf = FALSE;
3494 			}
3495 			i = di_curprog_pos(s);
3496 		}
3497 		else
3498 			i = di_curtrk_pos(s);
3499 
3500 		if (s->segplay == SEGP_AB) {
3501 			s->segplay = SEGP_NONE;	/* Cancel a->b mode */
3502 			DPY_PROGMODE(s, FALSE);
3503 		}
3504 
3505 		go_prev = FALSE;
3506 
3507 		if (i == 0 && s->cur_idx == 0) {
3508 			i = 0;
3509 			start_addr = di_clip_frames;
3510 			util_blktomsf(
3511 				start_addr,
3512 				&start_msf.min,
3513 				&start_msf.sec,
3514 				&start_msf.frame,
3515 				MSF_OFFSET
3516 			);
3517 			s->cur_trk = s->trkinfo[i].trkno;
3518 			s->cur_idx = 0;
3519 		}
3520 		else {
3521 			start_addr = s->trkinfo[i].addr;
3522 			start_msf.min = s->trkinfo[i].min;
3523 			start_msf.sec = s->trkinfo[i].sec;
3524 			start_msf.frame = s->trkinfo[i].frame;
3525 			s->cur_trk = s->trkinfo[i].trkno;
3526 			s->cur_idx = 1;
3527 
3528 			/* If the current track has been playing for less
3529 			 * than app_data.prev_threshold blocks, then go
3530 			 * to the beginning of the previous track (if we
3531 			 * are not already on the first track).
3532 			 */
3533 			if ((s->curpos_tot.addr - start_addr) <=
3534 			    app_data.prev_threshold)
3535 				go_prev = TRUE;
3536 		}
3537 
3538 		if (go_prev) {
3539 			if (s->shuffle || s->program) {
3540 				if (s->prog_cnt > 0) {
3541 					s->prog_cnt--;
3542 					fbioc_new_progshuf = FALSE;
3543 				}
3544 				if ((i = di_curprog_pos(s)) < 0)
3545 					return;
3546 
3547 				start_addr = s->trkinfo[i].addr;
3548 				start_msf.min = s->trkinfo[i].min;
3549 				start_msf.sec = s->trkinfo[i].sec;
3550 				start_msf.frame = s->trkinfo[i].frame;
3551 				s->cur_trk = s->trkinfo[i].trkno;
3552 			}
3553 			else if (i == 0) {
3554 				/* Go to the very beginning: this may be
3555 				 * a lead-in area before the start of track 1.
3556 				 */
3557 				start_addr = di_clip_frames;
3558 				util_blktomsf(
3559 					start_addr,
3560 					&start_msf.min,
3561 					&start_msf.sec,
3562 					&start_msf.frame,
3563 					MSF_OFFSET
3564 				);
3565 				s->cur_trk = s->trkinfo[i].trkno;
3566 			}
3567 			else if (i > 0) {
3568 				i--;
3569 
3570 				/* Skip over data tracks */
3571 				while (s->trkinfo[i].type == TYP_DATA) {
3572 					if (i <= 0)
3573 						break;
3574 					i--;
3575 				}
3576 
3577 				if (s->trkinfo[i].type != TYP_DATA) {
3578 					start_addr = s->trkinfo[i].addr;
3579 					start_msf.min = s->trkinfo[i].min;
3580 					start_msf.sec = s->trkinfo[i].sec;
3581 					start_msf.frame = s->trkinfo[i].frame;
3582 					s->cur_trk = s->trkinfo[i].trkno;
3583 				}
3584 			}
3585 		}
3586 
3587 		if (s->mode == MOD_PAUSE)
3588 			/* Mute: so we don't get a transient */
3589 			fbioc_mute_on(s);
3590 
3591 		if (s->shuffle || s->program) {
3592 			/* Program/Shuffle mode: just stop the playback
3593 			 * and let fbioc_run_prog go to the previous track
3594 			 */
3595 			fbioc_fake_stop = TRUE;
3596 
3597 			/* Force status update */
3598 			(void) fbioc_get_playstatus(s);
3599 		}
3600 		else {
3601 			end_msf.min = s->discpos_tot.min;
3602 			end_msf.sec = s->discpos_tot.sec;
3603 			end_msf.frame = s->discpos_tot.frame;
3604 
3605 			s->curpos_tot.addr = start_addr;
3606 			s->curpos_tot.min = start_msf.min;
3607 			s->curpos_tot.sec = start_msf.sec;
3608 			s->curpos_tot.frame = start_msf.frame;
3609 			s->curpos_trk.addr = 0;
3610 			s->curpos_trk.min = 0;
3611 			s->curpos_trk.sec = 0;
3612 			s->curpos_trk.frame = 0;
3613 
3614 			DPY_TRACK(s);
3615 			DPY_INDEX(s);
3616 			DPY_TIME(s, FALSE);
3617 
3618 			if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
3619 					  start_addr, s->discpos_tot.addr,
3620 					  &start_msf, &end_msf, 0, 0)) {
3621 				DO_BEEP();
3622 
3623 				/* Restore volume */
3624 				fbioc_mute_off(s);
3625 				return;
3626 			}
3627 
3628 			if (s->mode == MOD_PAUSE) {
3629 				(void) fbioc_pause_resume(FALSE);
3630 
3631 				/* Restore volume */
3632 				fbioc_mute_off(s);
3633 			}
3634 		}
3635 
3636 		break;
3637 
3638 	case MOD_STOP:
3639 		if (s->shuffle || s->program) {
3640 			/* Pre-selecting tracks not supported in shuffle
3641 			 * or program mode.
3642 			 */
3643 			DO_BEEP();
3644 			return;
3645 		}
3646 
3647 		/* Find previous track */
3648 		if (s->cur_trk <= 0) {
3649 			s->cur_trk = s->trkinfo[0].trkno;
3650 			DPY_TRACK(s);
3651 		}
3652 		else {
3653 			i = di_curtrk_pos(s);
3654 
3655 			if (i > 0) {
3656 				s->cur_trk = s->trkinfo[i-1].trkno;
3657 				DPY_TRACK(s);
3658 			}
3659 		}
3660 		break;
3661 
3662 	default:
3663 		DO_BEEP();
3664 		break;
3665 	}
3666 }
3667 
3668 
3669 /*
3670  * fbioc_nexttrk
3671  *	Next track function
3672  *
3673  * Args:
3674  *	s - Pointer to the curstat_t structure
3675  *
3676  * Return:
3677  *	Nothing.
3678  */
3679 void
fbioc_nexttrk(curstat_t * s)3680 fbioc_nexttrk(curstat_t *s)
3681 {
3682 	sword32_t	i,
3683 			start_addr;
3684 	msf_t		start_msf,
3685 			end_msf;
3686 
3687 	if (!fbioc_disc_ready(s)) {
3688 		DO_BEEP();
3689 		return;
3690 	}
3691 
3692 	switch (s->mode) {
3693 	case MOD_SAMPLE:
3694 		s->mode = MOD_PLAY;
3695 		DPY_PLAYMODE(s, FALSE);
3696 		/*FALLTHROUGH*/
3697 	case MOD_PLAY:
3698 	case MOD_PAUSE:
3699 		if (s->shuffle || s->program) {
3700 			if (s->prog_cnt >= s->prog_tot) {
3701 				/* Disallow advancing beyond current
3702 				 * shuffle/program sequence if
3703 				 * repeat mode is not on.
3704 				 */
3705 				if (s->repeat)
3706 					fbioc_new_progshuf = TRUE;
3707 				else
3708 					return;
3709 			}
3710 
3711 			if (s->mode == MOD_PAUSE)
3712 				/* Mute: so we don't get a transient */
3713 				fbioc_mute_on(s);
3714 
3715 			/* Program/Shuffle mode: just stop the playback
3716 			 * and let fbioc_run_prog go to the next track.
3717 			 */
3718 			fbioc_fake_stop = TRUE;
3719 
3720 			/* Force status update */
3721 			(void) fbioc_get_playstatus(s);
3722 
3723 			return;
3724 		}
3725 		else if (s->segplay == SEGP_AB) {
3726 			s->segplay = SEGP_NONE;	/* Cancel a->b mode */
3727 			DPY_PROGMODE(s, FALSE);
3728 		}
3729 
3730 		/* Find next track */
3731 		if ((i = di_curtrk_pos(s)) < 0)
3732 			return;
3733 
3734 		if (i > 0 || s->cur_idx > 0)
3735 			i++;
3736 
3737 		/* Skip over data tracks */
3738 		while (i < MAXTRACK && s->trkinfo[i].type == TYP_DATA)
3739 			i++;
3740 
3741 		if (i < MAXTRACK &&
3742 		    s->trkinfo[i].trkno >= 0 &&
3743 		    s->trkinfo[i].trkno != LEAD_OUT_TRACK) {
3744 
3745 			start_addr = s->trkinfo[i].addr;
3746 			start_msf.min = s->trkinfo[i].min;
3747 			start_msf.sec = s->trkinfo[i].sec;
3748 			start_msf.frame = s->trkinfo[i].frame;
3749 			s->cur_trk = s->trkinfo[i].trkno;
3750 			s->cur_idx = 1;
3751 
3752 			if (s->mode == MOD_PAUSE)
3753 				/* Mute: so we don't get a transient */
3754 				fbioc_mute_on(s);
3755 
3756 			end_msf.min = s->discpos_tot.min;
3757 			end_msf.sec = s->discpos_tot.sec;
3758 			end_msf.frame = s->discpos_tot.frame;
3759 
3760 			s->curpos_tot.addr = start_addr;
3761 			s->curpos_tot.min = start_msf.min;
3762 			s->curpos_tot.sec = start_msf.sec;
3763 			s->curpos_tot.frame = start_msf.frame;
3764 			s->curpos_trk.addr = 0;
3765 			s->curpos_trk.min = 0;
3766 			s->curpos_trk.sec = 0;
3767 			s->curpos_trk.frame = 0;
3768 
3769 			DPY_TRACK(s);
3770 			DPY_INDEX(s);
3771 			DPY_TIME(s, FALSE);
3772 
3773 			if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
3774 					  start_addr, s->discpos_tot.addr,
3775 					  &start_msf, &end_msf, 0, 0)) {
3776 				DO_BEEP();
3777 				return;
3778 			}
3779 
3780 			if (s->mode == MOD_PAUSE) {
3781 				(void) fbioc_pause_resume(FALSE);
3782 
3783 				/* Restore volume */
3784 				fbioc_mute_off(s);
3785 			}
3786 		}
3787 
3788 		break;
3789 
3790 	case MOD_STOP:
3791 		if (s->shuffle || s->program) {
3792 			/* Pre-selecting tracks not supported in shuffle
3793 			 * or program mode.
3794 			 */
3795 			DO_BEEP();
3796 			return;
3797 		}
3798 
3799 		/* Find next track */
3800 		if (s->cur_trk <= 0) {
3801 			s->cur_trk = s->trkinfo[0].trkno;
3802 			DPY_TRACK(s);
3803 		}
3804 		else {
3805 			i = di_curtrk_pos(s) + 1;
3806 
3807 			if (i > 0 && s->trkinfo[i].trkno != LEAD_OUT_TRACK) {
3808 				s->cur_trk = s->trkinfo[i].trkno;
3809 				DPY_TRACK(s);
3810 			}
3811 		}
3812 		break;
3813 
3814 	default:
3815 		DO_BEEP();
3816 		break;
3817 	}
3818 }
3819 
3820 
3821 /*
3822  * fbioc_previdx
3823  *	Previous index function
3824  *
3825  * Args:
3826  *	s - Pointer to the curstat_t structure
3827  *
3828  * Return:
3829  *	Nothing.
3830  */
3831 void
fbioc_previdx(curstat_t * s)3832 fbioc_previdx(curstat_t *s)
3833 {
3834 	msf_t		start_msf,
3835 			end_msf;
3836 	byte_t		idx;
3837 
3838 	if (s->shuffle || s->program) {
3839 		/* Index search is not supported in program/shuffle mode */
3840 		DO_BEEP();
3841 		return;
3842 	}
3843 
3844 	switch (s->mode) {
3845 	case MOD_SAMPLE:
3846 		s->mode = MOD_PLAY;
3847 		DPY_PLAYMODE(s, FALSE);
3848 		/*FALLTHROUGH*/
3849 	case MOD_PLAY:
3850 	case MOD_PAUSE:
3851 		if (s->segplay == SEGP_AB) {
3852 			s->segplay = SEGP_NONE;	/* Cancel a->b mode */
3853 			DPY_PROGMODE(s, FALSE);
3854 		}
3855 
3856 		/* Find appropriate index to start */
3857 		if (s->cur_idx > 1 &&
3858 		    (s->curpos_tot.addr - s->sav_iaddr) <=
3859 			    app_data.prev_threshold)
3860 			idx = s->cur_idx - 1;
3861 		else
3862 			idx = s->cur_idx;
3863 
3864 		/* This is a Hack...
3865 		 * Since there is no standard command to start
3866 		 * playback on an index boundary and then go on playing
3867 		 * until the end of the disc, we will use the PLAY AUDIO
3868 		 * TRACK/INDEX command to go to where we want to start,
3869 		 * immediately followed by a PAUSE.  We then find the
3870 		 * current block position and issue a PLAY AUDIO MSF
3871 		 * or PLAY AUDIO(12) command to start play there.
3872 		 * We mute the audio in between these operations to
3873 		 * prevent unpleasant transients.
3874 		 */
3875 
3876 		/* Mute */
3877 		fbioc_mute_on(s);
3878 
3879 		if (!fbioc_do_playaudio(ADDR_TRKIDX, 0, 0, NULL, NULL,
3880 				  (byte_t) s->cur_trk, idx)) {
3881 			/* Restore volume */
3882 			fbioc_mute_off(s);
3883 			DO_BEEP();
3884 			return;
3885 		}
3886 
3887 		/* A small delay to make sure the command took effect */
3888 		util_delayms(10);
3889 
3890 		fbioc_idx_pause = TRUE;
3891 
3892 		if (!fbioc_pause_resume(FALSE)) {
3893 			/* Restore volume */
3894 			fbioc_mute_off(s);
3895 			fbioc_idx_pause = FALSE;
3896 			return;
3897 		}
3898 
3899 		/* Use fbioc_get_playstatus to update the current status */
3900 		if (!fbioc_get_playstatus(s)) {
3901 			/* Restore volume */
3902 			fbioc_mute_off(s);
3903 			fbioc_idx_pause = FALSE;
3904 			return;
3905 		}
3906 
3907 		/* Save starting block addr of this index */
3908 		s->sav_iaddr = s->curpos_tot.addr;
3909 
3910 		if (s->mode != MOD_PAUSE)
3911 			/* Restore volume */
3912 			fbioc_mute_off(s);
3913 
3914 		start_msf.min = s->curpos_tot.min;
3915 		start_msf.sec = s->curpos_tot.sec;
3916 		start_msf.frame = s->curpos_tot.frame;
3917 		end_msf.min = s->discpos_tot.min;
3918 		end_msf.sec = s->discpos_tot.sec;
3919 		end_msf.frame = s->discpos_tot.frame;
3920 
3921 		if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
3922 				  s->curpos_tot.addr, s->discpos_tot.addr,
3923 				  &start_msf, &end_msf, 0, 0)) {
3924 			DO_BEEP();
3925 			fbioc_idx_pause = FALSE;
3926 			return;
3927 		}
3928 
3929 		fbioc_idx_pause = FALSE;
3930 
3931 		if (s->mode == MOD_PAUSE) {
3932 			(void) fbioc_pause_resume(FALSE);
3933 
3934 			/* Restore volume */
3935 			fbioc_mute_off(s);
3936 
3937 			/* Force update of curstat */
3938 			(void) fbioc_get_playstatus(s);
3939 		}
3940 
3941 		break;
3942 
3943 	default:
3944 		DO_BEEP();
3945 		break;
3946 	}
3947 }
3948 
3949 
3950 /*
3951  * fbioc_nextidx
3952  *	Next index function
3953  *
3954  * Args:
3955  *	s - Pointer to the curstat_t structure
3956  *
3957  * Return:
3958  *	Nothing.
3959  */
3960 void
fbioc_nextidx(curstat_t * s)3961 fbioc_nextidx(curstat_t *s)
3962 {
3963 	msf_t		start_msf,
3964 			end_msf;
3965 
3966 	if (s->shuffle || s->program) {
3967 		/* Index search is not supported in program/shuffle mode */
3968 		DO_BEEP();
3969 		return;
3970 	}
3971 
3972 	switch (s->mode) {
3973 	case MOD_SAMPLE:
3974 		s->mode = MOD_PLAY;
3975 		DPY_PLAYMODE(s, FALSE);
3976 		/*FALLTHROUGH*/
3977 	case MOD_PLAY:
3978 	case MOD_PAUSE:
3979 		if (s->segplay == SEGP_AB) {
3980 			s->segplay = SEGP_NONE;	/* Cancel a->b mode */
3981 			DPY_PROGMODE(s, FALSE);
3982 		}
3983 
3984 		/* Find appropriate index to start */
3985 
3986 		/* This is a Hack...
3987 		 * Since there is no standard command to start
3988 		 * playback on an index boundary and then go on playing
3989 		 * until the end of the disc, we will use the PLAY AUDIO
3990 		 * TRACK/INDEX command to go to where we want to start,
3991 		 * immediately followed by a PAUSE.  We then find the
3992 		 * current block position and issue a PLAY AUDIO MSF
3993 		 * or PLAY AUDIO(12) command to start play there.
3994 		 * We mute the audio in between these operations to
3995 		 * prevent unpleasant transients.
3996 		 */
3997 
3998 		/* Mute */
3999 		fbioc_mute_on(s);
4000 
4001 		if (!fbioc_do_playaudio(ADDR_TRKIDX, 0, 0, NULL, NULL,
4002 				  (byte_t) s->cur_trk,
4003 				  (byte_t) (s->cur_idx + 1))) {
4004 			/* Restore volume */
4005 			fbioc_mute_off(s);
4006 			DO_BEEP();
4007 			return;
4008 		}
4009 
4010 		/* A small delay to make sure the command took effect */
4011 		util_delayms(10);
4012 
4013 		fbioc_idx_pause = TRUE;
4014 
4015 		if (!fbioc_pause_resume(FALSE)) {
4016 			/* Restore volume */
4017 			fbioc_mute_off(s);
4018 			fbioc_idx_pause = FALSE;
4019 			return;
4020 		}
4021 
4022 		/* Use fbioc_get_playstatus to update the current status */
4023 		if (!fbioc_get_playstatus(s)) {
4024 			/* Restore volume */
4025 			fbioc_mute_off(s);
4026 			fbioc_idx_pause = FALSE;
4027 			return;
4028 		}
4029 
4030 		/* Save starting block addr of this index */
4031 		s->sav_iaddr = s->curpos_tot.addr;
4032 
4033 		if (s->mode != MOD_PAUSE)
4034 			/* Restore volume */
4035 			fbioc_mute_off(s);
4036 
4037 		start_msf.min = s->curpos_tot.min;
4038 		start_msf.sec = s->curpos_tot.sec;
4039 		start_msf.frame = s->curpos_tot.frame;
4040 		end_msf.min = s->discpos_tot.min;
4041 		end_msf.sec = s->discpos_tot.sec;
4042 		end_msf.frame = s->discpos_tot.frame;
4043 
4044 		if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
4045 				  s->curpos_tot.addr, s->discpos_tot.addr,
4046 				  &start_msf, &end_msf, 0, 0)) {
4047 			DO_BEEP();
4048 			fbioc_idx_pause = FALSE;
4049 			return;
4050 		}
4051 
4052 		fbioc_idx_pause = FALSE;
4053 
4054 		if (s->mode == MOD_PAUSE) {
4055 			(void) fbioc_pause_resume(FALSE);
4056 
4057 			/* Restore volume */
4058 			fbioc_mute_off(s);
4059 
4060 			/* Force update of curstat */
4061 			(void) fbioc_get_playstatus(s);
4062 		}
4063 
4064 		break;
4065 
4066 	default:
4067 		DO_BEEP();
4068 		break;
4069 	}
4070 }
4071 
4072 
4073 /*
4074  * fbioc_rew
4075  *	Search-rewind function
4076  *
4077  * Args:
4078  *	s - Pointer to the curstat_t structure
4079  *
4080  * Return:
4081  *	Nothing.
4082  */
4083 void
fbioc_rew(curstat_t * s,bool_t start)4084 fbioc_rew(curstat_t *s, bool_t start)
4085 {
4086 	sword32_t	i;
4087 	msf_t		start_msf,
4088 			end_msf;
4089 	byte_t		vol;
4090 
4091 	switch (s->mode) {
4092 	case MOD_SAMPLE:
4093 		/* Go to normal play mode first */
4094 		fbioc_play_pause(s);
4095 
4096 		/*FALLTHROUGH*/
4097 	case MOD_PLAY:
4098 	case MOD_PAUSE:
4099 		if (start) {
4100 			/* Button press */
4101 
4102 			if (s->mode == MOD_PLAY)
4103 				fbioc_stop_stat_poll();
4104 
4105 			if (PLAYMODE_IS_STD(app_data.play_mode)) {
4106 				/* Reduce volume */
4107 				vol = (byte_t) ((int) s->level *
4108 					app_data.skip_vol / 100);
4109 
4110 				(void) fbioc_cfg_vol((int)
4111 					((vol < (byte_t)app_data.skip_minvol) ?
4112 					 (byte_t) app_data.skip_minvol : vol),
4113 					s,
4114 					FALSE
4115 				);
4116 			}
4117 
4118 			/* Start search rewind */
4119 			fbioc_start_search = TRUE;
4120 			fbioc_run_rew(s);
4121 		}
4122 		else {
4123 			/* Button release */
4124 
4125 			fbioc_stop_rew(s);
4126 
4127 			/* Update display */
4128 			(void) fbioc_get_playstatus(s);
4129 
4130 			if (s->mode == MOD_PAUSE)
4131 				/* Mute: so we don't get a transient */
4132 				fbioc_mute_on(s);
4133 			else
4134 				/* Restore volume */
4135 				fbioc_mute_off(s);
4136 
4137 			if (s->shuffle || s->program) {
4138 				if ((i = di_curtrk_pos(s)) < 0 ||
4139 				    s->trkinfo[i].trkno == LEAD_OUT_TRACK) {
4140 					/* Restore volume */
4141 					fbioc_mute_off(s);
4142 					return;
4143 				}
4144 
4145 				start_msf.min = s->curpos_tot.min;
4146 				start_msf.sec = s->curpos_tot.sec;
4147 				start_msf.frame = s->curpos_tot.frame;
4148 				end_msf.min = s->trkinfo[i+1].min;
4149 				end_msf.sec = s->trkinfo[i+1].sec;
4150 				end_msf.frame = s->trkinfo[i+1].frame;
4151 
4152 				if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
4153 						  s->curpos_tot.addr,
4154 						  s->trkinfo[i+1].addr,
4155 						  &start_msf, &end_msf,
4156 						  0, 0)) {
4157 					DO_BEEP();
4158 
4159 					/* Restore volume */
4160 					fbioc_mute_off(s);
4161 					return;
4162 				}
4163 			}
4164 			else {
4165 				start_msf.min = s->curpos_tot.min;
4166 				start_msf.sec = s->curpos_tot.sec;
4167 				start_msf.frame = s->curpos_tot.frame;
4168 				end_msf.min = s->discpos_tot.min;
4169 				end_msf.sec = s->discpos_tot.sec;
4170 				end_msf.frame = s->discpos_tot.frame;
4171 
4172 				if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
4173 						  s->curpos_tot.addr,
4174 						  s->discpos_tot.addr,
4175 						  &start_msf, &end_msf,
4176 						  0, 0)) {
4177 					DO_BEEP();
4178 
4179 					/* Restore volume */
4180 					fbioc_mute_off(s);
4181 					return;
4182 				}
4183 			}
4184 
4185 			if (s->mode == MOD_PAUSE) {
4186 				(void) fbioc_pause_resume(FALSE);
4187 
4188 				/* Restore volume */
4189 				fbioc_mute_off(s);
4190 			}
4191 			else
4192 				fbioc_start_stat_poll(s);
4193 		}
4194 		break;
4195 
4196 	default:
4197 		if (start)
4198 			DO_BEEP();
4199 		break;
4200 	}
4201 }
4202 
4203 
4204 /*
4205  * fbioc_ff
4206  *	Search-fast-forward function
4207  *
4208  * Args:
4209  *	s - Pointer to the curstat_t structure
4210  *
4211  * Return:
4212  *	Nothing.
4213  */
4214 void
fbioc_ff(curstat_t * s,bool_t start)4215 fbioc_ff(curstat_t *s, bool_t start)
4216 {
4217 	sword32_t	i,
4218 			start_addr,
4219 			end_addr;
4220 	msf_t		start_msf,
4221 			end_msf;
4222 	byte_t		vol;
4223 
4224 	switch (s->mode) {
4225 	case MOD_SAMPLE:
4226 		/* Go to normal play mode first */
4227 		fbioc_play_pause(s);
4228 
4229 		/*FALLTHROUGH*/
4230 	case MOD_PLAY:
4231 	case MOD_PAUSE:
4232 		if (start) {
4233 			/* Button press */
4234 
4235 			if (s->mode == MOD_PLAY)
4236 				fbioc_stop_stat_poll();
4237 
4238 			if (PLAYMODE_IS_STD(app_data.play_mode)) {
4239 				/* Reduce volume */
4240 				vol = (byte_t) ((int) s->level *
4241 					app_data.skip_vol / 100);
4242 
4243 				(void) fbioc_cfg_vol((int)
4244 					((vol < (byte_t)app_data.skip_minvol) ?
4245 					 (byte_t) app_data.skip_minvol : vol),
4246 					s,
4247 					FALSE
4248 				);
4249 			}
4250 
4251 			/* Start search forward */
4252 			fbioc_start_search = TRUE;
4253 			fbioc_run_ff(s);
4254 		}
4255 		else {
4256 			/* Button release */
4257 
4258 			fbioc_stop_ff(s);
4259 
4260 			/* Update display */
4261 			(void) fbioc_get_playstatus(s);
4262 
4263 			if (s->mode == MOD_PAUSE)
4264 				/* Mute: so we don't get a transient */
4265 				fbioc_mute_on(s);
4266 			else
4267 				/* Restore volume */
4268 				fbioc_mute_off(s);
4269 
4270 			if (s->shuffle || s->program) {
4271 				if ((i = di_curtrk_pos(s)) < 0 ||
4272 				    s->trkinfo[i].trkno == LEAD_OUT_TRACK) {
4273 					/* Restore volume */
4274 					fbioc_mute_off(s);
4275 					return;
4276 				}
4277 
4278 				start_addr = s->curpos_tot.addr;
4279 				start_msf.min = s->curpos_tot.min;
4280 				start_msf.sec = s->curpos_tot.sec;
4281 				start_msf.frame = s->curpos_tot.frame;
4282 				end_addr = s->trkinfo[i+1].addr;
4283 				end_msf.min = s->trkinfo[i+1].min;
4284 				end_msf.sec = s->trkinfo[i+1].sec;
4285 				end_msf.frame = s->trkinfo[i+1].frame;
4286 
4287 				if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
4288 						  start_addr, end_addr,
4289 						  &start_msf, &end_msf,
4290 						  0, 0)) {
4291 					DO_BEEP();
4292 
4293 					/* Restore volume */
4294 					fbioc_mute_off(s);
4295 					return;
4296 				}
4297 			}
4298 			else if (s->segplay == SEGP_AB &&
4299 				 (s->curpos_tot.addr + app_data.min_playblks) >
4300 					s->bp_endpos_tot.addr) {
4301 				/* No more left to play */
4302 				/* Restore volume */
4303 				fbioc_mute_off(s);
4304 				return;
4305 			}
4306 			else {
4307 				start_addr = s->curpos_tot.addr;
4308 				start_msf.min = s->curpos_tot.min;
4309 				start_msf.sec = s->curpos_tot.sec;
4310 				start_msf.frame = s->curpos_tot.frame;
4311 
4312 				if (s->segplay == SEGP_AB) {
4313 					end_addr = s->bp_endpos_tot.addr;
4314 					end_msf.min = s->bp_endpos_tot.min;
4315 					end_msf.sec = s->bp_endpos_tot.sec;
4316 					end_msf.frame = s->bp_endpos_tot.frame;
4317 				}
4318 				else {
4319 					end_addr = s->discpos_tot.addr;
4320 					end_msf.min = s->discpos_tot.min;
4321 					end_msf.sec = s->discpos_tot.sec;
4322 					end_msf.frame = s->discpos_tot.frame;
4323 				}
4324 
4325 				if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
4326 						  start_addr, end_addr,
4327 						  &start_msf, &end_msf,
4328 						  0, 0)) {
4329 					DO_BEEP();
4330 
4331 					/* Restore volume */
4332 					fbioc_mute_off(s);
4333 					return;
4334 				}
4335 			}
4336 			if (s->mode == MOD_PAUSE) {
4337 				(void) fbioc_pause_resume(FALSE);
4338 
4339 				/* Restore volume */
4340 				fbioc_mute_off(s);
4341 			}
4342 			else
4343 				fbioc_start_stat_poll(s);
4344 		}
4345 		break;
4346 
4347 	default:
4348 		if (start)
4349 			DO_BEEP();
4350 		break;
4351 	}
4352 }
4353 
4354 
4355 /*
4356  * fbioc_warp
4357  *	Track warp function
4358  *
4359  * Args:
4360  *	s - Pointer to the curstat_t structure
4361  *
4362  * Return:
4363  *	Nothing.
4364  */
4365 void
fbioc_warp(curstat_t * s)4366 fbioc_warp(curstat_t *s)
4367 {
4368 	sword32_t	start_addr,
4369 			end_addr;
4370 	msf_t		start_msf,
4371 			end_msf;
4372 	int		i;
4373 
4374 	start_addr = s->curpos_tot.addr;
4375 	start_msf.min = s->curpos_tot.min;
4376 	start_msf.sec = s->curpos_tot.sec;
4377 	start_msf.frame = s->curpos_tot.frame;
4378 
4379 	switch (s->mode) {
4380 	case MOD_SAMPLE:
4381 		/* Go to normal play mode first */
4382 		fbioc_play_pause(s);
4383 
4384 		/*FALLTHROUGH*/
4385 	case MOD_PLAY:
4386 	case MOD_PAUSE:
4387 		if (s->shuffle || s->program) {
4388 			if ((i = di_curtrk_pos(s)) < 0) {
4389 				DO_BEEP();
4390 				return;
4391 			}
4392 
4393 			end_addr = s->trkinfo[i+1].addr;
4394 			end_msf.min = s->trkinfo[i+1].min;
4395 			end_msf.sec = s->trkinfo[i+1].sec;
4396 			end_msf.frame = s->trkinfo[i+1].frame;
4397 		}
4398 		else {
4399 			end_addr = s->discpos_tot.addr;
4400 			end_msf.min = s->discpos_tot.min;
4401 			end_msf.sec = s->discpos_tot.sec;
4402 			end_msf.frame = s->discpos_tot.frame;
4403 		}
4404 
4405 		if (s->segplay == SEGP_AB) {
4406 			if (start_addr < s->bp_startpos_tot.addr) {
4407 				start_addr = s->bp_startpos_tot.addr;
4408 				start_msf.min = s->bp_startpos_tot.min;
4409 				start_msf.sec = s->bp_startpos_tot.sec;
4410 				start_msf.frame = s->bp_startpos_tot.frame;
4411 			}
4412 
4413 			if (end_addr > s->bp_endpos_tot.addr) {
4414 				end_addr = s->bp_endpos_tot.addr;
4415 				end_msf.min = s->bp_endpos_tot.min;
4416 				end_msf.sec = s->bp_endpos_tot.sec;
4417 				end_msf.frame = s->bp_endpos_tot.frame;
4418 			}
4419 		}
4420 
4421 		if ((end_addr - app_data.min_playblks) < start_addr) {
4422 			/* No more left to play: just stop */
4423 			if (!fbioc_start_stop(FALSE, FALSE))
4424 				DO_BEEP();
4425 		}
4426 		else {
4427 			if (s->mode == MOD_PAUSE)
4428 				/* Mute: so we don't get a transient */
4429 				fbioc_mute_on(s);
4430 
4431 			if (!fbioc_do_playaudio(ADDR_BLK | ADDR_MSF,
4432 						start_addr, end_addr,
4433 						&start_msf, &end_msf,
4434 						0, 0)) {
4435 				DO_BEEP();
4436 
4437 				/* Restore volume */
4438 				fbioc_mute_off(s);
4439 				return;
4440 			}
4441 
4442 			if (s->mode == MOD_PAUSE) {
4443 				(void) fbioc_pause_resume(FALSE);
4444 
4445 				/* Restore volume */
4446 				fbioc_mute_off(s);
4447 			}
4448 		}
4449 		break;
4450 
4451 	default:
4452 		break;
4453 	}
4454 }
4455 
4456 
4457 /*
4458  * fbioc_route
4459  *	Channel routing function
4460  *
4461  * Args:
4462  *	s - Pointer to the curstat_t structure
4463  *
4464  * Return:
4465  *	Nothing.
4466  */
4467 void
fbioc_route(curstat_t * s)4468 fbioc_route(curstat_t *s)
4469 {
4470 	if (!app_data.chroute_supp)
4471 		return;
4472 
4473 	/* Only CDDA mode supports channel routing on FreeBSD/OpenBSD/NetBSD */
4474 	if (PLAYMODE_IS_CDDA(app_data.play_mode))
4475 		(void) cdda_chroute(fbioc_devp, s);
4476 }
4477 
4478 
4479 /*
4480  * fbioc_mute_on
4481  *	Mute audio function
4482  *
4483  * Args:
4484  *	s - Pointer to the curstat_t structure
4485  *
4486  * Return:
4487  *	Nothing.
4488  */
4489 void
fbioc_mute_on(curstat_t * s)4490 fbioc_mute_on(curstat_t *s)
4491 {
4492 	(void) fbioc_cfg_vol(0, s, FALSE);
4493 }
4494 
4495 
4496 /*
4497  * fbioc_mute_off
4498  *	Un-mute audio function
4499  *
4500  * Args:
4501  *	s - Pointer to the curstat_t structure
4502  *
4503  * Return:
4504  *	Nothing.
4505  */
4506 void
fbioc_mute_off(curstat_t * s)4507 fbioc_mute_off(curstat_t *s)
4508 {
4509 	(void) fbioc_cfg_vol((int) s->level, s, FALSE);
4510 }
4511 
4512 
4513 /*
4514  * fbioc_cddajitter
4515  *	CDDA jitter correction setting change notification
4516  *
4517  * Args:
4518  *	s - Pointer to the curstat_t structure
4519  *
4520  * Return:
4521  *	Nothing.
4522  */
4523 void
fbioc_cddajitter(curstat_t * s)4524 fbioc_cddajitter(curstat_t *s)
4525 {
4526 	sword32_t	curblk = 0;
4527 	byte_t		omode;
4528 
4529 	if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
4530 		omode = s->mode;
4531 		if (omode == MOD_PAUSE)
4532 			omode = MOD_PLAY;
4533 
4534 		/* Save state */
4535 		switch (omode) {
4536 		case MOD_PLAY:
4537 		case MOD_SAMPLE:
4538 			curblk = s->curpos_tot.addr;
4539 			fbioc_stop_stat_poll();
4540 			break;
4541 
4542 		default:
4543 			break;
4544 		}
4545 
4546 		/* Restart CDDA */
4547 		cdda_halt(fbioc_devp, s);
4548 		util_delayms(1000);
4549 
4550 		fbioc_cdda_client.curstat_addr = di_clinfo->curstat_addr;
4551 		fbioc_cdda_client.fatal_msg = di_clinfo->fatal_msg;
4552 		fbioc_cdda_client.warning_msg = di_clinfo->warning_msg;
4553 		fbioc_cdda_client.info_msg = di_clinfo->info_msg;
4554 		fbioc_cdda_client.info2_msg = di_clinfo->info2_msg;
4555 
4556 		(void) cdda_init(s, &fbioc_cdda_client);
4557 
4558 		/* Restore old state */
4559 		switch (omode) {
4560 		case MOD_PLAY:
4561 		case MOD_SAMPLE:
4562 			if (fbioc_play_recov(curblk, FALSE)) {
4563 				fbioc_start_stat_poll(s);
4564 
4565 				if (omode == MOD_PAUSE)
4566 					(void) fbioc_pause_resume(FALSE);
4567 			}
4568 			break;
4569 
4570 		default:
4571 			break;
4572 		}
4573 
4574 		s->mode = omode;
4575 	}
4576 }
4577 
4578 
4579 /*
4580  * fbioc_debug
4581  *	Debug level change notification
4582  *
4583  * Args:
4584  *	None.
4585  *
4586  * Return:
4587  *	Nothing.
4588  */
4589 void
fbioc_debug(void)4590 fbioc_debug(void)
4591 {
4592 	if (PLAYMODE_IS_CDDA(app_data.play_mode))
4593 		cdda_debug(app_data.debug);
4594 }
4595 
4596 
4597 /*
4598  * fbioc_start
4599  *	Start the FreeBSD/NetBSD/OpenBSD ioctl method.
4600  *
4601  * Args:
4602  *	s - Pointer to the curstat_t structure
4603  *
4604  * Return:
4605  *	Nothing.
4606  */
4607 void
fbioc_start(curstat_t * s)4608 fbioc_start(curstat_t *s)
4609 {
4610 	/* Check to see if disc is ready */
4611 	if (di_clinfo->timeout != NULL)
4612 		fbioc_start_insert_poll(s);
4613 	else
4614 		(void) fbioc_disc_ready(s);
4615 
4616 	/* Update display */
4617 	DPY_ALL(s);
4618 }
4619 
4620 
4621 /*
4622  * fbioc_icon
4623  *	Handler for main window iconification/de-iconification
4624  *
4625  * Args:
4626  *	s - Pointer to the curstat_t structure
4627  *	iconified - Whether the main window is iconified
4628  *
4629  * Return:
4630  *	Nothing.
4631  */
4632 void
fbioc_icon(curstat_t * s,bool_t iconified)4633 fbioc_icon(curstat_t *s, bool_t iconified)
4634 {
4635 	/* This function attempts to reduce the status polling frequency
4636 	 * when possible to cut down on CPU and bus usage.  This is
4637 	 * done when the CD player is iconified.
4638 	 */
4639 
4640 	/* Increase status polling interval by 4 seconds when iconified */
4641 	if (iconified)
4642 		fbioc_stat_interval = app_data.stat_interval + 4000;
4643 	else
4644 		fbioc_stat_interval = app_data.stat_interval;
4645 
4646 	switch (s->mode) {
4647 	case MOD_BUSY:
4648 	case MOD_NODISC:
4649 	case MOD_STOP:
4650 	case MOD_PAUSE:
4651 		break;
4652 
4653 	case MOD_SAMPLE:
4654 		/* No optimization in these modes */
4655 		fbioc_stat_interval = app_data.stat_interval;
4656 		break;
4657 
4658 	case MOD_PLAY:
4659 		if (!iconified) {
4660 			/* Force an immediate update */
4661 			fbioc_stop_stat_poll();
4662 			fbioc_start_stat_poll(s);
4663 		}
4664 		break;
4665 	}
4666 }
4667 
4668 
4669 /*
4670  * fbioc_halt
4671  *	Shut down the FreeBSD/NetBSD/OpenBSD ioctl method.
4672  *
4673  * Args:
4674  *	s - Pointer to the curstat_t structure
4675  *
4676  * Return:
4677  *	Nothing.
4678  */
4679 void
fbioc_halt(curstat_t * s)4680 fbioc_halt(curstat_t *s)
4681 {
4682 	/* If playing CDDA, stop it */
4683 	if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
4684 		switch (s->mode) {
4685 		case MOD_PLAY:
4686 		case MOD_PAUSE:
4687 		case MOD_SAMPLE:
4688 			(void) fbioc_start_stop(FALSE, FALSE);
4689 			s->mode = MOD_STOP;
4690 			fbioc_stop_stat_poll();
4691 			break;
4692 		default:
4693 			break;
4694 		}
4695 	}
4696 
4697 	/* Shut down CDDA */
4698 	cdda_halt(fbioc_devp, s);
4699 	app_data.play_mode = PLAYMODE_STD;
4700 
4701 	/* Re-enable front-panel eject button */
4702 	if (app_data.caddylock_supp)
4703 		fbioc_lock(s, FALSE);
4704 
4705 	if (s->mode != MOD_BUSY && s->mode != MOD_NODISC) {
4706 		if (app_data.exit_eject && app_data.eject_supp) {
4707 			/* User closing application: Eject disc */
4708 			(void) fbioc_start_stop(FALSE, TRUE);
4709 		}
4710 		else {
4711 			if (app_data.exit_stop)
4712 				/* User closing application: Stop disc */
4713 				fbioc_start_stop(FALSE, FALSE);
4714 
4715 			switch (s->mode) {
4716 			case MOD_PLAY:
4717 			case MOD_PAUSE:
4718 			case MOD_SAMPLE:
4719 				fbioc_stop_stat_poll();
4720 				break;
4721 			default:
4722 				break;
4723 			}
4724 		}
4725 	}
4726 
4727 	if (fbioc_devp != NULL) {
4728 		fbioc_close();
4729 		fbioc_not_open = TRUE;
4730 	}
4731 }
4732 
4733 
4734 /*
4735  * fbioc_methodstr
4736  *	Return a text string indicating the current method.
4737  *
4738  * Args:
4739  *	Nothing.
4740  *
4741  * Return:
4742  *	Method text string.
4743  */
4744 char *
fbioc_methodstr(void)4745 fbioc_methodstr(void)
4746 {
4747 #ifdef __FreeBSD__
4748 	return ("FreeBSD ioctl\n");
4749 #endif
4750 #ifdef __NetBSD__
4751 	return ("NetBSD ioctl\n");
4752 #endif
4753 #ifdef __OpenBSD__
4754 	return ("OpenBSD ioctl\n");
4755 #endif
4756 }
4757 
4758 
4759 #endif	/* DI_FBIOC DEMO_ONLY */
4760 
4761