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  *   HP-UX audio driver support
24  */
25 #ifndef lint
26 static char *_wr_hpux_c_ident_ = "@(#)wr_hpux.c	7.66 04/03/24";
27 #endif
28 
29 #include "common_d/appenv.h"
30 #include "common_d/util.h"
31 #include "libdi_d/libdi.h"
32 #include "cdda_d/cdda.h"
33 #include "cdda_d/common.h"
34 
35 #if defined(CDDA_WR_HPUX) && defined(CDDA_SUPPORTED)
36 
37 #include <sys/audio.h>
38 #include "cdda_d/wr_gen.h"
39 #include "cdda_d/wr_hpux.h"
40 
41 
42 extern appdata_t	app_data;
43 extern FILE		*errfp;
44 
45 STATIC int		hpux_wsemid,		/* Semaphores identifier */
46 			hpux_outport = 0,	/* Output port */
47 			hpux_vol_max,		/* Max volume */
48 			hpux_vol_half,		/* Half volume */
49 			hpux_vol_offset,	/* Volume offset */
50 			hpux_curr_vol0,		/* Previous ch0 volume */
51 			hpux_curr_vol1;		/* Previous ch1 volume */
52 STATIC pid_t		hpux_pipe_pid = -1;	/* Pipe to program pid */
53 STATIC cd_state_t	*hpux_wcd;		/* Shared memory pointer */
54 STATIC bool_t		hpux_write_dev,		/* Write to audio device */
55 			hpux_write_file,	/* Write to output file */
56 			hpux_write_pipe,	/* Pipe to program */
57 			hpux_file_be = FALSE;	/* Big endian output file */
58 STATIC gen_desc_t	*hpux_audio_desc = NULL,/* Audio device desc */
59 			*hpux_file_desc = NULL,	/* Output file desc */
60 			*hpux_pipe_desc = NULL;	/* Pipe to program desc */
61 STATIC unsigned int	hpux_file_mode;		/* Output file mode */
62 
63 
64 /*
65  * hpuxaud_scale_vol
66  *	Scale volume from xmcd 0-100 to HP-UX audio device values
67  *
68  * Args:
69  *	vol - xmcd volume 0-100
70  *
71  * Return:
72  *	HP-UX volume value
73  */
74 STATIC int
75 hpuxaud_scale_vol(int vol)
76 {
77 	int	n;
78 
79 	n = ((vol * hpux_vol_max) / 100);
80 	if (((vol * hpux_vol_max) % 100) >= 50)
81 		n++;
82 
83 	/* Range checking */
84 	if (n > hpux_vol_max)
85 		n = hpux_vol_max;
86 
87 	return (n - hpux_vol_offset);
88 }
89 
90 
91 /*
92  * hpuxaud_unscale_vol
93  *	Scale volume from HP-UX audio device values to xmcd 0-100
94  *
95  * Args:
96  *	vol - HP-UX volume value
97  *
98  * Return:
99  *	xmcd volume 0-100
100  */
101 STATIC int
102 hpuxaud_unscale_vol(int vol)
103 {
104 	int	n;
105 
106 	n = ((vol + hpux_vol_offset) * 100) / hpux_vol_max;
107 	if (((vol * 100) % hpux_vol_max) >= hpux_vol_half)
108 		n++;
109 
110 	/* Range checking */
111 	if (n < 0)
112 		n = 0;
113 	else if (n > 100)
114 		n = 100;
115 
116 	return (n);
117 }
118 
119 
120 /*
121  * hpuxaud_open_dev
122  *	Opens the audio device.
123  *
124  * Args:
125  *	None.
126  *
127  * Return:
128  *	FALSE - failure
129  *	TRUE  - success
130  */
131 STATIC bool_t
132 hpuxaud_open_dev(void)
133 {
134 	char			*cp;
135 	struct stat		stbuf;
136 
137 	if (!gen_set_eid(hpux_wcd))
138 		return FALSE;
139 
140 	/* Use environment variable if set */
141 	if ((cp = getenv("AUDIODEV")) == NULL)
142 		cp = DEFAULT_DEV_AUDIO;
143 
144 	/* Check to make sure it's a device */
145 	if (stat(cp, &stbuf) < 0 || !S_ISCHR(stbuf.st_mode)) {
146 		(void) sprintf(hpux_wcd->i->msgbuf,
147 				"hpuxaud_open_dev: "
148 				"%s is not a character device",
149 				cp);
150 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
151 		(void) gen_reset_eid(hpux_wcd);
152 		return FALSE;
153 	}
154 
155 	/* Open audio device */
156 	hpux_audio_desc = gen_open_file(
157 		hpux_wcd, cp, O_RDWR, 0, FILEFMT_RAW, 0
158 	);
159 	if (hpux_audio_desc == NULL) {
160 		(void) sprintf(hpux_wcd->i->msgbuf,
161 			    "hpuxaud_open_dev: open of %s failed (errno=%d)",
162 			    cp, errno);
163 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
164 		(void) gen_reset_eid(hpux_wcd);
165 		return FALSE;
166 	}
167 
168 	(void) gen_reset_eid(hpux_wcd);
169 	return TRUE;
170 }
171 
172 
173 /*
174  * hpuxaud_close_dev
175  *	Closes the audio device.
176  *
177  * Args:
178  *	None.
179  *
180  * Return:
181  *	FALSE - failure
182  *	TRUE  - success
183  */
184 STATIC bool_t
185 hpuxaud_close_dev(void)
186 {
187 	if (!gen_set_eid(hpux_wcd))
188 		return FALSE;
189 
190 	/* Close audio device */
191 	(void) gen_close_file(hpux_audio_desc);
192 	hpux_audio_desc = NULL;
193 
194 	(void) gen_reset_eid(hpux_wcd);
195 	return TRUE;
196 }
197 
198 
199 /*
200  * hpuxaud_gethw_vol
201  *	Query hardware volume settings and update structures.
202  *
203  * Args:
204  *	None.
205  *
206  * Return:
207  *	TRUE - success
208  *	FALSE - failure
209  */
210 STATIC bool_t
211 hpuxaud_gethw_vol(void)
212 {
213 	int			vol0,
214 				vol1,
215 				val;
216 	struct audio_gain	again;
217 
218 	/* Retrieve current gain settings */
219 	(void) memset(&again, 0, sizeof(again));
220 	again.channel_mask = AUDIO_CHANNEL_0 | AUDIO_CHANNEL_1;
221 
222 	if (!gen_ioctl(hpux_audio_desc, AUDIO_GET_GAINS, &again)) {
223 		(void) sprintf(hpux_wcd->i->msgbuf,
224 			    "hpuxaud_set_info: "
225 			    "AUDIO_GET_GAINS failed (errno=%d)",
226 			    errno);
227 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
228 		return FALSE;
229 	}
230 
231 	vol0 = hpuxaud_unscale_vol(again.cgain[0].transmit_gain);
232 	vol1 = hpuxaud_unscale_vol(again.cgain[1].transmit_gain);
233 
234 	if (vol0 == hpux_curr_vol0 && vol1 == hpux_curr_vol1)
235 		/* No change */
236 		return TRUE;
237 
238 	hpux_curr_vol0 = vol0;
239 	hpux_curr_vol1 = vol1;
240 
241 	/* Update volume and balance */
242 	if (vol0 == vol1) {
243 		hpux_wcd->i->vol = util_untaper_vol(vol0);
244 		hpux_wcd->i->vol_left = 100;
245 		hpux_wcd->i->vol_right = 100;
246 	}
247 	else if (vol0 > vol1) {
248 		hpux_wcd->i->vol = util_untaper_vol(vol0);
249 		hpux_wcd->i->vol_left = 100;
250 		hpux_wcd->i->vol_right = (vol1 * 100) / vol0;
251 		val = vol0 >> 1;
252 		if (((vol1 * 100) % vol0) >= val)
253 			hpux_wcd->i->vol_right++;
254 	}
255 	else {
256 		hpux_wcd->i->vol = util_untaper_vol(vol1);
257 		hpux_wcd->i->vol_right = 100;
258 		hpux_wcd->i->vol_left =	(vol0 * 100) / vol1;
259 		val = vol1 >> 1;
260 		if (((vol0 * 100) % vol1) >= val)
261 			hpux_wcd->i->vol_left++;
262 	}
263 
264 	return TRUE;
265 }
266 
267 
268 /*
269  * hpuxaud_set_outport
270  *	Set CDDA playback output port.
271  *
272  * Args:
273  *	outport - New output port selector bitmap CDDA_OUT_XXX
274  *
275  * Return:
276  *	Nothing.
277  */
278 STATIC void
279 hpuxaud_set_outport(int outport)
280 {
281 	int	val;
282 
283 	if (outport != 0 && outport != hpux_outport) {
284 		val = 0;
285 		if ((outport & CDDA_OUT_SPEAKER) != 0)
286 			val |= AUDIO_OUT_SPEAKER;
287 		if ((outport & CDDA_OUT_HEADPHONE) != 0)
288 			val |= AUDIO_OUT_HEADPHONE;
289 		if ((outport & CDDA_OUT_LINEOUT) != 0)
290 			val |= AUDIO_OUT_LINE;
291 
292 		if (!gen_ioctl(hpux_audio_desc, AUDIO_SET_OUTPUT,
293 			       (void *) val)) {
294 			(void) sprintf(hpux_wcd->i->msgbuf,
295 				    "hpuxaud_set_outport: "
296 				    "AUDIO_SET_OUTPUT (0x%x) "
297 				    "failed (errno=%d)",
298 				    val, errno);
299 			DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
300 		}
301 
302 		hpux_outport = outport;
303 	}
304 }
305 
306 
307 /*
308  * hpuxaud_config
309  *	Sets up audio device for playing CD quality audio.
310  *
311  * Args:
312  *	None.
313  *
314  * Return:
315  *	FALSE - failure
316  *	TRUE  - success
317  */
318 STATIC bool_t
319 hpuxaud_config(void)
320 {
321 	struct audio_limits	alimits;
322 	struct audio_describe	adesc;
323 
324 	/* Set data format */
325 	if (!gen_ioctl(hpux_audio_desc, AUDIO_SET_DATA_FORMAT,
326 		       (void *) AUDIO_FORMAT_LINEAR16BIT)) {
327 		(void) sprintf(hpux_wcd->i->msgbuf,
328 			    "hpuxaud_config: "
329 			    "AUDIO_SET_DATA_FORMAT failed (errno=%d)",
330 			    errno);
331 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
332 		return FALSE;
333 	}
334 
335 	/* Set number of channels */
336 	if (!gen_ioctl(hpux_audio_desc, AUDIO_SET_CHANNELS, (void *) 2)) {
337 		(void) sprintf(hpux_wcd->i->msgbuf,
338 			    "hpuxaud_config: "
339 			    "AUDIO_SET_CHANNELS failed (errno=%d)",
340 			    errno);
341 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
342 		return FALSE;
343 	}
344 
345 	/* Set sampling rate */
346 	if (!gen_ioctl(hpux_audio_desc, AUDIO_SET_SAMPLE_RATE,
347 		       (void *) 44100)) {
348 		(void) sprintf(hpux_wcd->i->msgbuf,
349 			    "hpuxaud_config: "
350 			    "AUDIO_SET_SAMPLE_RATE failed (errno=%d)",
351 			    errno);
352 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
353 		return FALSE;
354 	}
355 
356 	/* Get volume control range */
357 	(void) memset(&adesc, 0, sizeof(adesc));
358 	if (!gen_ioctl(hpux_audio_desc, AUDIO_DESCRIBE, &adesc)) {
359 		(void) sprintf(hpux_wcd->i->msgbuf,
360 			    "hpuxaud_set_info: "
361 			    "AUDIO_GET_GAINS failed (errno=%d)",
362 			    errno);
363 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
364 
365 		hpux_vol_max = HPUX_MAX_VOL;
366 		hpux_vol_half = HPUX_HALF_VOL;
367 		hpux_vol_offset = HPUX_VOL_OFFSET;
368 	}
369 	else {
370 		hpux_vol_max =
371 			adesc.max_transmit_gain - adesc.min_transmit_gain;
372 		hpux_vol_half = hpux_vol_max >> 1;
373 		hpux_vol_offset = 0 - adesc.min_transmit_gain;
374 	}
375 
376 	/* Set audio output port */
377 	hpuxaud_set_outport(hpux_wcd->i->outport);
378 
379 	/* Check buffer sizes - informational only */
380 	(void) memset(&alimits, 0, sizeof(alimits));
381 	if (!gen_ioctl(hpux_audio_desc, AUDIO_GET_LIMITS, &alimits)) {
382 		(void) sprintf(hpux_wcd->i->msgbuf,
383 			    "hpuxaud_config: "
384 			    "AUDIO_GET_LIMITS failed (errno=%d)",
385 			    errno);
386 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
387 	}
388 	else {
389 		DBGPRN(DBG_SND)(errfp, "recv_bufs=%d xmit_bufs=%d\n",
390 				  alimits.max_receive_buffer_size,
391 				  alimits.max_transmit_buffer_size);
392 	}
393 
394 	/* Query current volume settings */
395 	hpux_curr_vol0 = hpux_curr_vol1 = -1;
396 	if (!hpuxaud_gethw_vol())
397 		return FALSE;
398 
399 	return TRUE;
400 }
401 
402 
403 /*
404  * hpuxaud_update
405  *	Updates hardware volume and balance settings from GUI settings.
406  *
407  * Args:
408  *	None.
409  *
410  * Return:
411  *	Nothing.
412  */
413 STATIC void
414 hpuxaud_update(void)
415 {
416 	int			vol0,
417 				vol1,
418 				val;
419 	struct audio_gain	again;
420 	static int		curr_vol0 = -1,
421 				curr_vol1 = -1;
422 
423 	app_data.vol_taper = hpux_wcd->i->vol_taper;
424 
425 	/* Set audio output port */
426 	hpuxaud_set_outport(hpux_wcd->i->outport);
427 
428 	(void) memset(&again, 0, sizeof(again));
429 
430 	if (hpux_wcd->i->vol_left == hpux_wcd->i->vol_right) {
431 		vol0 = util_taper_vol(hpux_wcd->i->vol);
432 		vol1 = vol0;
433 		again.cgain[0].transmit_gain = hpuxaud_scale_vol(vol0);
434 		again.cgain[1].transmit_gain = again.cgain[0].transmit_gain;
435 	}
436 	else if (hpux_wcd->i->vol_left > hpux_wcd->i->vol_right) {
437 		val = (hpux_wcd->i->vol * hpux_wcd->i->vol_right) / 100;
438 		if (((hpux_wcd->i->vol * hpux_wcd->i->vol_right) % 100) >= 50)
439 			val++;
440 		vol0 = util_taper_vol(hpux_wcd->i->vol);
441 		vol1 = util_taper_vol(val);
442 		again.cgain[0].transmit_gain = hpuxaud_scale_vol(vol0);
443 		again.cgain[1].transmit_gain = hpuxaud_scale_vol(vol1);
444 	}
445 	else {
446 		val = (hpux_wcd->i->vol * hpux_wcd->i->vol_left) / 100;
447 		if (((hpux_wcd->i->vol * hpux_wcd->i->vol_left) % 100) >= 50)
448 			val++;
449 		vol0 = util_taper_vol(val);
450 		vol1 = util_taper_vol(hpux_wcd->i->vol);
451 		again.cgain[0].transmit_gain = hpuxaud_scale_vol(vol0);
452 		again.cgain[1].transmit_gain = hpuxaud_scale_vol(vol1);
453 	}
454 
455 	if (vol0 == curr_vol0 && vol1 == curr_vol1)
456 		/* No change */
457 		return;
458 
459 	curr_vol0 = vol0;
460 	curr_vol1 = vol1;
461 
462 	/* Apply new settings */
463 	again.channel_mask = AUDIO_CHANNEL_0 | AUDIO_CHANNEL_1;
464 
465 	if (!gen_ioctl(hpux_audio_desc, AUDIO_SET_GAINS, &again)) {
466 		(void) sprintf(hpux_wcd->i->msgbuf,
467 			    "hpuxaud_set_info: "
468 			    "AUDIO_SET_GAINS failed (errno=%d)",
469 			    errno);
470 		DBGPRN(DBG_SND)(errfp, "%s\n", hpux_wcd->i->msgbuf);
471 	}
472 }
473 
474 
475 /*
476  * hpuxaud_status
477  *	Updates volume and balance settings from audio device.
478  *
479  * Args:
480  *	None.
481  *
482  * Return:
483  *	Nothing.
484  */
485 STATIC void
486 hpuxaud_status(void)
487 {
488 	(void) hpuxaud_gethw_vol();
489 }
490 
491 
492 /*
493  * hpuxaud_write
494  *	Function responsible for writing from the buffer to the audio
495  *	device (and file if saving).
496  *
497  * Args:
498  *	s - Pointer to the curstat_t structure
499  *
500  * Return:
501  *	FALSE - failure
502  *	TRUE  - success
503  */
504 STATIC bool_t
505 hpuxaud_write(curstat_t *s)
506 {
507 	byte_t	*in_data,
508 		*sw_data,
509 		*le_data,
510 		*be_data,
511 		*dev_data,
512 		*file_data,
513 		*pipe_data;
514 	char	*fname = NULL;
515 	int	trk_idx = -1,
516 		hbcnt = 1;
517 	time_t	start_time,
518 		tot_time;
519 	bool_t	ret;
520 
521 	/* Get memory for audio device write buffer */
522 	le_data = (byte_t *) MEM_ALLOC("le_data", hpux_wcd->cds->chunk_bytes);
523 	if (le_data == NULL) {
524 		(void) sprintf(hpux_wcd->i->msgbuf,
525 				"hpuxaud_write: out of memory");
526 		DBGPRN(DBG_GEN)(errfp, "%s\n", hpux_wcd->i->msgbuf);
527 		return FALSE;
528 	}
529 
530 	/* Get memory for audio file write buffer */
531 	be_data = (byte_t *) MEM_ALLOC("be_data", hpux_wcd->cds->chunk_bytes);
532 	if (be_data == NULL) {
533 		MEM_FREE(le_data);
534 		(void) sprintf(hpux_wcd->i->msgbuf,
535 				"hpuxaud_write: out of memory");
536 		DBGPRN(DBG_GEN)(errfp, "%s\n", hpux_wcd->i->msgbuf);
537 		return FALSE;
538 	}
539 
540 	(void) memset(le_data, 0, hpux_wcd->cds->chunk_bytes);
541 	(void) memset(be_data, 0, hpux_wcd->cds->chunk_bytes);
542 
543 	in_data = app_data.cdda_bigendian ? be_data : le_data;
544 	sw_data = app_data.cdda_bigendian ? le_data : be_data;
545 
546 	/* Start time */
547 	start_time = time(NULL);
548 
549 	/* While not stopped */
550 	ret = TRUE;
551 	while (hpux_wcd->i->state != CDSTAT_COMPLETED) {
552 		/* Get lock */
553 		cdda_waitsem(hpux_wsemid, LOCK);
554 
555 		/* End if finished and nothing in the buffer */
556 		if (hpux_wcd->cdb->occupied == 0 && hpux_wcd->i->cdda_done) {
557 			hpux_wcd->i->state = CDSTAT_COMPLETED;
558 			cdda_postsem(hpux_wsemid, LOCK);
559 			break;
560 		}
561 
562 		/* Wait until there is something there */
563 		while (hpux_wcd->cdb->occupied <= 0 &&
564 		       hpux_wcd->i->state != CDSTAT_COMPLETED) {
565 			cdda_postsem(hpux_wsemid, LOCK);
566 			cdda_waitsem(hpux_wsemid, DATA);
567 			cdda_waitsem(hpux_wsemid, LOCK);
568 		}
569 
570 		/* Break if stopped */
571 		if (hpux_wcd->i->state == CDSTAT_COMPLETED) {
572 			cdda_postsem(hpux_wsemid, LOCK);
573 			break;
574 		}
575 
576 		/* Set the current track and track length info */
577 		cdda_wr_setcurtrk(hpux_wcd);
578 
579 		/* Copy a chunk from the nextout slot into the data buffers */
580 		(void) memcpy(
581 			in_data,
582 			&hpux_wcd->cdb->b[
583 			    hpux_wcd->cds->chunk_bytes * hpux_wcd->cdb->nextout
584 			],
585 			hpux_wcd->cds->chunk_bytes
586 		);
587 
588 		/* Update pointers */
589 		hpux_wcd->cdb->nextout++;
590 		hpux_wcd->cdb->nextout %= hpux_wcd->cds->buffer_chunks;
591 		hpux_wcd->cdb->occupied--;
592 
593 		/* Set heartbeat and interval, update stats */
594 		if (--hbcnt == 0) {
595 			cdda_heartbeat(&hpux_wcd->i->writer_hb,CDDA_HB_WRITER);
596 
597 			tot_time = time(NULL) - start_time;
598 			if (tot_time > 0 && hpux_wcd->i->frm_played > 0) {
599 				hpux_wcd->i->frm_per_sec = (int)
600 					((float) hpux_wcd->i->frm_played /
601 					 (float) tot_time) + 0.5;
602 			}
603 
604 			hbcnt = cdda_heartbeat_interval(
605 				hpux_wcd->i->frm_per_sec
606 			);
607 		}
608 
609 		/* Signal room available */
610 		cdda_postsem(hpux_wsemid, ROOM);
611 
612 		/* Release lock */
613 		cdda_postsem(hpux_wsemid, LOCK);
614 
615 		/* Change channel routing and attenuation if necessary */
616 		gen_chroute_att(hpux_wcd->i->chroute,
617 				hpux_wcd->i->att,
618 				(sword16_t *)(void *) in_data,
619 				(size_t) hpux_wcd->cds->chunk_bytes);
620 
621 		/* Prepare byte-swapped version of the data */
622 		gen_byteswap(in_data, sw_data,
623 			     (size_t) hpux_wcd->cds->chunk_bytes);
624 
625 		/* Write to device */
626 		if (hpux_write_dev) {
627 			/* Always feed HP-UX audio driver big endian data */
628 			dev_data = be_data;
629 
630 			if (!gen_write_chunk(hpux_audio_desc, dev_data,
631 					(size_t) hpux_wcd->cds->chunk_bytes)) {
632 				hpux_wcd->i->state = CDSTAT_COMPLETED;
633 				cdda_postsem(hpux_wsemid, LOCK);
634 				ret = FALSE;
635 				break;
636 			}
637 
638 			/* Update volume and balance settings */
639 			hpuxaud_update();
640 
641 			if ((hpux_wcd->i->frm_played % FRAME_PER_SEC) == 0)
642 				hpuxaud_status();
643 		}
644 
645 		/* Write to file */
646 		if (hpux_write_file) {
647 			file_data = hpux_file_be ? be_data : le_data;
648 
649 			/* Open new track file if necessary */
650 			if (!app_data.cdda_trkfile) {
651 				fname = s->trkinfo[0].outfile;
652 			}
653 			else if (trk_idx != hpux_wcd->i->trk_idx) {
654 				trk_idx = hpux_wcd->i->trk_idx;
655 
656 				if (hpux_file_desc != NULL &&
657 				    !gen_close_file(hpux_file_desc)) {
658 					hpux_wcd->i->state = CDSTAT_COMPLETED;
659 					cdda_postsem(hpux_wsemid, LOCK);
660 					ret = FALSE;
661 					break;
662 				}
663 
664 				fname = s->trkinfo[trk_idx].outfile;
665 				hpux_file_desc = gen_open_file(
666 					hpux_wcd,
667 					fname,
668 					O_WRONLY | O_TRUNC | O_CREAT,
669 					(mode_t) hpux_file_mode,
670 					app_data.cdda_filefmt,
671 					hpux_wcd->i->trk_len
672 				);
673 				if (hpux_file_desc == NULL) {
674 					hpux_wcd->i->state = CDSTAT_COMPLETED;
675 					cdda_postsem(hpux_wsemid, LOCK);
676 					ret = FALSE;
677 					break;
678 				}
679 			}
680 
681 			if (!gen_write_chunk(hpux_file_desc, file_data,
682 					(size_t) hpux_wcd->cds->chunk_bytes)) {
683 				hpux_wcd->i->state = CDSTAT_COMPLETED;
684 				cdda_postsem(hpux_wsemid, LOCK);
685 				ret = FALSE;
686 				break;
687 			}
688 		}
689 
690 		/* Pipe to program */
691 		if (hpux_write_pipe) {
692 			pipe_data = hpux_file_be ? be_data : le_data;
693 
694 			if (!gen_write_chunk(hpux_pipe_desc, pipe_data,
695 					(size_t) hpux_wcd->cds->chunk_bytes)) {
696 				hpux_wcd->i->state = CDSTAT_COMPLETED;
697 				cdda_postsem(hpux_wsemid, LOCK);
698 				ret = FALSE;
699 				break;
700 			}
701 		}
702 
703 		hpux_wcd->i->frm_played += hpux_wcd->cds->chunk_blocks;
704 
705 		while (hpux_wcd->i->state == CDSTAT_PAUSED)
706 			util_delayms(400);
707 
708 		/* Update debug level */
709 		app_data.debug = hpux_wcd->i->debug;
710 	}
711 
712 	/* Wake up reader */
713 	cdda_postsem(hpux_wsemid, ROOM);
714 
715 	/* Free memory */
716 	MEM_FREE(le_data);
717 	MEM_FREE(be_data);
718 
719 	return (ret);
720 }
721 
722 
723 /*
724  * hpux_winit
725  *	Pre-playback support check function
726  *
727  * Args:
728  *	None.
729  *
730  * Return:
731  *	Bitmask of supported features
732  */
733 word32_t
734 hpux_winit(void)
735 {
736 	return (CDDA_WRITEDEV | CDDA_WRITEFILE | CDDA_WRITEPIPE);
737 }
738 
739 
740 /*
741  * hpux_write
742  *	Opens audio device/file, attaches shared memory and semaphores.
743  *	Continuously reads data from shared memory and writes it
744  *	to the audio device/file.
745  *
746  * Args:
747  *	s - Pointer to the curstat_t structure
748  *
749  * Return:
750  *	FALSE - failure
751  *	TRUE  - success
752  */
753 bool_t
754 hpux_write(curstat_t *s)
755 {
756 	size_t	estlen;
757 
758 	if (!PLAYMODE_IS_CDDA(app_data.play_mode)) {
759 		(void) fprintf(errfp, "hpux_write: Nothing to do.\n");
760 		return FALSE;
761 	}
762 
763 	hpux_write_dev  = (bool_t) ((app_data.play_mode & PLAYMODE_CDDA) != 0);
764 	hpux_write_file = (bool_t) ((app_data.play_mode & PLAYMODE_FILE) != 0);
765 	hpux_write_pipe = (bool_t) ((app_data.play_mode & PLAYMODE_PIPE) != 0);
766 
767 	/* Generic services initialization */
768 	gen_write_init();
769 
770 	/* Allocate memory */
771 	hpux_wcd = (cd_state_t *) MEM_ALLOC("cd_state_t", sizeof(cd_state_t));
772 	if (hpux_wcd == NULL) {
773 		(void) fprintf(errfp, "hpux_write: out of memory\n");
774 		return FALSE;
775 	}
776 	(void) memset(hpux_wcd, 0, sizeof(cd_state_t));
777 
778 	/* Initialize shared memory and semaphores */
779 	if ((hpux_wsemid = cdda_initipc(hpux_wcd)) < 0)
780 		return FALSE;
781 
782 	(void) sscanf(app_data.cdinfo_filemode, "%o", &hpux_file_mode);
783 	/* Make sure file is at least accessible by user */
784 	hpux_file_mode |= S_IRUSR | S_IWUSR;
785 	hpux_file_mode &= ~(S_ISUID | S_ISGID | S_IWGRP | S_IWOTH);
786 
787 	estlen = (hpux_wcd->i->end_lba - hpux_wcd->i->start_lba + 1) *
788 		 CDDA_BLKSZ;
789 
790 	/* Set heartbeat */
791 	cdda_heartbeat(&hpux_wcd->i->writer_hb, CDDA_HB_WRITER);
792 
793 	if (hpux_write_file || hpux_write_pipe) {
794 		/* Open file and/or pipe */
795 		if (!app_data.cdda_trkfile && hpux_write_file) {
796 			hpux_file_desc = gen_open_file(
797 				hpux_wcd,
798 				s->trkinfo[0].outfile,
799 				O_WRONLY | O_TRUNC | O_CREAT,
800 				(mode_t) hpux_file_mode,
801 				app_data.cdda_filefmt,
802 				estlen
803 			);
804 			if (hpux_file_desc == NULL)
805 				return FALSE;
806 		}
807 
808 		if (hpux_write_pipe) {
809 			hpux_pipe_desc = gen_open_pipe(
810 				hpux_wcd,
811 				app_data.pipeprog,
812 				&hpux_pipe_pid,
813 				app_data.cdda_filefmt,
814 				estlen
815 			);
816 			if (hpux_pipe_desc == NULL)
817 				return FALSE;
818 
819 			DBGPRN(DBG_SND)(errfp,
820 					  "\nhpuxaud_write: Pipe to [%s]: "
821 					  "chunk_blks=%d\n",
822 					  app_data.pipeprog,
823 					  hpux_wcd->cds->chunk_blocks);
824 		}
825 
826 		/* Set endian */
827 		hpux_file_be = gen_filefmt_be(app_data.cdda_filefmt);
828 	}
829 
830 	if (hpux_write_dev) {
831 		/* Open audio device */
832 		if (!hpuxaud_open_dev())
833 			return FALSE;
834 
835 		/* Configure audio */
836 		if (!hpuxaud_config())
837 			return FALSE;
838 
839 		/* Initial settings */
840 		hpuxaud_update();
841 	}
842 
843 	/* Do the writing */
844 	if (!hpuxaud_write(s))
845 		return FALSE;
846 
847 	return TRUE;
848 }
849 
850 
851 /*
852  * hpux_wdone
853  *	Post-playback cleanup function
854  *
855  * Args:
856  *	killreader - whether to terminate the reader thread or process
857  *
858  * Return:
859  *	Nothing.
860  */
861 void
862 hpux_wdone(bool_t killreader)
863 {
864 	DBGPRN(DBG_GEN)(errfp, "\nhpux_wdone: Cleaning up writer\n");
865 
866 	if (hpux_audio_desc != NULL)
867 		(void) hpuxaud_close_dev();
868 
869 	if (hpux_file_desc != NULL) {
870 		(void) gen_close_file(hpux_file_desc);
871 		hpux_file_desc = NULL;
872 	}
873 
874 	if (hpux_pipe_desc != NULL) {
875 		(void) gen_close_pipe(hpux_pipe_desc);
876 		hpux_pipe_desc = NULL;
877 	}
878 
879 	cdda_yield();
880 
881 	if (hpux_pipe_pid > 0) {
882 		(void) kill(hpux_pipe_pid, SIGTERM);
883 		hpux_pipe_pid = -1;
884 	}
885 
886 	if (hpux_wcd != NULL) {
887 		if (hpux_wcd->i != NULL) {
888 			if (killreader && hpux_wcd->i->reader != (thid_t) 0) {
889 				cdda_kill(hpux_wcd->i->reader, SIGTERM);
890 				hpux_wcd->i->state = CDSTAT_COMPLETED;
891 			}
892 
893 			/* Reset frames played counter */
894 			hpux_wcd->i->frm_played = 0;
895 		}
896 
897 		MEM_FREE(hpux_wcd);
898 		hpux_wcd = NULL;
899 	}
900 
901 	hpux_write_dev = hpux_write_file = hpux_write_pipe = FALSE;
902 }
903 
904 
905 /*
906  * hpux_winfo
907  *	Append method identification to informational string.
908  *
909  * Args:
910  *	str - The informational string
911  *
912  * Return:
913  *	Nothing.
914  */
915 void
916 hpux_winfo(char *str)
917 {
918 	(void) strcat(str, "HP-UX audio driver\n");
919 }
920 
921 #endif	/* CDDA_WR_HPUX CDDA_SUPPORTED */
922 
923