1 /*
2  *   cdda - CD Digital Audio support
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  *   Sun Solaris support
24  *
25  *   This software fragment contains code that interfaces the
26  *   application to the Solaris operating environment.  The name "Sun"
27  *   and "Solaris" are used here for identification purposes only.
28  *
29  *   Contributing author: Darragh O'Brien <darragh.obrien@sun.com>
30  */
31 #ifndef lint
32 static char *_wr_sol_c_ident_ = "@(#)wr_sol.c	7.166 04/03/24";
33 #endif
34 
35 #include "common_d/appenv.h"
36 #include "common_d/util.h"
37 #include "libdi_d/libdi.h"
38 #include "cdda_d/cdda.h"
39 #include "cdda_d/common.h"
40 
41 #if defined(CDDA_WR_SOL) && defined(CDDA_SUPPORTED)
42 
43 #include <stropts.h>
44 #include <sys/stream.h>
45 #include <sys/audioio.h>
46 #include "cdda_d/wr_gen.h"
47 #include "cdda_d/wr_sol.h"
48 
49 extern appdata_t	app_data;
50 extern FILE		*errfp;
51 
52 STATIC int		sol_wsemid,		/* Semaphores identifier */
53 			sol_outport = 0;	/* Output port */
54 STATIC pid_t		sol_pipe_pid = -1;	/* Pipe to program pid */
55 STATIC cd_state_t	*sol_wcd;		/* Shared memory pointer */
56 STATIC bool_t		sol_write_dev,		/* Write to audio device */
57 			sol_write_file,		/* Write to output file */
58 			sol_write_pipe,		/* Pipe to program */
59 			sol_file_be = FALSE;	/* Big endian output file */
60 STATIC gen_desc_t	*sol_audio_desc = NULL,	/* Audio device desc */
61 			*sol_file_desc = NULL,	/* Output file desc */
62 			*sol_pipe_desc = NULL;	/* Pipe to program desc */
63 STATIC unsigned int	sol_file_mode,		/* Output file mode */
64 			sol_curr_vol,		/* Current volume */
65 			sol_curr_bal;		/* Current balance */
66 
67 
68 /*
69  * solaud_scale_vol
70  *	Scale volume from xmcd 0-100 to Solaris audio device 0-SOL_MAX_VOL
71  *
72  * Args:
73  *	vol - xmcd volume 0-100
74  *
75  * Return:
76  *	Solaris volume 0-SOL_MAX_VOL
77  */
78 STATIC unsigned int
solaud_scale_vol(int vol)79 solaud_scale_vol(int vol)
80 {
81 	unsigned int	ret;
82 
83 	ret = (vol * SOL_MAX_VOL) / 100;
84 	if (((vol * SOL_MAX_VOL) % 100) >= 50)
85 		ret++;
86 
87 	/* Range checking */
88 	if (ret > SOL_MAX_VOL)
89 		ret = SOL_MAX_VOL;
90 
91 	return (ret);
92 }
93 
94 
95 /*
96  * solaud_unscale_vol
97  *	Scale volume from Solaris audio device 0-SOL_MAX_VOL to xmcd 0-100
98  *
99  * Args:
100  *	vol - Solaris volume 0-SOL_MAX_VOL
101  *
102  * Return:
103  *	xmcd volume 0-100
104  */
105 STATIC int
solaud_unscale_vol(unsigned int vol)106 solaud_unscale_vol(unsigned int vol)
107 {
108 	int	ret;
109 
110 	ret = (vol * 100) / SOL_MAX_VOL;
111 	if (((vol * 100) % SOL_MAX_VOL) >= SOL_HALF_VOL)
112 		ret++;
113 
114 	/* Range checking */
115 	if (ret < 0)
116 		ret = 0;
117 	else if (ret > 100)
118 		ret = 100;
119 
120 	return (ret);
121 }
122 
123 
124 /*
125  * solaud_scale_bal
126  *	Convert xmcd individual channel volume percentage values to
127  *	Solaris audio balance value.
128  *
129  * Args:
130  *	vol_left  - left volume percentage
131  *	vol_right - right volume percentage
132  *
133  * Return:
134  *	Solaris balance SOL_L_BAL-SOL_R_BAL
135  */
136 STATIC uchar_t
solaud_scale_bal(int vol_left,int vol_right)137 solaud_scale_bal(int vol_left, int vol_right)
138 {
139 	uchar_t	ret = SOL_M_BAL;
140 
141 	if (vol_left == 100 && vol_right == 100) {
142 		ret = SOL_M_BAL;
143 	}
144 	else if (vol_right < 100) {
145 		ret = (vol_right * SOL_M_BAL) / 100;
146 		if (((vol_right * SOL_M_BAL) % 100) >= 50)
147 			ret++;
148 	}
149 	else if (vol_left < 100) {
150 		ret = ((vol_left * SOL_M_BAL) / 100) +
151 			SOL_M_BAL;
152 		if (((vol_left * SOL_M_BAL) % 100) >= 50)
153 			ret++;
154 	}
155 
156 	/* Range checking */
157 	if (ret > SOL_R_BAL)
158 		ret = SOL_R_BAL;
159 
160 	return (ret);
161 }
162 
163 
164 /*
165  * solaud_unscale_bal
166  *	Convert Solaris audio balance value to xmcd individual channel
167  *	volume percentage values.
168  *
169  * Args:
170  *	bal       - Solaris audio balance value
171  *	vol_left  - left volume percentage (return)
172  *	vol_right - right volume percentage (return)
173  *
174  * Return:
175  *	Nothing.
176  */
177 STATIC void
solaud_unscale_bal(uchar_t bal,int * vol_left,int * vol_right)178 solaud_unscale_bal(uchar_t bal, int *vol_left, int *vol_right)
179 {
180 	if (bal > SOL_R_BAL)
181 		bal = SOL_R_BAL;
182 
183 	if (bal == SOL_M_BAL) {
184 		*vol_left = 100;
185 		*vol_right = 100;
186 	}
187 	else if (bal < SOL_M_BAL) {
188 		*vol_left = 100;
189 		*vol_right = (bal * 100) / SOL_M_BAL;
190 		if (((bal * 100) % SOL_M_BAL) >= SOL_HALF_VOL)
191 			(*vol_right)++;
192 	}
193 	else if (bal > SOL_M_BAL) {
194 		bal -= SOL_M_BAL;
195 		*vol_left = (bal * 100) / SOL_M_BAL;
196 		*vol_right = 100;
197 		if (((bal * 100) % SOL_M_BAL) >= SOL_HALF_VOL)
198 			(*vol_left)++;
199 	}
200 
201 	/* Range checking */
202 	if (*vol_left < 0)
203 		*vol_left = 0;
204 	else if (*vol_left > 100)
205 		*vol_left = 100;
206 
207 	if (*vol_right < 0)
208 		*vol_right = 0;
209 	else if (*vol_right > 100)
210 		*vol_right = 100;
211 }
212 
213 
214 /*
215  * solaud_get_info
216  *	Get audio hardware settings.
217  *
218  * Args:
219  *	info - Info to return
220  *
221  * Return:
222  *	FALSE - failure
223  *	TRUE  - success
224  */
225 STATIC bool_t
solaud_get_info(audio_info_t * info)226 solaud_get_info(audio_info_t *info)
227 {
228 	/* Retrieve settings */
229 	if (!gen_ioctl(sol_audio_desc, AUDIO_GETINFO, info)) {
230 		(void) sprintf(sol_wcd->i->msgbuf,
231 			"solaud_get_info: AUDIO_GETINFO failed: %s",
232 			strerror(errno));
233 		DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
234 		return FALSE;
235 	}
236 
237 	return TRUE;
238 }
239 
240 
241 /*
242  * solaud_set_info
243  *	Set audio hardware settings.
244  *
245  * Args:
246  *	info - Info to set
247  *
248  * Return:
249  *	FALSE - failure
250  *	TRUE  - success
251  */
252 STATIC bool_t
solaud_set_info(audio_info_t * info)253 solaud_set_info(audio_info_t *info)
254 {
255 	/* Apply settings */
256 	if (!gen_ioctl(sol_audio_desc, AUDIO_SETINFO, info)) {
257 		(void) sprintf(sol_wcd->i->msgbuf,
258 			"solaud_set_info: AUDIO_SETINFO failed: %s",
259 			strerror(errno));
260 		return FALSE;
261 	}
262 
263 	return TRUE;
264 }
265 
266 
267 /*
268  * solaud_flush
269  *	Wrapper for STREAMS I_FLUSH ioctl.
270  *
271  * Args:
272  *	gdp - Pointer to the gen_desc_t structure
273  *
274  * Return:
275  *	FALSE - failure
276  *	TRUE  - success
277  */
278 STATIC bool_t
solaud_flush(gen_desc_t * gdp)279 solaud_flush(gen_desc_t *gdp)
280 {
281 	/* Apply settings */
282 	if (!gen_ioctl(gdp, I_FLUSH, (void *) FLUSHRW)) {
283 		(void) sprintf(sol_wcd->i->msgbuf,
284 			"solaud_flush: I_FLUSH failed: %s",
285 			strerror(errno));
286 		DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
287 		return FALSE;
288 	}
289 
290 	return TRUE;
291 }
292 
293 
294 /*
295  * solaud_drain
296  *	Wrapper for AUDIO_DRAIN ioctl.
297  *
298  * Args:
299  *	gdp - Pointer to the gen_desc_t structure
300  *
301  * Return:
302  *	FALSE - failure
303  *	TRUE  - success
304  */
305 STATIC bool_t
solaud_drain(gen_desc_t * gdp)306 solaud_drain(gen_desc_t *gdp)
307 {
308 	/* Apply settings */
309 	if (!gen_ioctl(gdp, AUDIO_DRAIN, NULL)) {
310 		(void) sprintf(sol_wcd->i->msgbuf,
311 				"solaud_drain: AUDIO_DRAIN failed: %s",
312 				strerror(errno));
313 		DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
314 		return FALSE;
315 	}
316 
317 	return TRUE;
318 }
319 
320 
321 /*
322  * solaud_open_dev
323  *	Opens the audio device.
324  *
325  * Args:
326  *	None.
327  *
328  * Return:
329  *	FALSE - failure
330  *	TRUE  - success
331  */
332 STATIC bool_t
solaud_open_dev(void)333 solaud_open_dev(void)
334 {
335 	char		*cp;
336 	struct stat	stbuf;
337 
338 	if (!gen_set_eid(sol_wcd))
339 		return FALSE;
340 
341 	/* Use environment variable if set */
342 	if ((cp = getenv("AUDIODEV")) == NULL)
343 		cp = DEFAULT_DEV_AUDIO;
344 
345 	/* Check to make sure it's a device */
346 	if (stat(cp, &stbuf) < 0) {
347 		(void) sprintf(sol_wcd->i->msgbuf,
348 				"solaud_open_dev: stat of %s failed: %s",
349 				cp, strerror(errno));
350 		DBGPRN(DBG_GEN)(errfp, "%s\n", sol_wcd->i->msgbuf);
351 		(void) gen_reset_eid(sol_wcd);
352 		return FALSE;
353 	}
354 	if (!S_ISCHR(stbuf.st_mode)) {
355 		(void) sprintf(sol_wcd->i->msgbuf,
356 				"solaud_open_dev: %s is not a "
357 				"character device",
358 				cp);
359 		DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
360 		(void) gen_reset_eid(sol_wcd);
361 		return FALSE;
362 	}
363 
364 	/* Open audio device */
365 	if ((sol_audio_desc = gen_open_file(sol_wcd, cp, O_WRONLY, 0,
366 					    FILEFMT_RAW, 0)) == NULL) {
367 		(void) sprintf(sol_wcd->i->msgbuf,
368 				"solaud_open_dev: open of %s failed: %s",
369 				cp, strerror(errno));
370 		DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
371 		(void) gen_reset_eid(sol_wcd);
372 		return FALSE;
373 	}
374 
375 	(void) gen_reset_eid(sol_wcd);
376 	return TRUE;
377 }
378 
379 
380 /*
381  * solaud_close_dev
382  *	Closes the audio device.
383  *
384  * Args:
385  *	None.
386  *
387  * Return:
388  *	FALSE - failure
389  *	TRUE  - success
390  */
391 STATIC bool_t
solaud_close_dev(void)392 solaud_close_dev(void)
393 {
394 	if (!gen_set_eid(sol_wcd))
395 		return FALSE;
396 
397 	/* Close audio device */
398 	if (!gen_close_file(sol_audio_desc)) {
399 		DBGPRN(DBG_SND)(errfp,
400 			"solaud_close_dev: close failed: %s\n",
401 			strerror(errno));
402 		(void) gen_reset_eid(sol_wcd);
403 		return FALSE;
404 	}
405 
406 	sol_audio_desc = NULL;
407 
408 	(void) gen_reset_eid(sol_wcd);
409 	return TRUE;
410 }
411 
412 
413 /*
414  * solaud_config
415  *	Sets up audio device for playing CD quality audio.
416  *
417  * Args:
418  *	None.
419  *
420  * Return:
421  *	FALSE - failure
422  *	TRUE  - success
423  */
424 STATIC bool_t
solaud_config(void)425 solaud_config(void)
426 {
427 	audio_info_t	solaud_info;
428 
429 	/* Initialize */
430 	SOL_AUDIO_INIT(&solaud_info, sizeof(audio_info_t));
431 
432 	solaud_info.play.sample_rate = 44100;
433 	solaud_info.play.channels = 2;
434 	solaud_info.play.precision = 16;
435 	solaud_info.play.encoding = AUDIO_ENCODING_LINEAR;
436 
437 	/* Select audio output port */
438 	if (sol_wcd->i->outport != 0 && sol_wcd->i->outport != sol_outport) {
439 		solaud_info.play.port = 0;
440 		if ((sol_wcd->i->outport & CDDA_OUT_SPEAKER) != 0)
441 			solaud_info.play.port |= AUDIO_SPEAKER;
442 		if ((sol_wcd->i->outport & CDDA_OUT_HEADPHONE) != 0)
443 			solaud_info.play.port |= AUDIO_HEADPHONE;
444 		if ((sol_wcd->i->outport & CDDA_OUT_LINEOUT) != 0)
445 			solaud_info.play.port |= AUDIO_LINE_OUT;
446 
447 		sol_outport = sol_wcd->i->outport;
448 	}
449 
450 	/* Apply new settings */
451 	if (!solaud_set_info(&solaud_info))
452 		return FALSE;
453 
454 	/* Retrieve current settings */
455 	if (!solaud_get_info(&solaud_info))
456 		return FALSE;
457 
458 	sol_curr_vol = sol_curr_bal = -1;
459 
460 	/* Update volume */
461 	sol_wcd->i->vol = util_untaper_vol(
462 		solaud_unscale_vol(solaud_info.play.gain)
463 	);
464 
465 	/* Update balance */
466 	solaud_unscale_bal(solaud_info.play.balance,
467 			   &sol_wcd->i->vol_left, &sol_wcd->i->vol_right);
468 
469 	return TRUE;
470 }
471 
472 
473 /*
474  * solaud_write_eof
475  *	Puts an eof marker in the audio stream.
476  *
477  * Args:
478  *	gdp - Pointer to the gen_desc_t structure
479  *
480  * Return:
481  *	FALSE - failure
482  *	TRUE  - success
483  */
484 STATIC bool_t
solaud_write_eof(gen_desc_t * gdp)485 solaud_write_eof(gen_desc_t *gdp)
486 {
487 	if (!gen_set_eid(sol_wcd))
488 		return FALSE;
489 
490 	if (!gen_write_chunk(gdp, NULL, 0)) {
491 		(void) sprintf(sol_wcd->i->msgbuf,
492 				"solaud_write_eof: write failed: %s",
493 				strerror(errno));
494 		DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
495 
496 		(void) gen_reset_eid(sol_wcd);
497 		return FALSE;
498 	}
499 
500 	(void) gen_reset_eid(sol_wcd);
501 	return TRUE;
502 }
503 
504 
505 /*
506  * solaud_set_paused
507  *	Update Solaris audio driver paused state based on current status.
508  *
509  * Args:
510  *	state - The current paused state
511  *
512  * Return:
513  *	The current paused state
514  */
515 STATIC int
solaud_paused(int state)516 solaud_paused(int state)
517 {
518 	audio_info_t	solaud_info;
519 	static int	prev_state = CDSTAT_NOSTATUS;
520 
521 	if (prev_state == state)
522 		return (state);	/* No change */
523 
524 	if (sol_write_dev) {
525 		if (!solaud_get_info(&solaud_info))
526 			return (state);	/* Can't get info, just return */
527 
528 		/* Update paused */
529 		solaud_info.play.pause = (state == CDSTAT_PAUSED) ? 1 : 0;
530 
531 		/* Apply new settings */
532 		(void) solaud_set_info(&solaud_info);
533 	}
534 
535 	prev_state = state;
536 	return (state);
537 }
538 
539 
540 /*
541  * solaud_update
542  *	Updates hardware volume and balance settings from GUI.
543  *
544  * Args:
545  *	None.
546  *
547  * Return:
548  *	Nothing.
549  */
550 STATIC void
solaud_update(void)551 solaud_update(void)
552 {
553 	audio_info_t	solaud_info;
554 	int		vol,
555 			bal;
556 	static int	curr_vol = -1,
557 			curr_bal = -1;
558 
559 	app_data.vol_taper = sol_wcd->i->vol_taper;
560 
561 	vol = solaud_scale_vol(util_taper_vol(sol_wcd->i->vol));
562 	bal = solaud_scale_bal(sol_wcd->i->vol_left, sol_wcd->i->vol_right);
563 
564 	if (vol == curr_vol && bal == curr_bal &&
565 	    (sol_wcd->i->outport == 0 || sol_wcd->i->outport == sol_outport))
566 		/* No change */
567 		return;
568 
569 	/* Retrieve current settings */
570 	if (!solaud_get_info(&solaud_info))
571 		return;
572 
573 	/* Select audio output port */
574 	if (sol_wcd->i->outport != 0 && sol_wcd->i->outport != sol_outport) {
575 		solaud_info.play.port = 0;
576 		if ((sol_wcd->i->outport & CDDA_OUT_SPEAKER) != 0)
577 			solaud_info.play.port |= AUDIO_SPEAKER;
578 		if ((sol_wcd->i->outport & CDDA_OUT_HEADPHONE) != 0)
579 			solaud_info.play.port |= AUDIO_HEADPHONE;
580 		if ((sol_wcd->i->outport & CDDA_OUT_LINEOUT) != 0)
581 			solaud_info.play.port |= AUDIO_LINE_OUT;
582 
583 		sol_outport = sol_wcd->i->outport;
584 	}
585 
586 	curr_vol = vol;
587 	curr_bal = bal;
588 
589 	/* Update volume and balance */
590 	solaud_info.play.gain = (unsigned int) vol;
591 	solaud_info.play.balance = (unsigned int) bal;
592 
593 	/* Apply new settings */
594 	(void) solaud_set_info(&solaud_info);
595 }
596 
597 
598 /*
599  * solaud_status
600  *	Updates volume and balance settings from audio device.
601  *
602  * Args:
603  *	None.
604  *
605  * Return:
606  *	Nothing.
607  */
608 STATIC void
solaud_status(void)609 solaud_status(void)
610 {
611 	audio_info_t	solaud_info;
612 
613 	/* Retrieve current settings */
614 	if (!solaud_get_info(&solaud_info))
615 		return;
616 
617 	if (solaud_info.play.gain == sol_curr_vol &&
618 	    solaud_info.play.balance == sol_curr_bal)
619 		/* No change */
620 		return;
621 
622 	sol_curr_vol = solaud_info.play.gain;
623 	sol_curr_bal = solaud_info.play.balance;
624 
625 	/* Update volume and balance */
626 	sol_wcd->i->vol = util_untaper_vol(solaud_unscale_vol(sol_curr_vol));
627 	solaud_unscale_bal(sol_curr_bal,
628 			   &sol_wcd->i->vol_left, &sol_wcd->i->vol_right);
629 }
630 
631 
632 /*
633  * solaud_write
634  *	Function responsible for writing from the buffer to the audio
635  *	device (and file if saving). Each chunk is followed by an eof
636  *	marker.
637  *
638  * Args:
639  *	s - Pointer to the curstat_t structure
640  *
641  * Return:
642  *	FALSE - failure
643  *	TRUE  - success
644  */
645 STATIC bool_t
solaud_write(curstat_t * s)646 solaud_write(curstat_t *s)
647 {
648 	byte_t	*in_data,
649 		*sw_data,
650 		*le_data,
651 		*be_data,
652 		*dev_data,
653 		*file_data,
654 		*pipe_data;
655 	char	*fname = NULL;
656 	int	trk_idx = -1,
657 		hbcnt = 1;
658 	time_t	start_time,
659 		tot_time;
660 	bool_t	ret;
661 
662 	/* Get memory for audio device write buffer */
663 	le_data = (byte_t *) MEM_ALLOC("le_data", sol_wcd->cds->chunk_bytes);
664 	if (le_data == NULL) {
665 		(void) sprintf(sol_wcd->i->msgbuf,
666 				"solaud_write: out of memory");
667 		DBGPRN(DBG_GEN)(errfp, "%s\n", sol_wcd->i->msgbuf);
668 		return FALSE;
669 	}
670 
671 	/* Get memory for audio file write buffer */
672 	be_data = (byte_t *) MEM_ALLOC("be_data", sol_wcd->cds->chunk_bytes);
673 	if (be_data == NULL) {
674 		MEM_FREE(le_data);
675 		(void) sprintf(sol_wcd->i->msgbuf,
676 				"solaud_write: out of memory");
677 		DBGPRN(DBG_GEN)(errfp, "%s\n", sol_wcd->i->msgbuf);
678 		return FALSE;
679 	}
680 
681 	(void) memset(le_data, 0, sol_wcd->cds->chunk_bytes);
682 	(void) memset(be_data, 0, sol_wcd->cds->chunk_bytes);
683 
684 	in_data = app_data.cdda_bigendian ? be_data : le_data;
685 	sw_data = app_data.cdda_bigendian ? le_data : be_data;
686 
687 	/* Start time */
688 	start_time = time(NULL);
689 
690 	/* While not stopped */
691 	ret = TRUE;
692 	while (sol_wcd->i->state != CDSTAT_COMPLETED) {
693 		/* Get lock */
694 		cdda_waitsem(sol_wsemid, LOCK);
695 
696 		/* End if finished and nothing in the buffer */
697 		if (sol_wcd->cdb->occupied == 0 && sol_wcd->i->cdda_done) {
698 			if (sol_write_dev && !solaud_drain(sol_audio_desc)) {
699 				ret = FALSE;
700 				break;
701 			}
702 			sol_wcd->i->state = CDSTAT_COMPLETED;
703 			cdda_postsem(sol_wsemid, LOCK);
704 			break;
705 		}
706 
707 		/* Wait until there is something there */
708 		while (sol_wcd->cdb->occupied <= 0 &&
709 		       sol_wcd->i->state != CDSTAT_COMPLETED) {
710 			cdda_postsem(sol_wsemid, LOCK);
711 			cdda_waitsem(sol_wsemid, DATA);
712 			cdda_waitsem(sol_wsemid, LOCK);
713 		}
714 
715 		/* Break if stopped */
716 		if (sol_wcd->i->state == CDSTAT_COMPLETED) {
717 			cdda_postsem(sol_wsemid, LOCK);
718 			break;
719 		}
720 
721 		/* Set the current track and track length info */
722 		cdda_wr_setcurtrk(sol_wcd);
723 
724 		/* Copy a chunk from the nextout slot into the data buffers */
725 		(void) memcpy(
726 			in_data,
727 			&sol_wcd->cdb->b[
728 			    sol_wcd->cds->chunk_bytes * sol_wcd->cdb->nextout
729 			],
730 			sol_wcd->cds->chunk_bytes
731 		);
732 
733 		/* Update pointers */
734 		sol_wcd->cdb->nextout++;
735 		sol_wcd->cdb->nextout %= sol_wcd->cds->buffer_chunks;
736 		sol_wcd->cdb->occupied--;
737 
738 		/* Set heartbeat and interval, update stats */
739 		if (--hbcnt == 0) {
740 			cdda_heartbeat(&sol_wcd->i->writer_hb, CDDA_HB_WRITER);
741 
742 			tot_time = time(NULL) - start_time;
743 			if (tot_time > 0 && sol_wcd->i->frm_played > 0) {
744 				sol_wcd->i->frm_per_sec = (int)
745 					((float) sol_wcd->i->frm_played /
746 					 (float) tot_time) + 0.5;
747 			}
748 
749 			hbcnt = cdda_heartbeat_interval(
750 				sol_wcd->i->frm_per_sec
751 			);
752 		}
753 
754 		/* Signal room available */
755 		cdda_postsem(sol_wsemid, ROOM);
756 
757 		/* Release lock */
758 		cdda_postsem(sol_wsemid, LOCK);
759 
760 		/* Change channel routing and attenuation if necessary */
761 		gen_chroute_att(sol_wcd->i->chroute,
762 				sol_wcd->i->att,
763 				(sword16_t *)(void *) in_data,
764 				(size_t) sol_wcd->cds->chunk_bytes);
765 
766 		/* Prepare byte-swapped version of the data */
767 		gen_byteswap(in_data, sw_data,
768 			     (size_t) sol_wcd->cds->chunk_bytes);
769 
770 		/* Write to device */
771 		if (sol_write_dev) {
772 			/* Feed native endian data to the audio driver */
773 #if _BYTE_ORDER_ == _B_ENDIAN_
774 			dev_data = be_data;
775 #else
776 			dev_data = le_data;
777 #endif
778 
779 			if (!gen_write_chunk(sol_audio_desc, dev_data,
780 					(size_t) sol_wcd->cds->chunk_bytes)) {
781 				sol_wcd->i->state = CDSTAT_COMPLETED;
782 				cdda_postsem(sol_wsemid, LOCK);
783 				ret = FALSE;
784 				break;
785 			}
786 
787 			if (!solaud_write_eof(sol_audio_desc)) {
788 				sol_wcd->i->state = CDSTAT_COMPLETED;
789 				cdda_postsem(sol_wsemid, LOCK);
790 				ret = FALSE;
791 				break;
792 			}
793 
794 			/* Update volume and balance settings */
795 			solaud_update();
796 
797 			if ((sol_wcd->i->frm_played % FRAME_PER_SEC) == 0)
798 				solaud_status();
799 		}
800 
801 		/* Write to file */
802 		if (sol_write_file) {
803 			file_data = sol_file_be ? be_data : le_data;
804 
805 			/* Open new track file if necessary */
806 			if (!app_data.cdda_trkfile) {
807 				fname = s->trkinfo[0].outfile;
808 			}
809 			else if (trk_idx != sol_wcd->i->trk_idx) {
810 				trk_idx = sol_wcd->i->trk_idx;
811 
812 				if (sol_file_desc != NULL &&
813 				    !gen_close_file(sol_file_desc)) {
814 					sol_wcd->i->state = CDSTAT_COMPLETED;
815 					cdda_postsem(sol_wsemid, LOCK);
816 					ret = FALSE;
817 					break;
818 				}
819 
820 				fname = s->trkinfo[trk_idx].outfile;
821 				sol_file_desc = gen_open_file(
822 					sol_wcd,
823 					fname,
824 					O_WRONLY | O_TRUNC | O_CREAT,
825 					(mode_t) sol_file_mode,
826 					app_data.cdda_filefmt,
827 					sol_wcd->i->trk_len
828 				);
829 				if (sol_file_desc == NULL) {
830 					sol_wcd->i->state = CDSTAT_COMPLETED;
831 					cdda_postsem(sol_wsemid, LOCK);
832 					ret = FALSE;
833 					break;
834 				}
835 			}
836 
837 			if (!gen_write_chunk(sol_file_desc, file_data,
838 					(size_t) sol_wcd->cds->chunk_bytes)) {
839 				sol_wcd->i->state = CDSTAT_COMPLETED;
840 				cdda_postsem(sol_wsemid, LOCK);
841 				ret = FALSE;
842 				break;
843 			}
844 		}
845 
846 		/* Pipe to program */
847 		if (sol_write_pipe) {
848 			pipe_data = sol_file_be ? be_data : le_data;
849 
850 			if (!gen_write_chunk(sol_pipe_desc, pipe_data,
851 					(size_t) sol_wcd->cds->chunk_bytes)) {
852 				sol_wcd->i->state = CDSTAT_COMPLETED;
853 				cdda_postsem(sol_wsemid, LOCK);
854 				ret = FALSE;
855 				break;
856 			}
857 		}
858 
859 		sol_wcd->i->frm_played += sol_wcd->cds->chunk_blocks;
860 
861 		while (solaud_paused(sol_wcd->i->state) == CDSTAT_PAUSED)
862 			util_delayms(400);
863 
864 		/* Update debug level */
865 		app_data.debug = sol_wcd->i->debug;
866 	}
867 
868 	/* Wake up reader */
869 	cdda_postsem(sol_wsemid, ROOM);
870 
871 	/* Free memory */
872 	MEM_FREE(le_data);
873 	MEM_FREE(be_data);
874 
875 	return (ret);
876 }
877 
878 
879 /*
880  * sol_winit
881  *	Pre-playback support check function
882  *
883  * Args:
884  *	None.
885  *
886  * Return:
887  *	Bitmask of supported features
888  */
889 word32_t
sol_winit(void)890 sol_winit(void)
891 {
892 #if defined(i386) || defined(__i386__)
893 	return (CDDA_WRITEDEV | CDDA_WRITEFILE | CDDA_WRITEPIPE);
894 #else
895 	int	arch = ((gethostid() >> 24) & ARCH_MASK);
896 
897 	switch (arch) {
898 	case ARCH_SUN2:
899 	case ARCH_SUN3:
900 	case ARCH_SUN4C:
901 	case ARCH_SUN4E:
902 		DBGPRN(DBG_SND)(errfp, "sol_winit: %s\n%s\n",
903 			"CD-quality stereo playback is not supported on",
904 			"this machine architecture.");
905 		return (CDDA_WRITEFILE | CDDA_WRITEPIPE);
906 
907 	case ARCH_SUN4M:
908 	case ARCH_SUNX:
909 	default:
910 		return (CDDA_WRITEDEV | CDDA_WRITEFILE | CDDA_WRITEPIPE);
911 	}
912 #endif
913 }
914 
915 
916 /*
917  * sol_write
918  *	Opens audio device/file, attaches shared memory and semaphores.
919  *	Continuously reads data from shared memory and writes it
920  *	to the audio device/file.
921  *
922  * Args:
923  *	s - Pointer to the curstat_t structure
924  *
925  * Return:
926  *	FALSE - failure
927  *	TRUE  - success
928  */
929 bool_t
sol_write(curstat_t * s)930 sol_write(curstat_t *s)
931 {
932 	size_t	estlen;
933 
934 	if (!PLAYMODE_IS_CDDA(app_data.play_mode)) {
935 		(void) fprintf(errfp, "sol_write: Nothing to do.\n");
936 		return FALSE;
937 	}
938 
939 	sol_write_dev  = (bool_t) ((app_data.play_mode & PLAYMODE_CDDA) != 0);
940 	sol_write_file = (bool_t) ((app_data.play_mode & PLAYMODE_FILE) != 0);
941 	sol_write_pipe = (bool_t) ((app_data.play_mode & PLAYMODE_PIPE) != 0);
942 
943 	/* Generic services initialization */
944 	gen_write_init();
945 
946 	/* Allocate memory */
947 	sol_wcd = (cd_state_t *) MEM_ALLOC("cd_state_t", sizeof(cd_state_t));
948 	if (sol_wcd == NULL) {
949 		(void) fprintf(errfp, "sol_write: out of memory\n");
950 		return FALSE;
951 	}
952 	(void) memset(sol_wcd, 0, sizeof(cd_state_t));
953 
954 	/* Initialize shared memory and semaphores */
955 	if ((sol_wsemid = cdda_initipc(sol_wcd)) < 0)
956 		return FALSE;
957 
958 	(void) sscanf(app_data.cdinfo_filemode, "%o", &sol_file_mode);
959 	/* Make sure file is at least accessible by user */
960 	sol_file_mode |= S_IRUSR | S_IWUSR;
961 	sol_file_mode &= ~(S_ISUID | S_ISGID | S_IWGRP | S_IWOTH);
962 
963 	estlen = (sol_wcd->i->end_lba - sol_wcd->i->start_lba + 1) *
964 		 CDDA_BLKSZ;
965 
966 	/* Set heartbeat */
967 	cdda_heartbeat(&sol_wcd->i->writer_hb, CDDA_HB_WRITER);
968 
969 	if (sol_write_file || sol_write_pipe) {
970 		/* Open file and/or pipe */
971 		if (!app_data.cdda_trkfile && sol_write_file) {
972 			sol_file_desc = gen_open_file(
973 				sol_wcd,
974 				s->trkinfo[0].outfile,
975 				O_WRONLY | O_TRUNC | O_CREAT,
976 				(mode_t) sol_file_mode,
977 				app_data.cdda_filefmt,
978 				estlen
979 			);
980 			if (sol_file_desc == NULL)
981 				return FALSE;
982 		}
983 
984 		if (sol_write_pipe) {
985 			sol_pipe_desc = gen_open_pipe(
986 				sol_wcd,
987 				app_data.pipeprog,
988 				&sol_pipe_pid,
989 				app_data.cdda_filefmt,
990 				estlen
991 			);
992 			if (sol_pipe_desc == NULL)
993 				return FALSE;
994 
995 			DBGPRN(DBG_SND)(errfp,
996 					  "\nsolaud_write: Pipe to [%s]: "
997 					  "chunk_blks=%d\n",
998 					  app_data.pipeprog,
999 					  sol_wcd->cds->chunk_blocks);
1000 		}
1001 
1002 		/* Set endian */
1003 		sol_file_be = gen_filefmt_be(app_data.cdda_filefmt);
1004 	}
1005 
1006 	if (sol_write_dev) {
1007 		/* Open audio device */
1008 		if (!solaud_open_dev())
1009 			return FALSE;
1010 
1011 		/* Configure audio */
1012 		if (!solaud_config())
1013 			return FALSE;
1014 
1015 		/* Initial settings */
1016 		solaud_update();
1017 	}
1018 
1019 	/* Do the writing */
1020 	if (!solaud_write(s))
1021 		return FALSE;
1022 
1023 	/* Flush audio */
1024 	if (sol_write_dev && !solaud_flush(sol_audio_desc))
1025 		return FALSE;
1026 
1027 	return TRUE;
1028 }
1029 
1030 
1031 /*
1032  * sol_wdone
1033  *	Post-playback cleanup function
1034  *
1035  * Args:
1036  *	killreader - whether to terminate the reader thread or process
1037  *
1038  * Return:
1039  *	Nothing.
1040  */
1041 void
sol_wdone(bool_t killreader)1042 sol_wdone(bool_t killreader)
1043 {
1044 	DBGPRN(DBG_GEN)(errfp, "\nsol_wdone: Cleaning up writer\n");
1045 
1046 	if (sol_audio_desc != NULL)
1047 		(void) solaud_close_dev();
1048 
1049 	if (sol_file_desc != NULL) {
1050 		(void) gen_close_file(sol_file_desc);
1051 		sol_file_desc = NULL;
1052 	}
1053 
1054 	if (sol_pipe_desc != NULL) {
1055 		(void) gen_close_pipe(sol_pipe_desc);
1056 		sol_pipe_desc = NULL;
1057 	}
1058 
1059 	cdda_yield();
1060 
1061 	if (sol_pipe_pid > 0) {
1062 		(void) kill(sol_pipe_pid, SIGTERM);
1063 		sol_pipe_pid = -1;
1064 	}
1065 
1066 	if (sol_wcd != NULL) {
1067 		if (sol_wcd->i != NULL) {
1068 			if (killreader && sol_wcd->i->reader != (thid_t) 0) {
1069 				cdda_kill(sol_wcd->i->reader, SIGTERM);
1070 				sol_wcd->i->state = CDSTAT_COMPLETED;
1071 			}
1072 
1073 			/* Reset frames played counter */
1074 			sol_wcd->i->frm_played = 0;
1075 		}
1076 
1077 		MEM_FREE(sol_wcd);
1078 		sol_wcd = NULL;
1079 	}
1080 
1081 	sol_write_dev = sol_write_file = sol_write_pipe = FALSE;
1082 }
1083 
1084 
1085 /*
1086  * sol_winfo
1087  *	Append method identification to informational string.
1088  *
1089  * Args:
1090  *	str - The informational string
1091  *
1092  * Return:
1093  *	Nothing.
1094  */
1095 void
sol_winfo(char * str)1096 sol_winfo(char *str)
1097 {
1098 	(void) strcat(str, "Solaris audio driver\n");
1099 }
1100 
1101 #endif	/* CDDA_WR_SOL CDDA_SUPPORTED */
1102 
1103