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  *	This file contains code to support CDDA operations with
22  *	parallel reader/writer processes using System V IPC
23  *	shared memory and semaphores.
24  */
25 #ifndef lint
26 static char *_sysvipc_c_ident_ = "@(#)sysvipc.c	7.158 04/03/17";
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 
36 #ifdef CDDA_SYSVIPC
37 
38 #include "cdda_d/sysvipc.h"
39 
40 /*
41  * Platform-specific configuration for process scheduling control
42  */
43 #if defined(_POSIX_PRIORITY_SCHEDULING)
44 /* If _POSIX_PRIORITY_SCHEDULING is defined in <unistd.h> then
45  * the sched_setscheduler(2) system call is supported.
46  */
47 #include <sched.h>
48 #define USE_POSIX_SCHED
49 #endif
50 
51 #if !defined(USE_POSIX_SCHED) && \
52     (defined(_LINUX)  || defined(_SUNOS4) || defined(__hpux)   || \
53      defined(_AIX)    || defined(_ULTRIX) || defined(__bsdi__) || \
54      defined(FreeBSD) || defined(NetBSD)  || defined(OpenBSD))
55 #include <sys/resource.h>
56 #define USE_SETPRIORITY
57 #endif
58 
59 #if !defined(USE_POSIX_SCHED) && defined(SVR4) && !defined(sgi)
60 #include <sys/priocntl.h>
61 #ifdef _UNIXWARE2
62 #include <sys/fppriocntl.h>		/* UnixWare 2.x and later */
63 #else
64 #include <sys/rtpriocntl.h>		/* Most SVR4 including Solaris */
65 #endif
66 #define USE_PRIOCNTL
67 #endif
68 
69 #if !defined(USE_POSIX_SCHED) && !defined(USE_SETPRIORITY) && \
70     !defined(USE_PRIOCNTL)
71 #define USE_NICE			/* Fallback to nice(2) */
72 #endif
73 
74 
75 extern appdata_t	app_data;
76 extern FILE		*errfp;
77 extern cdda_client_t	*cdda_clinfo;
78 
79 extern cdda_rd_tbl_t	cdda_rd_calltbl[];
80 extern cdda_wr_tbl_t	cdda_wr_calltbl[];
81 
82 STATIC void		*sysvipc_shmaddr;	/* Shared memory pointer */
83 STATIC int		shmid = -1,		/* Shared memory identifier */
84 			semid = -1,		/* Semaphores identifier */
85 			skip_hbchk = 0;		/* Skip heartbeat checking */
86 STATIC cd_state_t	*cd = NULL;		/* CD state pointer */
87 STATIC char		errbuf[ERR_BUF_SZ];	/* Error message buffer */
88 
89 
90 #if defined(__GNUC__) && __GNUC__ >= 2 && \
91     defined(sgi) && (defined(_ABIN32) || defined(_ABI64))
92 
93 #define SEMCTL	irix_gcc_semctl
94 
95 /*
96  * gcc_semctl
97  *	Wrapper function to re-align the semun_t structure for
98  *	SGI IRIX 6.x.  Needed when compiling with gcc due to
99  *	differences in calling convention between gcc and IRIX's libc.
100  *
101  * Args:
102  *	Same as semctl(2).
103  *
104  * Return:
105  *	Same as semctl(2).
106  */
107 STATIC int
108 irix_gcc_semctl(int semid, int semnun, int cmd, semun_t arg)
109 {
110 	return semctl(semid, semnun, cmd, (word64_t) arg.val << 32);
111 }
112 
113 #else
114 
115 #define SEMCTL	semctl
116 
117 #endif
118 
119 
120 /*
121  * cdda_sysvipc_rsig
122  *	Reader process signal handler function
123  *
124  * Args:
125  *	signo - The signal number
126  *
127  * Return:
128  *	Nothing.
129  */
130 /*ARGSUSED*/
131 STATIC void
132 cdda_sysvipc_rsig(int signo)
133 {
134 	void	(*rdone)(bool_t);
135 
136 	rdone = cdda_rd_calltbl[app_data.cdda_rdmethod].readdone;
137 
138 	/* Call cleanup function */
139 	(*rdone)(FALSE);
140 
141 	/* End reader process */
142 	_exit(1);
143 }
144 
145 
146 /*
147  * cdda_sysvipc_wsig
148  *	Writer process signal handler function
149  *
150  * Args:
151  *	signo - The signal number
152  *
153  * Return:
154  *	Nothing.
155  */
156 /*ARGSUSED*/
157 STATIC void
158 cdda_sysvipc_wsig(int signo)
159 {
160 	void	(*wdone)(bool_t);
161 
162 	wdone = cdda_wr_calltbl[app_data.cdda_wrmethod].writedone;
163 
164 	/* Call cleanup function */
165 	(*wdone)(FALSE);
166 
167 	/* End writer process */
168 	_exit(1);
169 }
170 
171 
172 /*
173  * cdda_sysvipc_highpri
174  *	Increase the calling process' scheduling priority
175  *
176  * Args:
177  *	name - process name string
178  *
179  * Return:
180  *	Nothing.
181  */
182 STATIC void
183 cdda_sysvipc_highpri(char *name)
184 {
185 #ifdef HAS_EUID
186 	uid_t	savuid = geteuid();
187 
188 	if (util_seteuid(0) < 0 || geteuid() != 0)
189 #else
190 	if (getuid() != 0)
191 #endif
192 	{
193 		DBGPRN(DBG_GEN)(errfp,
194 			"cdda_sysvipc_setpri: Cannot change scheduling: "
195 			"Insufficient privilege.\n");
196 #ifdef HAS_EUID
197 		(void) util_seteuid(savuid);
198 #endif
199 		return;
200 	}
201 
202 #ifdef USE_NICE
203 	{
204 		int	ret;
205 
206 		if ((ret = nice(-10)) == -1) {
207 			DBGPRN(DBG_GEN)(errfp,
208 				"cdda_sysvipc_highpri: "
209 				"Cannot change scheduling: errno=%d\n",
210 				errno);
211 #ifdef HAS_EUID
212 			(void) util_seteuid(savuid);
213 #endif
214 			return;
215 		}
216 		DBGPRN(DBG_GEN)(errfp,
217 				"\n%s running with nice value %d (pid=%d)\n",
218 				name, ret, (int) getpid());
219 #ifdef HAS_EUID
220 		(void) util_seteuid(savuid);
221 #endif
222 		return;
223 	}
224 #endif	/* USE_NICE */
225 
226 #ifdef USE_SETPRIORITY
227 	{
228 		int	pri = -10;
229 
230 		if (setpriority(PRIO_PROCESS, 0, pri) < 0) {
231 			DBGPRN(DBG_GEN)(errfp,
232 				"cdda_sysvipc_highpri: "
233 				"Cannot change scheduling: errno=%d\n",
234 				errno);
235 #ifdef HAS_EUID
236 			(void) util_seteuid(savuid);
237 #endif
238 			return;
239 		}
240 		DBGPRN(DBG_GEN)(errfp,
241 			    "\n%s running with priority value %d (pid=%d)\n",
242 			    name, pri, (int) getpid());
243 #ifdef HAS_EUID
244 		(void) util_seteuid(savuid);
245 #endif
246 		return;
247 	}
248 #endif	/* USE_SETPRIORITY */
249 
250 #ifdef USE_PRIOCNTL
251 	{
252 		char		*sched_class;
253 		pcinfo_t	pci;
254 		pcparms_t	pcp;
255 
256 #ifdef _UNIXWARE2
257 		fpparms_t	*fp;
258 
259 		/* set fixed priority time class */
260 		fp = (fpparms_t *) &pcp.pc_clparms;
261 		fp->fp_pri = FP_NOCHANGE;
262 		fp->fp_tqsecs = (unsigned long) FP_TQDEF;
263 		fp->fp_tqnsecs = FP_TQDEF;
264 		sched_class = "FP";
265 #else
266 		rtparms_t	*rt;
267 
268 		/* set real time class */
269 		rt = (rtparms_t *) &pcp.pc_clparms;
270 		rt->rt_pri = RT_NOCHANGE;
271 		rt->rt_tqsecs = (unsigned long) RT_TQDEF;
272 		rt->rt_tqnsecs = RT_TQDEF;
273 		sched_class = "RT";
274 #endif
275 		(void) strcpy(pci.pc_clname, sched_class);
276 
277 		if (priocntl(0, 0, PC_GETCID, (void *) &pci) < 0) {
278 			DBGPRN(DBG_GEN)(errfp, "cdda_sysvipc_highpri: "
279 				"priocntl PC_GETCID failed: "
280 				"errno=%d\n", errno);
281 #ifdef HAS_EUID
282 			(void) util_seteuid(savuid);
283 #endif
284 			return;
285 		}
286 
287 		pcp.pc_cid = pci.pc_cid;
288 
289 		if (priocntl(P_PID, getpid(), PC_SETPARMS, (void *) &pcp) < 0) {
290 			DBGPRN(DBG_GEN)(errfp, "cdda_sysvipc_highpri: "
291 				"priocntl PC_SETPARMS failed: "
292 				"errno=%d\n", errno);
293 			return;
294 		}
295 		DBGPRN(DBG_GEN)(errfp,
296 				"\n%s running SVR4 %s scheduling (pid=%d)\n",
297 				name, sched_class, (int) getpid());
298 #ifdef HAS_EUID
299 		(void) util_seteuid(savuid);
300 #endif
301 		return;
302 	}
303 #endif	/* USE_PRIOCNTL */
304 
305 #ifdef USE_POSIX_SCHED
306 	{
307 		int			minpri,
308 					maxpri;
309 		struct sched_param	scparm;
310 
311 		/* Set priority to 80% between min and max */
312 		minpri = sched_get_priority_min(SCHED_RR);
313 		maxpri = sched_get_priority_max(SCHED_RR);
314 
315 		if (minpri < 0 || maxpri < 0) {
316 			DBGPRN(DBG_GEN)(errfp, "cdda_sysvipc_highpri: "
317 				"Failed getting min/max priority values: "
318 				"errno=%d\n", errno);
319 #ifdef HAS_EUID
320 			(void) util_seteuid(savuid);
321 #endif
322 			return;
323 		}
324 
325 		scparm.sched_priority = ((maxpri - minpri) * 8 / 10) + minpri;
326 
327 		if (sched_setscheduler(0, SCHED_RR, &scparm) < 0) {
328 			DBGPRN(DBG_GEN)(errfp, "cdda_sysvipc_highpri: "
329 				"sched_setscheduler failed: "
330 				"errno=%d\n", errno);
331 #ifdef HAS_EUID
332 			(void) util_seteuid(savuid);
333 #endif
334 			return;
335 		}
336 
337 		DBGPRN(DBG_GEN)(errfp,
338 			    "\n%s running POSIX SCHED_RR scheduling (pid=%d)\n"
339 			    "Priority=%d (range: %d,%d)\n",
340 			    name, (int) getpid(),
341 			    scparm.sched_priority, minpri, maxpri);
342 
343 #ifdef HAS_EUID
344 		(void) util_seteuid(savuid);
345 #endif
346 		return;
347 	}
348 #endif	/* USE_POSIX_SCHED */
349 }
350 
351 
352 /*
353  * cdda_sysvipc_getsem
354  *	Initialize semaphores, making lock available.
355  *
356  * Args:
357  *	None.
358  *
359  * Return:
360  *	FALSE - failure
361  *	TRUE  - success
362  */
363 STATIC bool_t
364 cdda_sysvipc_getsem(void)
365 {
366 	semun_t	arg;
367 
368 	/* Initialize semaphores */
369 	if ((semid = semget(SEMKEY + app_data.devnum, 3,
370 			    IPC_PERMS|IPC_CREAT|IPC_EXCL)) < 0) {
371 		/* If the semaphores exist already, use them. This
372 		 * should not happen if the application previously
373 		 * exited correctly.
374 		 */
375 		if (errno == EEXIST) {
376 			if ((semid = semget(SEMKEY + app_data.devnum, 3,
377 					    IPC_PERMS|IPC_EXCL)) < 0) {
378 				(void) sprintf(errbuf, "cdda_sysvipc_getsem: "
379 					    "semget failed (errno=%d)", errno);
380 				CDDA_INFO(errbuf);
381 				DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
382 				return FALSE;
383 			}
384 		}
385 		else {
386 			(void) sprintf(errbuf, "cdda_sysvipc_getsem: "
387 				    "semget failed (errno=%d)", errno);
388 			CDDA_INFO(errbuf);
389 			DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
390 			return FALSE;
391 		}
392 	}
393 
394 	/* Make lock available */
395 	arg.val = 1;
396 	if (SEMCTL(semid, LOCK, SETVAL, arg) < 0) {
397 		(void) sprintf(errbuf, "cdda_sysvipc_getsem: "
398 			    "semctl SETVAL failed (errno=%d)", errno);
399 		CDDA_INFO(errbuf);
400 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
401 		return FALSE;
402 	}
403 
404 	return TRUE;
405 }
406 
407 
408 /*
409  * cdda_sysvipc_getshm
410  *	Initialize shared memory.  We first work out how much shared
411  *	memory we require and fill in a temporary cd_size_t structure
412  *	along the way. We then create the shared memory segment and
413  *	set the fields of *cd pointing into it.
414  *
415  * Args:
416  *	None.
417  *
418  * Return:
419  *	FALSE - failure
420  *	TRUE  - success
421  */
422 STATIC bool_t
423 cdda_sysvipc_getshm(void)
424 {
425 	cd_size_t	*cd_tmp_size;
426 
427 	/* Store sizes here temporarily */
428 	cd_tmp_size = (cd_size_t *) MEM_ALLOC(
429 		"cd_tmp_size", sizeof(cd_size_t)
430 	);
431 	if (cd_tmp_size == NULL) {
432 		CDDA_FATAL(app_data.str_nomemory);
433 		return FALSE;
434 	}
435 
436 	(void) memset(cd_tmp_size, 0, sizeof(cd_size_t));
437 
438 	/* Sizes */
439 	cd_tmp_size->str_length = DEF_CDDA_STR_LEN;
440 	cd_tmp_size->chunk_blocks = app_data.cdda_readchkblks;
441 
442 	/* Sizes depend on jitter */
443 	if (app_data.cdda_jitter_corr) {
444 		cd_tmp_size->olap_blocks = DEF_CDDA_OLAP_BLKS;
445 		cd_tmp_size->search_blocks = DEF_CDDA_SRCH_BLKS;
446 	}
447 	else {
448 		cd_tmp_size->olap_blocks = 0;
449 		cd_tmp_size->search_blocks = 0;
450 	}
451 
452 	/* Chunk bytes */
453 	cd_tmp_size->chunk_bytes = cd_tmp_size->chunk_blocks * CDDA_BLKSZ;
454 
455 	/* Buffer chunks */
456 	cd_tmp_size->buffer_chunks =
457 		(FRAME_PER_SEC * DEF_CDDA_BUFFER_SECS) /
458 		 cd_tmp_size->chunk_blocks;
459 	/* Check if we need to round up */
460 	if (((FRAME_PER_SEC * DEF_CDDA_BUFFER_SECS) %
461 	     cd_tmp_size->chunk_blocks) != 0)
462 		cd_tmp_size->buffer_chunks++;
463 
464 	/* Buffer blocks */
465 	cd_tmp_size->buffer_blocks =
466 		cd_tmp_size->buffer_chunks * cd_tmp_size->chunk_blocks;
467 
468 	/* Buffer bytes */
469 	cd_tmp_size->buffer_bytes =
470 		cd_tmp_size->buffer_chunks * cd_tmp_size->chunk_bytes;
471 
472 	/* Overlap bytes */
473 	cd_tmp_size->olap_bytes = cd_tmp_size->olap_blocks * CDDA_BLKSZ;
474 
475 	/* Search */
476 	cd_tmp_size->search_bytes = cd_tmp_size->search_blocks * CDDA_BLKSZ;
477 
478 	/* How much shared memory do we need? */
479 	cd_tmp_size->size =
480 		sizeof(cd_size_t) + sizeof(cd_info_t) + sizeof(cd_buffer_t);
481 
482 	/* Add memory for overlap */
483 	cd_tmp_size->size += (cd_tmp_size->search_bytes << 1);
484 
485 	/* Add memory for data read */
486 	cd_tmp_size->size +=
487 		cd_tmp_size->chunk_bytes + (cd_tmp_size->olap_bytes << 1);
488 
489 	/* Add memory for buffer */
490 	cd_tmp_size->size += cd_tmp_size->buffer_bytes;
491 
492 	/* Now create the shared memory segment */
493 	shmid = shmget(SHMKEY + app_data.devnum, cd_tmp_size->size,
494 		       IPC_PERMS|IPC_CREAT|IPC_EXCL);
495 	if (shmid < 0) {
496 		/*
497 		 * If shared memory exists already, load it, remove it
498 		 * and create new one. This should not be necessary
499 		 * if the application previously exited correctly.
500 		 */
501 		if (errno == EEXIST) {
502 			shmid = shmget(SHMKEY + app_data.devnum,
503 				       cd_tmp_size->size, IPC_PERMS|IPC_EXCL);
504 			if (shmid < 0) {
505 				(void) sprintf(errbuf, "cdda_sysvipc_getshm: "
506 					    "shmget failed (errno=%d)", errno);
507 				CDDA_INFO(errbuf);
508 				DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
509 				MEM_FREE(cd_tmp_size);
510 				return FALSE;
511 			}
512 
513 			if (shmctl(shmid, IPC_RMID, NULL) != 0) {
514 				(void) sprintf(errbuf, "cdda_sysvipc_getshm: "
515 					"shmctl IPC_RMID failed (errno=%d)",
516 					errno);
517 				CDDA_INFO(errbuf);
518 				DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
519 				MEM_FREE(cd_tmp_size);
520 				return FALSE;
521 			}
522 
523 			shmid = shmget(SHMKEY + app_data.devnum,
524 				       cd_tmp_size->size,
525 				       IPC_PERMS|IPC_CREAT|IPC_EXCL);
526 			if (shmid < 0) {
527 				(void) sprintf(errbuf, "cdda_sysvipc_getshm: "
528 					    "shmget failed (errno=%d)", errno);
529 				CDDA_INFO(errbuf);
530 				DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
531 				MEM_FREE(cd_tmp_size);
532 				return FALSE;
533 			}
534 		}
535 		else {
536 			(void) sprintf(errbuf, "cdda_sysvipc_getshm: "
537 					"shmget failed (errno=%d)", errno);
538 			CDDA_INFO(errbuf);
539 			DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
540 			MEM_FREE(cd_tmp_size);
541 			return FALSE;
542 		}
543 	}
544 
545 	/* Attach */
546 	if ((sysvipc_shmaddr = (void *) shmat(shmid, NULL, 0)) == NULL) {
547 		(void) sprintf(errbuf,
548 				"cdda_sysvipc_getshm: shmat failed (errno=%d)",
549 				errno);
550 		CDDA_INFO(errbuf);
551 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
552 		MEM_FREE(cd_tmp_size);
553 		return FALSE;
554 	}
555 
556 	/* Now set our fields pointing into the shared memory */
557 	cd->cds = (cd_size_t *) sysvipc_shmaddr;
558 
559 	/* Copy in sizes */
560 	(void) memcpy(sysvipc_shmaddr, cd_tmp_size, sizeof(cd_size_t));
561 
562 	/* Info */
563 	cd->i = (cd_info_t *)(void *)
564 		((byte_t *) sysvipc_shmaddr + sizeof(cd_size_t));
565 
566 	/* Buffer */
567 	cd->cdb = (cd_buffer_t *)(void *)
568 		((byte_t *) cd->i + sizeof(cd_info_t));
569 
570 	/* Overlap */
571 	cd->cdb->olap = (byte_t *) ((byte_t *) cd->cdb + sizeof(cd_buffer_t));
572 
573 	/* Data */
574 	cd->cdb->data = (byte_t *)
575 			((byte_t *) cd->cdb->olap +
576 				    (cd->cds->search_bytes << 1));
577 
578 	/* Buffer */
579 	cd->cdb->b = (byte_t *)
580 		     ((byte_t *) cd->cdb->data +
581 				 cd->cds->chunk_bytes +
582 				 (cd->cds->olap_bytes << 1));
583 
584 	/* Free temporary cd_size_t */
585 	MEM_FREE(cd_tmp_size);
586 	return TRUE;
587 }
588 
589 
590 /*
591  * cdda_sysvipc_initshm
592  *	Initialize fields of the cd_state_t structure. The cd_size_t structure
593  *	has already been done in cdda_sysvipc_getshm(). The buffer we
594  *	initialize prior to playing.
595  *
596  * Args:
597  *	s - Pointer to the curstat_t structure.
598  *
599  * Return:
600  *	Nothing.
601  */
602 STATIC void
603 cdda_sysvipc_initshm(curstat_t *s)
604 {
605 	/* General state */
606 	cd->i->state = CDSTAT_COMPLETED;
607 	cd->i->chroute = app_data.ch_route;
608 	cd->i->att = s->cdda_att;
609 	cd->i->outport = (int) app_data.outport;
610 	cd->i->vol_taper = app_data.vol_taper;
611 	cd->i->vol = 0;
612 	cd->i->vol_left = 100;
613 	cd->i->vol_right = 100;
614 	cd->i->jitter = (int) app_data.cdda_jitter_corr;
615 	cd->i->frm_played = 0;
616 	cd->i->trk_idx = 0;
617 	cd->i->trk_len = 0;
618 	cd->i->debug = app_data.debug;
619 
620 	/* Start and ending LBAs for tracks */
621 	cd->i->start_lba = 0;
622 	cd->i->end_lba = 0;
623 
624 	/* Initial frames per second statistic */
625 	cd->i->frm_per_sec = 0;
626 
627 	/* Process ids */
628 	cd->i->reader = (thid_t) 0;
629 	cd->i->writer = (thid_t) 0;
630 
631 	/* Reader and writer heartbeats */
632 	cd->i->reader_hb = 0;
633 	cd->i->writer_hb = 0;
634 
635 	/* heartbeat timeouts */
636 	cd->i->reader_hb_timeout = app_data.hb_timeout;
637 	cd->i->writer_hb_timeout = app_data.hb_timeout * DEF_CDDA_BUFFER_SECS;
638 }
639 
640 
641 /*
642  * cdda_sysvipc_cleanup
643  *	Detaches shared memory, removes it and destroys semaphores.
644  *
645  * Args:
646  *	None.
647  *
648  * Return:
649  *	Nothing.
650  */
651 STATIC void
652 cdda_sysvipc_cleanup(void)
653 {
654 	semun_t	arg;
655 
656 	if (cd == NULL)
657 		return;
658 
659 	DBGPRN(DBG_GEN)(errfp, "\ncdda_sysvipc_cleanup: Cleaning up CDDA.\n");
660 
661 	/* Detach from shared memory */
662 	if (cd->cds != NULL && shmdt((char *) cd->cds) < 0) {
663 		(void) sprintf(errbuf,
664 			    "cdda_sysvipc_cleanup: shmdt failed (errno=%d)",
665 			    errno);
666 		CDDA_INFO(errbuf);
667 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
668 	}
669 
670 	/* Deallocate cd_state_t structure */
671 	MEM_FREE(cd);
672 	cd = NULL;
673 
674 	/* Remove shared memory */
675 	if (shmid >= 0 && shmctl(shmid, IPC_RMID, NULL) < 0) {
676 		(void) sprintf(errbuf,
677 			    "cdda_sysvipc_cleanup: shmctl IPC_RMID failed "
678 			    "(errno=%d)",
679 			    errno);
680 		CDDA_INFO(errbuf);
681 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
682 	}
683 	shmid = -1;
684 
685 	/* Destroy semaphores */
686 	arg.val = 0;
687 	if (semid >= 0 && SEMCTL(semid, 0, IPC_RMID, arg) < 0) {
688 		(void) sprintf(errbuf,
689 			    "cdda_sysvipc_cleanup: semctl IPC_RMID failed "
690 			    "(errno=%d)",
691 			    errno);
692 		CDDA_INFO(errbuf);
693 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
694 	}
695 	semid = -1;
696 }
697 
698 
699 /*
700  * cdda_sysvipc_init2
701  *	Initialize shared memory and semaphores.
702  *
703  * Args:
704  *	Pointer to the curstat_t structure.
705  *
706  * Return:
707  *	TRUE - success
708  *	FALSE - failure
709  */
710 STATIC bool_t
711 cdda_sysvipc_init2(curstat_t *s)
712 {
713 	/* Allocate memory */
714 	cd = (cd_state_t *) MEM_ALLOC("cd_state_t", sizeof(cd_state_t));
715 	if (cd == NULL)
716 		/* Out of memory */
717 		return FALSE;
718 
719 	(void) memset(cd, 0, sizeof(cd_state_t));
720 
721 	/* Initialize semaphores */
722 	if (!cdda_sysvipc_getsem()) {
723 		cdda_sysvipc_cleanup();
724 		return FALSE;
725 	}
726 
727 	/* Initialize shared memory */
728 	if (!cdda_sysvipc_getshm()) {
729 		cdda_sysvipc_cleanup();
730 		return FALSE;
731 	}
732 
733 	/* Initialize fields */
734 	cdda_sysvipc_initshm(s);
735 
736 	DBGPRN(DBG_GEN)(errfp, "\nSYSVIPC: CDDA initted.\n");
737 	return TRUE;
738 }
739 
740 
741 /*
742  * cdda_sysvipc_capab
743  *	Return configured CDDA capabilities
744  *
745  * Args:
746  *	None.
747  *
748  * Return:
749  *	Bitmask of supported CDDA read and write features
750  */
751 word32_t
752 cdda_sysvipc_capab(void)
753 {
754 	word32_t	(*rinit)(void),
755 			(*winit)(void);
756 
757 	if (app_data.cdda_rdmethod == CDDA_RD_NONE ||
758 	    app_data.cdda_wrmethod == CDDA_WR_NONE)
759 		return 0;
760 
761 	if (app_data.cdda_rdmethod < CDDA_RD_NONE ||
762 	    app_data.cdda_rdmethod >= CDDA_RD_METHODS ||
763 	    app_data.cdda_wrmethod < CDDA_WR_NONE ||
764 	    app_data.cdda_wrmethod >= CDDA_WR_METHODS) {
765 		CDDA_WARNING(app_data.str_cddainit_fail);
766 		DBGPRN(DBG_GEN)(errfp, "Warning: %s\n",
767 				  app_data.str_cddainit_fail);
768 		return 0;
769 	}
770 
771 	/* Call readinit and writeinit functions to determine capability */
772 	rinit = cdda_rd_calltbl[app_data.cdda_rdmethod].readinit;
773 	winit = cdda_wr_calltbl[app_data.cdda_wrmethod].writeinit;
774 
775 	if (rinit == NULL || winit == NULL) {
776 		CDDA_WARNING(app_data.str_cddainit_fail);
777 		DBGPRN(DBG_GEN)(errfp, "Warning: %s\n",
778 				  app_data.str_cddainit_fail);
779 		return 0;
780 	}
781 
782 	return ((*rinit)() | (*winit)());
783 }
784 
785 
786 /*
787  * cdda_sysvipc_preinit
788  *	Early program startup initializations.
789  *
790  * Args:
791  *	None.
792  *
793  * Return:
794  *	Nothing.
795  */
796 void
797 cdda_sysvipc_preinit(void)
798 {
799 	/* Do nothing */
800 }
801 
802 
803 /*
804  * cdda_sysvipc_init
805  *	Initialize cdda subsystem.
806  *
807  * Args:
808  *	s - Pointer to the curstat_t structure.
809  *
810  * Return:
811  *	FALSE - failure
812  *	TRUE  - success
813  */
814 /*ARGSUSED*/
815 bool_t
816 cdda_sysvipc_init(curstat_t *s)
817 {
818 	if (app_data.cdda_rdmethod <= CDDA_RD_NONE ||
819 	    app_data.cdda_rdmethod >= CDDA_RD_METHODS ||
820 	    app_data.cdda_wrmethod <= CDDA_WR_NONE ||
821 	    app_data.cdda_wrmethod >= CDDA_WR_METHODS ||
822 	    cdda_rd_calltbl[app_data.cdda_rdmethod].readfunc == NULL ||
823 	    cdda_wr_calltbl[app_data.cdda_wrmethod].writefunc == NULL) {
824 		return FALSE;
825 	}
826 
827 	/* Defer the rest of initialization to cdda_sysvipc_init2 */
828 	return TRUE;
829 }
830 
831 
832 /*
833  * cdda_sysvipc_halt
834  *	Halt cdda subsystem.
835  *
836  * Args:
837  *	devp - Read device descriptor
838  *	s    - Pointer to the curstat_t structure
839  *
840  * Return:
841  *	Nothing.
842  */
843 void
844 cdda_sysvipc_halt(di_dev_t *devp, curstat_t *s)
845 {
846 	if (cd == NULL)
847 		return;
848 
849 	/* Stop playback, if applicable */
850 	(void) cdda_sysvipc_stop(devp, s);
851 
852 	/* Clean up IPC */
853 	cdda_sysvipc_cleanup();
854 
855 	DBGPRN(DBG_GEN)(errfp, "\nSYSVIPC: CDDA halted.\n");
856 }
857 
858 
859 /*
860  * cdda_sysvipc_play
861  *	Start cdda play.
862  *
863  * Args:
864  *	devp - Read device descriptor
865  *	s -  Pointer to the curstat_t structure
866  *	start_lba - Start logical block address
867  *	end_lba   - End logical block address
868  *
869  * Return:
870  *	TRUE - success
871  *	FALSE - failure
872  */
873 /*ARGSUSED*/
874 bool_t
875 cdda_sysvipc_play(di_dev_t *devp, curstat_t *s,
876 		  sword32_t start_lba, sword32_t end_lba)
877 {
878 	int	i;
879 	pid_t	cpid;
880 	semun_t	arg;
881 	bool_t	ret,
882 		(*rfunc)(di_dev_t *),
883 		(*wfunc)(curstat_t *);
884 	void	(*rdone)(bool_t),
885 		(*wdone)(bool_t);
886 
887 	if (cd == NULL && !cdda_sysvipc_init2(s))
888 		return FALSE;
889 
890 	rfunc = cdda_rd_calltbl[app_data.cdda_rdmethod].readfunc;
891 	wfunc = cdda_wr_calltbl[app_data.cdda_wrmethod].writefunc;
892 	rdone = cdda_rd_calltbl[app_data.cdda_rdmethod].readdone;
893 	wdone = cdda_wr_calltbl[app_data.cdda_wrmethod].writedone;
894 
895 	if (start_lba >= end_lba) {
896 		(void) sprintf(errbuf,
897 			    "cdda_sysvipc_play: "
898 			    "Start LBA is greater than or equal to end LBA.");
899 		CDDA_INFO(errbuf);
900 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
901 		return FALSE;
902 	}
903 
904 	/* If not stopped, stop first */
905 	if (cd->i->state != CDSTAT_COMPLETED)
906 		(void) cdda_sysvipc_stop(devp, s);
907 
908 	/* Set status */
909 	cd->i->state = CDSTAT_PLAYING;
910 
911 	/* Where are we starting */
912 	cd->i->start_lba = start_lba;
913 	cd->i->end_lba = end_lba;
914 
915 	/* Not finished reading */
916 	cd->i->cdda_done = 0;
917 
918 	/* Buffer pointers */
919 	cd->cdb->occupied = 0;
920 	cd->cdb->nextin = 0;
921 	cd->cdb->nextout = 0;
922 
923 	/* Clear message buffer */
924 	cd->i->msgbuf[0] = '\0';
925 
926 	/* Room available */
927 	arg.val = 1;
928 	if (SEMCTL(semid, ROOM, SETVAL, arg) < 0) {
929 		(void) sprintf(errbuf,
930 			    "cdda_sysvipc_play: semctl SETVAL failed "
931 			    "(errno=%d)",
932 			    errno);
933 		CDDA_INFO(errbuf);
934 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
935 		cd->i->state = CDSTAT_COMPLETED;
936 		return FALSE;
937 	}
938 
939 	/* No data available */
940 	arg.val = 0;
941 	if (SEMCTL(semid, DATA, SETVAL, arg) < 0) {
942 		(void) sprintf(errbuf,
943 			    "cdda_sysvipc_play: semctl SETVAL failed "
944 			    "(errno=%d)",
945 			    errno);
946 		CDDA_INFO(errbuf);
947 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
948 		cd->i->state = CDSTAT_COMPLETED;
949 		return FALSE;
950 	}
951 
952 	/* Fork CDDA reader process */
953 	switch (cpid = FORK()) {
954 	case -1:
955 		(void) sprintf(errbuf,
956 			    "cdda_sysvipc_play: fork failed (reader) "
957 			    "(errno=%d)",
958 			    errno);
959 		CDDA_INFO(errbuf);
960 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
961 		cd->i->state = CDSTAT_COMPLETED;
962 		if (cd->i->writer != (thid_t) 0) {
963 			cdda_sysvipc_kill(cd->i->writer, SIGTERM);
964 			cd->i->writer = (thid_t) 0;
965 		}
966 		return FALSE;
967 
968 	case 0:
969 		/* Child */
970 
971 		/* Close any unneeded file descriptors */
972 		for (i = 3; i < 10; i++) {
973 			if (i != devp->fd)
974 				(void) close(i);
975 		}
976 
977 		/* Increase reader process scheduling priority if specified */
978 		if (app_data.cdda_sched & CDDA_RDPRI)
979 			cdda_sysvipc_highpri("Reader process");
980 
981 		/* Store reader pid */
982 		cd->i->reader = (thid_t)(unsigned long) getpid();
983 
984 		/* Signal handling */
985 		(void) util_signal(SIGINT, SIG_IGN);
986 		(void) util_signal(SIGQUIT, SIG_IGN);
987 		(void) util_signal(SIGPIPE, SIG_IGN);
988 		(void) util_signal(SIGTERM, cdda_sysvipc_rsig);
989 
990 		/* Call read function */
991 		ret = (*rfunc)(devp);
992 
993 		/* Call cleanup function */
994 		(*rdone)((bool_t) !ret);
995 
996 		_exit(ret ? 0 : 1);
997 		/*NOTREACHED*/
998 
999 	default:
1000 		/* Parent */
1001 		DBGPRN(DBG_GEN)(errfp, "\nStarted reader process pid=%d\n",
1002 				(int) cpid);
1003 		break;
1004 	}
1005 
1006 	/* Fork CDDA writer process */
1007 	switch (cpid = FORK()) {
1008 	case -1:
1009 		(void) sprintf(errbuf,
1010 			    "cdda_sysvipc_play: fork failed (writer) "
1011 			    "(errno=%d)",
1012 			    errno);
1013 		CDDA_INFO(errbuf);
1014 		DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
1015 		cd->i->state = CDSTAT_COMPLETED;
1016 		if (cd->i->reader != (thid_t) 0) {
1017 			cdda_sysvipc_kill(cd->i->reader, SIGTERM);
1018 			cd->i->reader = (thid_t) 0;
1019 		}
1020 		return FALSE;
1021 
1022 	case 0:
1023 		/* Child */
1024 
1025 		/* Close any unneeded file descriptors */
1026 		for (i = 3; i < 10; i++)
1027 			(void) close(i);
1028 
1029 		/* Increase writer process scheduling priority if specified */
1030 		if (app_data.cdda_sched & CDDA_WRPRI)
1031 			cdda_sysvipc_highpri("Writer process");
1032 
1033 #ifndef HAS_EUID
1034 		/* Set to original uid/gid */
1035 		util_set_ougid();
1036 #endif
1037 
1038 		/* Store writer pid */
1039 		cd->i->writer = (thid_t)(unsigned long) getpid();
1040 
1041 		/* Signal handling */
1042 		(void) util_signal(SIGINT, SIG_IGN);
1043 		(void) util_signal(SIGQUIT, SIG_IGN);
1044 		(void) util_signal(SIGPIPE, SIG_IGN);
1045 		(void) util_signal(SIGTERM, cdda_sysvipc_wsig);
1046 
1047 		/* Call write function */
1048 		ret = (*wfunc)(s);
1049 
1050 		/* Call cleanup function */
1051 		(*wdone)((bool_t) !ret);
1052 
1053 		_exit(ret ? 0 : 1);
1054 		/*NOTREACHED*/
1055 
1056 	default:
1057 		/* Parent */
1058 		DBGPRN(DBG_GEN)(errfp, "\nStarted writer process pid=%d\n",
1059 				(int) cpid);
1060 		break;
1061 	}
1062 
1063 	return TRUE;
1064 }
1065 
1066 
1067 /*
1068  * cdda_sysvipc_pause_resume
1069  *	Pause CDDA playback.
1070  *
1071  * Args:
1072  *	devp   - Read device descriptor
1073  *	s      - Pointer to the curstat_t structure
1074  *	resume - Whether to resume playback
1075  *
1076  * Return:
1077  *	TRUE - success
1078  *	FALSE - failure
1079  */
1080 /*ARGSUSED*/
1081 bool_t
1082 cdda_sysvipc_pause_resume(di_dev_t *devp, curstat_t *s, bool_t resume)
1083 {
1084 	if (cd == NULL && !cdda_sysvipc_init2(s))
1085 		return FALSE;
1086 
1087 	if (cd->i->writer != (thid_t) 0) {
1088 		if (resume)
1089 			cd->i->state = CDSTAT_PLAYING;
1090 		else
1091 			cd->i->state = CDSTAT_PAUSED;
1092 
1093 		/* Skip heartbeat checking */
1094 		skip_hbchk = CDDA_HB_SKIP;
1095 	}
1096 	return TRUE;
1097 }
1098 
1099 
1100 /*
1101  * cdda_sysvipc_stop
1102  *	Stop CDDA playback.
1103  *
1104  * Args:
1105  *	devp - Read device descriptor
1106  *	s    - Pointer to the curstat_t structure
1107  *
1108  * Return:
1109  *	TRUE - success
1110  *	FALSE - failure
1111  */
1112 /*ARGSUSED*/
1113 bool_t
1114 cdda_sysvipc_stop(di_dev_t *devp, curstat_t *s)
1115 {
1116 	int		ret;
1117 	pid_t		cpid;
1118 	waitret_t	wstat;
1119 
1120 	if (cd == NULL && !cdda_sysvipc_init2(s))
1121 		return FALSE;
1122 
1123 	/* Set status */
1124 	cd->i->state = CDSTAT_COMPLETED;
1125 
1126 	if (cd->i->writer != (thid_t) 0) {
1127 		cpid = (pid_t)(unsigned long) cd->i->writer;
1128 
1129 		/* Wait for writer, blocking */
1130 		DBGPRN(DBG_GEN)(errfp,
1131 			"\ncdda_sysvipc_stop: Waiting for writer pid=%d\n",
1132 			(int) cpid);
1133 
1134 		ret = util_waitchild(cpid, app_data.hb_timeout,
1135 				     NULL, 0, FALSE, &wstat);
1136 		if (ret < 0) {
1137 			DBGPRN(DBG_GEN)(errfp,
1138 				"waitpid on writer failed (errno=%d)\n",
1139 				errno
1140 			);
1141 		}
1142 		else if (WIFEXITED(wstat)) {
1143 			ret = WEXITSTATUS(wstat);
1144 			DBGPRN(DBG_GEN)(errfp, "Writer exited, status %d\n",
1145 					ret);
1146 
1147 			/* Display message, if any */
1148 			if (ret != 0 && cd->i->msgbuf[0] != '\0')
1149 				CDDA_INFO(cd->i->msgbuf);
1150 		}
1151 		else if (WIFSIGNALED(wstat)) {
1152 			DBGPRN(DBG_GEN)(errfp, "Writer killed, signal %d\n",
1153 					WTERMSIG(wstat));
1154 		}
1155 
1156 		cd->i->writer = (thid_t) 0;
1157 	}
1158 
1159 	if (cd->i->reader != (thid_t) 0) {
1160 		cpid = (pid_t)(unsigned long) cd->i->reader;
1161 
1162 		/* Wait for reader, blocking */
1163 		DBGPRN(DBG_GEN)(errfp,
1164 			"\ncdda_sysvipc_stop: Waiting for reader pid=%d\n",
1165 			(int) cpid);
1166 
1167 		ret = util_waitchild(cpid, app_data.hb_timeout,
1168 				     NULL, 0, FALSE, &wstat);
1169 		if (ret < 0) {
1170 			DBGPRN(DBG_GEN)(errfp,
1171 				"waitpid on writer failed (errno=%d)\n",
1172 				errno
1173 			);
1174 		}
1175 		else if (WIFEXITED(wstat)) {
1176 			ret = WEXITSTATUS(wstat);
1177 			DBGPRN(DBG_GEN)(errfp, "Reader exited, status %d\n",
1178 				        ret);
1179 
1180 			/* Display message, if any */
1181 			if (ret != 0 && cd->i->msgbuf[0] != '\0')
1182 				CDDA_INFO(cd->i->msgbuf);
1183 		}
1184 		else if (WIFSIGNALED(wstat)) {
1185 			DBGPRN(DBG_GEN)(errfp, "Reader killed, signal %d\n",
1186 				        WTERMSIG(wstat));
1187 		}
1188 
1189 		cd->i->reader = (thid_t) 0;
1190 	}
1191 
1192 	/* Reset states */
1193 	cdda_sysvipc_initshm(s);
1194 
1195 	return TRUE;
1196 }
1197 
1198 
1199 /*
1200  * cdda_sysvipc_vol
1201  *	Change volume setting.
1202  *
1203  * Args:
1204  *	devp  - Read device descriptor
1205  *	s     - Pointer to the curstat_t structure
1206  *	vol   - Desired volume level
1207  *	query - Whether querying or setting the volume
1208  *
1209  * Return:
1210  *	The volume setting, or -1 on failure.
1211  */
1212 /*ARGSUSED*/
1213 int
1214 cdda_sysvipc_vol(di_dev_t *devp, curstat_t *s, int vol, bool_t query)
1215 {
1216 	if (cd == NULL && !cdda_sysvipc_init2(s))
1217 		return -1;
1218 
1219 	if (query) {
1220 		s->level_left = (byte_t) cd->i->vol_left;
1221 		s->level_right = (byte_t) cd->i->vol_right;
1222 		return (cd->i->vol);
1223 	}
1224 
1225 	cd->i->vol_taper = app_data.vol_taper;
1226 	cd->i->vol = vol;
1227 	cd->i->vol_left = (int) s->level_left;
1228 	cd->i->vol_right = (int) s->level_right;
1229 
1230 	return (vol);
1231 }
1232 
1233 
1234 /*
1235  * cdda_sysvipc_chroute
1236  *	Change channel routing setting.
1237  *
1238  * Args:
1239  *	devp - Read device descriptor
1240  *	s    - Pointer to the curstat_t structure
1241  *
1242  * Return:
1243  *	TRUE - success
1244  *	FALSE - failure
1245  */
1246 /*ARGSUSED*/
1247 bool_t
1248 cdda_sysvipc_chroute(di_dev_t *devp, curstat_t *s)
1249 {
1250 	if (cd != NULL) {
1251 		cd->i->chroute = app_data.ch_route;
1252 		return TRUE;
1253 	}
1254 	else
1255 		return FALSE;
1256 }
1257 
1258 
1259 /*
1260  * cdda_sysvipc_att
1261  *	Change level attenuator setting.
1262  *
1263  * Args:
1264  *	s - Pointer to the curstat_t structure
1265  *
1266  * Return:
1267  *	Nothing.
1268  */
1269 void
1270 cdda_sysvipc_att(curstat_t *s)
1271 {
1272 	if (cd != NULL)
1273 		cd->i->att = (int) s->cdda_att;
1274 }
1275 
1276 
1277 /*
1278  * cdda_sysvipc_outport
1279  *	Change output port setting.
1280  *
1281  * Args:
1282  *	None.
1283  *
1284  * Return:
1285  *	Nothing.
1286  */
1287 void
1288 cdda_sysvipc_outport(void)
1289 {
1290 	if (cd != NULL)
1291 		cd->i->outport = (int) app_data.outport;
1292 }
1293 
1294 
1295 /*
1296  * cdda_sysvipc_getstatus
1297  *	Get CDDA playback status.
1298  *
1299  * Args:
1300  *	devp - Read device descriptor
1301  *	s    - Pointer to curstat_t structure
1302  *	sp - CDDA status return structure
1303  *
1304  * Return:
1305  *	TRUE  - success
1306  *	FALSE - failure
1307  */
1308 /*ARGSUSED*/
1309 bool_t
1310 cdda_sysvipc_getstatus(di_dev_t *devp, curstat_t *s, cdstat_t *sp)
1311 {
1312 	int		i;
1313 	time_t		now;
1314 	bool_t		reader_fail,
1315 			writer_fail;
1316 	static int	hbint = 0,
1317 			hbcnt = 0;
1318 
1319 	if (cd == NULL && !cdda_sysvipc_init2(s))
1320 		return FALSE;
1321 
1322 	/* Current playback status */
1323 	sp->status = cd->i->state;
1324 	if (sp->status == CDSTAT_COMPLETED)
1325 		(void) cdda_sysvipc_stop(devp, s);
1326 
1327 	/* Current playback location */
1328 	sp->abs_addr.addr = cd->i->start_lba + cd->i->frm_played;
1329 
1330 	util_blktomsf(
1331 		sp->abs_addr.addr,
1332 		&sp->abs_addr.min,
1333 		&sp->abs_addr.sec,
1334 		&sp->abs_addr.frame,
1335 		MSF_OFFSET
1336 	);
1337 
1338 	i = cd->i->trk_idx;
1339 	if (sp->abs_addr.addr >= s->trkinfo[i].addr &&
1340 	    sp->abs_addr.addr < s->trkinfo[i+1].addr) {
1341 			sp->track = s->trkinfo[i].trkno;
1342 
1343 			sp->rel_addr.addr =
1344 				sp->abs_addr.addr - s->trkinfo[i].addr;
1345 
1346 			util_blktomsf(
1347 				sp->rel_addr.addr,
1348 				&sp->rel_addr.min,
1349 				&sp->rel_addr.sec,
1350 				&sp->rel_addr.frame,
1351 				0
1352 			);
1353 	}
1354 	else for (i = 0; i < (int) s->tot_trks; i++) {
1355 		if (sp->abs_addr.addr >= s->trkinfo[i].addr &&
1356 		    sp->abs_addr.addr < s->trkinfo[i+1].addr) {
1357 			sp->track = s->trkinfo[i].trkno;
1358 
1359 			sp->rel_addr.addr =
1360 				sp->abs_addr.addr - s->trkinfo[i].addr;
1361 
1362 			util_blktomsf(
1363 				sp->rel_addr.addr,
1364 				&sp->rel_addr.min,
1365 				&sp->rel_addr.sec,
1366 				&sp->rel_addr.frame,
1367 				0
1368 			);
1369 			break;
1370 		}
1371 	}
1372 
1373 	sp->index = 1;	/* Index number not supported in this mode */
1374 
1375 	/* Current volume and balance */
1376 	sp->level = (byte_t) cd->i->vol;
1377 	sp->level_left = (byte_t) cd->i->vol_left;
1378 	sp->level_right = (byte_t) cd->i->vol_right;
1379 
1380 	sp->tot_frm = cd->i->end_lba - cd->i->start_lba + 1;
1381 	sp->frm_played = cd->i->frm_played;
1382 	sp->frm_per_sec = cd->i->frm_per_sec;
1383 
1384 	/* Initialize heartbeat checking interval */
1385 	if (hbint == 0)
1386 		hbcnt = hbint = (1000 / app_data.stat_interval) + 1;
1387 
1388 	/* Check reader and writer heartbeats */
1389 	if (sp->status == CDSTAT_PLAYING && --hbcnt == 0 &&
1390 	    cd->i->reader != (thid_t) 0 && cd->i->writer != (thid_t) 0 &&
1391 	    cd->i->reader_hb != 0 && cd->i->writer_hb != 0) {
1392 		hbcnt = hbint;
1393 		now = time(NULL);
1394 
1395 		if (skip_hbchk > 0) {
1396 		    /* Skip heartbeat checking for a few iterations after
1397 		     * resuming from pause, to let the reader and writer
1398 		     * threads to catch up.
1399 		     */
1400 		    reader_fail = writer_fail = FALSE;
1401 		    skip_hbchk--;
1402 		}
1403 		else {
1404 		    reader_fail = (bool_t)
1405 			((now - cd->i->reader_hb) > cd->i->reader_hb_timeout);
1406 		    writer_fail = (bool_t)
1407 			((now - cd->i->writer_hb) > cd->i->writer_hb_timeout);
1408 		}
1409 
1410 		if (reader_fail || writer_fail) {
1411 			/* Reader or writer is hung or died */
1412 			cdda_sysvipc_kill(cd->i->reader, SIGTERM);
1413 			cdda_sysvipc_kill(cd->i->writer, SIGTERM);
1414 
1415 			(void) cdda_sysvipc_stop(devp, s);
1416 
1417 			(void) sprintf(errbuf,
1418 				       "CDDA %s thread failure!",
1419 				       writer_fail ? "writer" : "reader");
1420 			CDDA_INFO(errbuf);
1421 			DBGPRN(DBG_GEN)(errfp, "%s\n", errbuf);
1422 		}
1423 	}
1424 
1425 	return TRUE;
1426 }
1427 
1428 
1429 /*
1430  * cdda_sysvipc_debug
1431  *	Debug level change notification function
1432  *
1433  * Args:
1434  *	lev - New debug level
1435  *
1436  * Return:
1437  *	Nothing.
1438  */
1439 void
1440 cdda_sysvipc_debug(word32_t lev)
1441 {
1442 	if (cd != NULL)
1443 		cd->i->debug = lev;
1444 }
1445 
1446 
1447 
1448 /*
1449  * cdda_sysvipc_info
1450  *	Append CDDA read and write method information to supplied string.
1451  *
1452  * Args:
1453  *	str - The string to append to.
1454  *
1455  * Return:
1456  *	Nothing.
1457  */
1458 void
1459 cdda_sysvipc_info(char *str)
1460 {
1461 	void	(*rinfo)(char *),
1462 		(*winfo)(char *);
1463 
1464 	(void) strcat(str, "SYSV IPC\n");
1465 
1466 	(void) strcat(str, "    Extract:  ");
1467 	if (app_data.cdda_rdmethod <= CDDA_RD_NONE ||
1468 	    app_data.cdda_rdmethod >= CDDA_RD_METHODS) {
1469 		(void) strcat(str, "not configured\n");
1470 	}
1471 	else {
1472 		rinfo = cdda_rd_calltbl[app_data.cdda_rdmethod].readinfo;
1473 		if (rinfo == NULL)
1474 			(void) strcat(str, "not configured\n");
1475 		else
1476 			(*rinfo)(str);
1477 	}
1478 
1479 	(void) strcat(str, "    Playback: ");
1480 	if (app_data.cdda_wrmethod <= CDDA_WR_NONE ||
1481 	    app_data.cdda_wrmethod >= CDDA_WR_METHODS) {
1482 		(void) strcat(str, "not configured\n");
1483 	}
1484 	else {
1485 		winfo = cdda_wr_calltbl[app_data.cdda_wrmethod].writeinfo;
1486 		if (winfo == NULL)
1487 			(void) strcat(str, "not configured\n");
1488 		else
1489 			(*winfo)(str);
1490 	}
1491 }
1492 
1493 
1494 /*
1495  * cdda_sysvipc_initipc
1496  *	Retrieves shared memory and semaphores. Sets up our pointers
1497  *	from cd into shared memory.  Used by the reader/writer child
1498  *	processes.
1499  *
1500  * Args:
1501  *	cd - Pointer to the cd_state_t structure to be filled in
1502  *
1503  * Return:
1504  *	The IPC semaphore ID, or -1 if failed.
1505  */
1506 int
1507 cdda_sysvipc_initipc(cd_state_t *cdp)
1508 {
1509 	int	id;
1510 
1511 	if (sysvipc_shmaddr == NULL) {
1512 		DBGPRN(DBG_GEN)(errfp,
1513 			"cdda_sysvipc_initipc: No shared memory!\n");
1514 		return -1;
1515 	}
1516 
1517 	/* Now set our fields pointing into the shared memory */
1518 	cdp->cds = (cd_size_t *) sysvipc_shmaddr;
1519 
1520 	/* Info */
1521 	cdp->i = (cd_info_t *)(void *)
1522 		((byte_t *) sysvipc_shmaddr + sizeof(cd_size_t));
1523 
1524 	/* Buffer */
1525 	cdp->cdb = (cd_buffer_t *)(void *)
1526 		((byte_t *) cd->i + sizeof(cd_info_t));
1527 
1528 	/* Overlap */
1529 	cdp->cdb->olap = (byte_t *) ((byte_t *) cd->cdb + sizeof(cd_buffer_t));
1530 
1531 	/* Data */
1532 	cdp->cdb->data = (byte_t *)
1533 			 ((byte_t *) cd->cdb->olap +
1534 				    (cd->cds->search_bytes << 1));
1535 
1536 	/* Buffer */
1537 	cdp->cdb->b = (byte_t *)
1538 		      ((byte_t *) cd->cdb->data + cd->cds->chunk_bytes +
1539 				 (cd->cds->olap_bytes << 1));
1540 
1541 	/* Semaphores */
1542 	if ((id = semget(SEMKEY + app_data.devnum, 3,
1543 			 IPC_PERMS|IPC_EXCL)) < 0) {
1544 		DBGPRN(DBG_GEN)(errfp,
1545 			"cdda_sysvipc_initipc: semget failed: errno=%d\n",
1546 			errno);
1547 	}
1548 
1549 	return (id);
1550 }
1551 
1552 
1553 /*
1554  * cdda_sysvipc_waitsem
1555  *	Waits for a semaphore, decrementing its value.
1556  *
1557  * Args:
1558  *	semid - Semaphore id
1559  *	num   - Semaphore to wait on
1560  *
1561  * Return:
1562  *	Nothing.
1563  */
1564 void
1565 cdda_sysvipc_waitsem(int semid, int num)
1566 {
1567 	struct sembuf	p_buf;
1568 
1569 	p_buf.sem_num = (short) num;
1570 	p_buf.sem_op = -1;
1571 	p_buf.sem_flg = 0;
1572 
1573 	while (semop(semid, &p_buf, 1) < 0) {
1574 		if (errno == ERANGE) {
1575 			semun_t	arg;
1576 
1577 			arg.val = 0;
1578 			arg.buf = NULL;
1579 			arg.array = NULL;
1580 
1581 			(void) semctl(semid, num, SETVAL, arg);
1582 		}
1583 		else {
1584 			DBGPRN(DBG_GEN)(errfp, "cdda_sysvipc_waitsem: "
1585 			    "semop failed on %d (errno=%d)\n",
1586 			    num, errno);
1587 			break;
1588 		}
1589 	}
1590 }
1591 
1592 
1593 /*
1594  * cdda_sysvipc_postsem
1595  *	Release a semaphore, incrementing its value.
1596  *
1597  * Arguments:
1598  *	semid - Semaphore id
1599  *	num   - Semaphore to increment
1600  *
1601  * Return:
1602  *	Nothing.
1603  */
1604 void
1605 cdda_sysvipc_postsem(int semid, int num)
1606 {
1607 	struct sembuf	v_buf;
1608 
1609 	v_buf.sem_num = (short) num;
1610 	v_buf.sem_op = 1;
1611 	v_buf.sem_flg = 0;
1612 
1613 	while (semop(semid, &v_buf, 1) < 0) {
1614 		if (errno == ERANGE) {
1615 			semun_t	arg;
1616 
1617 			arg.val = 0;
1618 			arg.buf = NULL;
1619 			arg.array = NULL;
1620 
1621 			(void) semctl(semid, num, SETVAL, arg);
1622 		}
1623 		else {
1624 			DBGPRN(DBG_GEN)(errfp, "cdda_sysvipc_postsem: "
1625 			    "semop failed on %d (errno=%d)\n",
1626 			    num, errno);
1627 			break;
1628 		}
1629 	}
1630 }
1631 
1632 
1633 /*
1634  * cdda_sysvipc_yield
1635  *	Let other processes run.
1636  *
1637  * Args:
1638  *	None.
1639  *
1640  * Return:
1641  *	Nothing.
1642  */
1643 void
1644 cdda_sysvipc_yield(void)
1645 {
1646 	/* Just use the delay function go to sleep and let the process
1647 	 * scheduler take care of it.
1648 	 */
1649 	util_delayms(1000);
1650 }
1651 
1652 
1653 /*
1654  * cdda_sysvipc_kill
1655  *	Terminate a specified process.
1656  *
1657  * Args:
1658  *	id - The process or thread id
1659  *	sig - The signal
1660  *
1661  * Return:
1662  *	Nothing.
1663  */
1664 void
1665 cdda_sysvipc_kill(thid_t id, int sig)
1666 {
1667 	(void) kill((pid_t)(unsigned long) id, sig);
1668 }
1669 
1670 
1671 #endif	/* CDDA_SYSVIPC */
1672 
1673