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