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