1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (C) 4Front Technologies 1996-2008. 23 * 24 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/sysmacros.h> 30 #include <sys/stropts.h> 31 #include <sys/strsun.h> 32 #include <sys/list.h> 33 #include <sys/mkdev.h> 34 #include <sys/conf.h> 35 #include <sys/note.h> 36 #include <sys/atomic.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 40 #include "audio_impl.h" 41 42 /* 43 * Audio DDI glue implementation. 44 */ 45 46 /* 47 * The audio module is itself a pseudo driver, as it contains the 48 * logic to support un-associated nodes. (Think generic /dev/mixer 49 * and /dev/sndstat used by OSS.) 50 */ 51 static int 52 audio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 53 { 54 audio_dev_t *adev; 55 56 /* pseudo devices don't need S/R support */ 57 if ((cmd != DDI_ATTACH) || (dip == NULL)) { 58 return (DDI_FAILURE); 59 } 60 61 if (ddi_get_instance(dip) != 0) { 62 return (DDI_FAILURE); 63 } 64 65 /* this can't fail */ 66 adev = audio_dev_alloc(dip, 0); 67 adev->d_flags = DEV_SNDSTAT_CAP; 68 audio_dev_set_description(adev, "Audio Common Code"); 69 audio_dev_set_version(adev, "pseudo"); 70 ddi_set_driver_private(dip, adev); 71 72 /* look up our properties! */ 73 74 if (audio_dev_register(adev) != DDI_SUCCESS) { 75 audio_dev_free(adev); 76 return (DDI_FAILURE); 77 } 78 79 ddi_report_dev(dip); 80 81 return (DDI_SUCCESS); 82 } 83 84 static int 85 audio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 86 { 87 audio_dev_t *adev; 88 89 /* pseudo devices don't need S/R support */ 90 if (cmd != DDI_DETACH) { 91 return (DDI_FAILURE); 92 } 93 94 if (dip == NULL) { 95 return (DDI_FAILURE); 96 } 97 98 if ((adev = ddi_get_driver_private(dip)) == NULL) { 99 return (DDI_FAILURE); 100 } 101 102 if (audio_dev_unregister(adev) != DDI_SUCCESS) { 103 return (DDI_FAILURE); 104 } 105 106 audio_dev_free(adev); 107 108 return (DDI_SUCCESS); 109 } 110 111 static int 112 audio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp) 113 { 114 dip = NULL; 115 116 if (getminor((dev_t)arg) & AUDIO_MN_CLONE_MASK) { 117 audio_client_t *c; 118 c = auclnt_hold_by_devt((dev_t)arg); 119 if (c != NULL) { 120 dip = c->c_dev->d_dip; 121 auclnt_release(c); 122 } 123 } else { 124 audio_dev_t *adev; 125 if ((adev = auimpl_dev_hold_by_devt((dev_t)arg)) != NULL) { 126 dip = adev->d_dip; 127 auimpl_dev_release(adev); 128 } 129 } 130 131 if (dip == NULL) { 132 return (DDI_FAILURE); 133 } 134 135 switch (cmd) { 136 case DDI_INFO_DEVT2DEVINFO: 137 *resp = dip; 138 break; 139 case DDI_INFO_DEVT2INSTANCE: 140 *resp = (void *)(uintptr_t)ddi_get_instance(dip); 141 break; 142 default: 143 *resp = NULL; 144 return (DDI_FAILURE); 145 } 146 return (DDI_SUCCESS); 147 } 148 149 static int 150 audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp) 151 { 152 int rv; 153 audio_client_t *c; 154 155 if (otyp == OTYP_BLK) { 156 return (ENXIO); 157 } 158 159 if ((c = auimpl_client_create(*devp)) == NULL) { 160 audio_dev_warn(NULL, "client create failed"); 161 return (ENXIO); 162 } 163 164 c->c_omode = oflag; 165 c->c_pid = ddi_get_pid(); 166 c->c_cred = credp; 167 168 /* 169 * Call client/personality specific open handler. Note that 170 * we "insist" that there is an open. The personality layer 171 * will initialize/allocate any engines required. 172 * 173 * Hmm... do we need to pass in the cred? 174 */ 175 if ((rv = c->c_open(c, oflag)) != 0) { 176 audio_dev_warn(c->c_dev, "open failed (rv %d)", rv); 177 auimpl_client_destroy(c); 178 return (rv); 179 } 180 181 /* we do device cloning! */ 182 *devp = makedevice(c->c_major, c->c_minor); 183 184 /* now we can receive upcalls */ 185 auimpl_client_activate(c); 186 187 atomic_inc_uint(&c->c_dev->d_serial); 188 189 return (0); 190 } 191 192 static int 193 audio_stropen(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp) 194 { 195 int rv; 196 audio_client_t *c; 197 198 if (sflag != 0) { 199 /* no direct clone or module opens */ 200 return (ENXIO); 201 } 202 203 /* 204 * Make sure its a STREAMS personality - only legacy Sun API uses 205 * STREAMS. 206 */ 207 switch (AUDIO_MN_TYPE_MASK & getminor(*devp)) { 208 case AUDIO_MINOR_DEVAUDIO: 209 case AUDIO_MINOR_DEVAUDIOCTL: 210 break; 211 default: 212 return (ENOSTR); 213 } 214 215 if ((c = auimpl_client_create(*devp)) == NULL) { 216 audio_dev_warn(NULL, "client create failed"); 217 return (ENXIO); 218 } 219 220 rq->q_ptr = WR(rq)->q_ptr = c; 221 c->c_omode = oflag; 222 c->c_pid = ddi_get_pid(); 223 c->c_cred = credp; 224 c->c_rq = rq; 225 c->c_wq = WR(rq); 226 227 /* 228 * Call client/personality specific open handler. Note that 229 * we "insist" that there is an open. The personality layer 230 * will initialize/allocate any engines required. 231 * 232 * Hmm... do we need to pass in the cred? 233 */ 234 if ((rv = c->c_open(c, oflag)) != 0) { 235 audio_dev_warn(c->c_dev, "open failed (rv %d)", rv); 236 auimpl_client_destroy(c); 237 return (rv); 238 } 239 240 /* we do device cloning! */ 241 *devp = makedevice(c->c_major, c->c_minor); 242 243 qprocson(rq); 244 245 /* now we can receive upcalls */ 246 auimpl_client_activate(c); 247 248 atomic_inc_uint(&c->c_dev->d_serial); 249 250 return (0); 251 } 252 253 static int 254 audio_strclose(queue_t *rq, int flag, cred_t *credp) 255 { 256 audio_client_t *c; 257 audio_dev_t *d; 258 int rv; 259 260 _NOTE(ARGUNUSED(flag)); 261 _NOTE(ARGUNUSED(credp)); 262 263 if ((c = rq->q_ptr) == NULL) { 264 return (ENXIO); 265 } 266 if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) { 267 rv = auclnt_drain(c); 268 } 269 270 /* make sure we won't get any upcalls */ 271 auimpl_client_deactivate(c); 272 273 /* 274 * Pick up any data sitting around in input buffers. This 275 * avoids leaving record data stuck in queues. 276 */ 277 if (c->c_istream.s_engine != NULL) 278 auimpl_input_callback(c->c_istream.s_engine); 279 280 /* get a local hold on the device */ 281 d = c->c_dev; 282 auimpl_dev_hold(c->c_dev); 283 284 /* Turn off queue processing... */ 285 qprocsoff(rq); 286 287 /* Call personality specific close handler */ 288 c->c_close(c); 289 290 auimpl_client_destroy(c); 291 292 /* notify peers that a change has occurred */ 293 atomic_inc_uint(&d->d_serial); 294 295 /* now we can drop the release we had on the device */ 296 auimpl_dev_release(d); 297 298 return (rv); 299 } 300 301 static int 302 audio_close(dev_t dev, int flag, int otyp, cred_t *credp) 303 { 304 audio_client_t *c; 305 audio_dev_t *d; 306 307 _NOTE(ARGUNUSED(flag)); 308 _NOTE(ARGUNUSED(credp)); 309 _NOTE(ARGUNUSED(otyp)); 310 311 if ((c = auclnt_hold_by_devt(dev)) == NULL) { 312 audio_dev_warn(NULL, "close on bogus devt %x,%x", 313 getmajor(dev), getminor(dev)); 314 return (ENXIO); 315 } 316 317 /* we don't want any upcalls anymore */ 318 auimpl_client_deactivate(c); 319 320 /* 321 * Pick up any data sitting around in input buffers. This 322 * avoids leaving record data stuck in queues. 323 */ 324 if (c->c_istream.s_engine != NULL) 325 auimpl_input_callback(c->c_istream.s_engine); 326 327 /* get a local hold on the device */ 328 d = c->c_dev; 329 auimpl_dev_hold(c->c_dev); 330 331 /* 332 * NB: This must be done before c->c_close, since it calls 333 * auclnt_close which will block waiting for the refence count 334 * to drop to zero. 335 */ 336 auclnt_release(c); 337 338 /* Call personality specific close handler */ 339 c->c_close(c); 340 341 auimpl_client_destroy(c); 342 343 /* notify peers that a change has occurred */ 344 atomic_inc_uint(&d->d_serial); 345 346 /* now we can drop the release we had on the device */ 347 auimpl_dev_release(d); 348 349 return (0); 350 } 351 352 static int 353 audio_write(dev_t dev, struct uio *uio, cred_t *credp) 354 { 355 audio_client_t *c; 356 int rv; 357 358 if ((c = auclnt_hold_by_devt(dev)) == NULL) { 359 return (ENXIO); 360 } 361 if ((rv = auclnt_serialize(c)) == 0) { 362 rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp); 363 auclnt_unserialize(c); 364 } 365 auclnt_release(c); 366 367 return (rv); 368 } 369 370 static int 371 audio_read(dev_t dev, struct uio *uio, cred_t *credp) 372 { 373 audio_client_t *c; 374 int rv; 375 376 if ((c = auclnt_hold_by_devt(dev)) == NULL) { 377 return (ENXIO); 378 } 379 if ((rv = auclnt_serialize(c)) == 0) { 380 rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp); 381 auclnt_unserialize(c); 382 } 383 auclnt_release(c); 384 385 return (rv); 386 } 387 388 static int 389 audio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 390 int *rvalp) 391 { 392 audio_client_t *c; 393 int rv; 394 395 if ((c = auclnt_hold_by_devt(dev)) == NULL) { 396 return (ENXIO); 397 } 398 rv = (c->c_ioctl == NULL) ? ENXIO : c->c_ioctl(c, cmd, arg, mode, 399 credp, rvalp); 400 auclnt_release(c); 401 402 return (rv); 403 } 404 405 static int 406 audio_chpoll(dev_t dev, short events, int anyyet, short *reventsp, 407 struct pollhead **phpp) 408 { 409 audio_client_t *c; 410 int rv; 411 412 if ((c = auclnt_hold_by_devt(dev)) == NULL) { 413 return (ENXIO); 414 } 415 rv = (c->c_chpoll == NULL) ? 416 ENXIO : 417 c->c_chpoll(c, events, anyyet, reventsp, phpp); 418 auclnt_release(c); 419 420 return (rv); 421 } 422 423 static int 424 audio_wput(queue_t *wq, mblk_t *mp) 425 { 426 audio_client_t *c; 427 428 c = wq->q_ptr; 429 if (c->c_wput) { 430 c->c_wput(c, mp); 431 } else { 432 freemsg(mp); 433 } 434 return (0); 435 } 436 437 static int 438 audio_wsrv(queue_t *wq) 439 { 440 audio_client_t *c; 441 442 c = wq->q_ptr; 443 if (c->c_wsrv) { 444 c->c_wsrv(c); 445 } else { 446 flushq(wq, FLUSHALL); 447 } 448 return (0); 449 } 450 451 static int 452 audio_rsrv(queue_t *rq) 453 { 454 audio_client_t *c; 455 456 c = rq->q_ptr; 457 if (c->c_rsrv) { 458 c->c_rsrv(c); 459 } else { 460 flushq(rq, FLUSHALL); 461 } 462 return (0); 463 } 464 465 466 static struct dev_ops audio_dev_ops = { 467 DEVO_REV, /* rev */ 468 0, /* refcnt */ 469 NULL, /* getinfo */ 470 nulldev, /* identify */ 471 nulldev, /* probe */ 472 audio_attach, /* attach */ 473 audio_detach, /* detach */ 474 nodev, /* reset */ 475 NULL, /* cb_ops */ 476 NULL, /* bus_ops */ 477 NULL, /* power */ 478 }; 479 480 static struct modldrv modldrv = { 481 &mod_driverops, 482 "Audio Framework", 483 &audio_dev_ops, 484 }; 485 486 static struct modlinkage modlinkage = { 487 MODREV_1, /* MODREV_1 indicated by manual */ 488 &modldrv, 489 NULL 490 }; 491 492 struct audio_ops_helper { 493 struct cb_ops cbops; /* NB: must be first */ 494 struct streamtab strtab; 495 struct qinit rqinit; 496 struct qinit wqinit; 497 struct module_info minfo; 498 char name[MODMAXNAMELEN+1]; 499 }; 500 501 void 502 audio_init_ops(struct dev_ops *devops, const char *name) 503 { 504 struct audio_ops_helper *helper; 505 506 helper = kmem_zalloc(sizeof (*helper), KM_SLEEP); 507 508 (void) strlcpy(helper->name, name, sizeof (helper->name)); 509 510 helper->minfo.mi_idnum = 0; /* only for strlog(1M) */ 511 helper->minfo.mi_idname = helper->name; 512 helper->minfo.mi_minpsz = 0; 513 helper->minfo.mi_maxpsz = 8192; 514 helper->minfo.mi_hiwat = 65536; 515 helper->minfo.mi_lowat = 32768; 516 517 helper->wqinit.qi_putp = audio_wput; 518 helper->wqinit.qi_srvp = audio_wsrv; 519 helper->wqinit.qi_qopen = NULL; 520 helper->wqinit.qi_qclose = NULL; 521 helper->wqinit.qi_qadmin = NULL; 522 helper->wqinit.qi_minfo = &helper->minfo; 523 helper->wqinit.qi_mstat = NULL; 524 525 helper->rqinit.qi_putp = putq; 526 helper->rqinit.qi_srvp = audio_rsrv; 527 helper->rqinit.qi_qopen = audio_stropen; 528 helper->rqinit.qi_qclose = audio_strclose; 529 helper->rqinit.qi_qadmin = NULL; 530 helper->rqinit.qi_minfo = &helper->minfo; 531 helper->rqinit.qi_mstat = NULL; 532 533 helper->strtab.st_rdinit = &helper->rqinit; 534 helper->strtab.st_wrinit = &helper->wqinit; 535 helper->strtab.st_muxrinit = NULL; 536 helper->strtab.st_muxwinit = NULL; 537 538 helper->cbops.cb_open = audio_open; 539 helper->cbops.cb_close = audio_close; 540 helper->cbops.cb_strategy = nodev; 541 helper->cbops.cb_print = nodev; 542 helper->cbops.cb_dump = nodev; 543 helper->cbops.cb_read = audio_read; 544 helper->cbops.cb_write = audio_write; 545 helper->cbops.cb_ioctl = audio_ioctl; 546 helper->cbops.cb_devmap = nodev; 547 helper->cbops.cb_mmap = nodev; 548 helper->cbops.cb_segmap = nodev; 549 helper->cbops.cb_chpoll = audio_chpoll; 550 helper->cbops.cb_prop_op = ddi_prop_op; 551 helper->cbops.cb_str = &helper->strtab; 552 helper->cbops.cb_flag = D_MP | D_64BIT; 553 helper->cbops.cb_rev = CB_REV; 554 helper->cbops.cb_aread = nodev; 555 helper->cbops.cb_awrite = nodev; 556 557 devops->devo_cb_ops = &helper->cbops; 558 devops->devo_getinfo = audio_getinfo; 559 } 560 561 void 562 audio_fini_ops(struct dev_ops *devops) 563 { 564 kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper)); 565 devops->devo_cb_ops = NULL; 566 devops->devo_getinfo = NULL; 567 } 568 569 void 570 auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va) 571 { 572 char buf[256]; 573 574 if (dev != NULL) { 575 (void) snprintf(buf, sizeof (buf), "%s#%d: %s", 576 ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip), 577 fmt); 578 } else { 579 (void) snprintf(buf, sizeof (buf), "audio: %s", fmt); 580 } 581 582 vcmn_err(CE_WARN, buf, va); 583 } 584 585 586 void 587 audio_dev_warn(audio_dev_t *dev, const char *fmt, ...) 588 { 589 va_list va; 590 591 va_start(va, fmt); 592 auimpl_dev_vwarn(dev, fmt, va); 593 va_end(va); 594 } 595 596 /* 597 * _init, _info, and _fini DDI glue. 598 */ 599 int 600 _init(void) 601 { 602 int rv; 603 604 auimpl_client_init(); 605 auimpl_dev_init(); 606 auimpl_sun_init(); 607 auimpl_oss_init(); 608 609 audio_init_ops(&audio_dev_ops, "audio"); 610 611 if ((rv = mod_install(&modlinkage)) != 0) { 612 audio_fini_ops(&audio_dev_ops); 613 auimpl_dev_fini(); 614 auimpl_client_fini(); 615 } 616 return (rv); 617 } 618 619 int 620 _info(struct modinfo *modinfop) 621 { 622 return (mod_info(&modlinkage, modinfop)); 623 } 624 625 int 626 _fini(void) 627 { 628 int rv; 629 630 if ((rv = mod_remove(&modlinkage)) != 0) 631 return (rv); 632 633 auimpl_dev_fini(); 634 auimpl_client_fini(); 635 636 return (rv); 637 } 638