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) != NULL) {
  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, NULL }
 489 };
 490 
 491 struct audio_ops_helper {
 492         struct cb_ops           cbops;  /* NB: must be first */
 493         struct streamtab        strtab;
 494         struct qinit            rqinit;
 495         struct qinit            wqinit;
 496         struct module_info      minfo;
 497         char                    name[MODMAXNAMELEN+1];
 498 };
 499 
 500 void
 501 audio_init_ops(struct dev_ops *devops, const char *name)
 502 {
 503         struct audio_ops_helper *helper;
 504 
 505         helper = kmem_zalloc(sizeof (*helper), KM_SLEEP);
 506 
 507         (void) strlcpy(helper->name, name, sizeof (helper->name));
 508 
 509         helper->minfo.mi_idnum = 0;  /* only for strlog(1M) */
 510         helper->minfo.mi_idname = helper->name;
 511         helper->minfo.mi_minpsz = 0;
 512         helper->minfo.mi_maxpsz = 8192;
 513         helper->minfo.mi_hiwat = 65536;
 514         helper->minfo.mi_lowat = 32768;
 515 
 516         helper->wqinit.qi_putp = audio_wput;
 517         helper->wqinit.qi_srvp = audio_wsrv;
 518         helper->wqinit.qi_qopen = NULL;
 519         helper->wqinit.qi_qclose = NULL;
 520         helper->wqinit.qi_qadmin = NULL;
 521         helper->wqinit.qi_minfo = &helper->minfo;
 522         helper->wqinit.qi_mstat = NULL;
 523 
 524         helper->rqinit.qi_putp = putq;
 525         helper->rqinit.qi_srvp = audio_rsrv;
 526         helper->rqinit.qi_qopen = audio_stropen;
 527         helper->rqinit.qi_qclose = audio_strclose;
 528         helper->rqinit.qi_qadmin = NULL;
 529         helper->rqinit.qi_minfo = &helper->minfo;
 530         helper->rqinit.qi_mstat = NULL;
 531 
 532         helper->strtab.st_rdinit = &helper->rqinit;
 533         helper->strtab.st_wrinit = &helper->wqinit;
 534         helper->strtab.st_muxrinit = NULL;
 535         helper->strtab.st_muxwinit = NULL;
 536 
 537         helper->cbops.cb_open = audio_open;
 538         helper->cbops.cb_close = audio_close;
 539         helper->cbops.cb_strategy = nodev;
 540         helper->cbops.cb_print = nodev;
 541         helper->cbops.cb_dump = nodev;
 542         helper->cbops.cb_read = audio_read;
 543         helper->cbops.cb_write = audio_write;
 544         helper->cbops.cb_ioctl = audio_ioctl;
 545         helper->cbops.cb_devmap = nodev;
 546         helper->cbops.cb_mmap = nodev;
 547         helper->cbops.cb_segmap = nodev;
 548         helper->cbops.cb_chpoll = audio_chpoll;
 549         helper->cbops.cb_prop_op = ddi_prop_op;
 550         helper->cbops.cb_str = &helper->strtab;
 551         helper->cbops.cb_flag = D_MP | D_64BIT;
 552         helper->cbops.cb_rev = CB_REV;
 553         helper->cbops.cb_aread = nodev;
 554         helper->cbops.cb_awrite = nodev;
 555 
 556         devops->devo_cb_ops = &helper->cbops;
 557         devops->devo_getinfo = audio_getinfo;
 558 }
 559 
 560 void
 561 audio_fini_ops(struct dev_ops *devops)
 562 {
 563         kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper));
 564         devops->devo_cb_ops = NULL;
 565         devops->devo_getinfo = NULL;
 566 }
 567 
 568 void
 569 auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va)
 570 {
 571         char    buf[256];
 572 
 573         if (dev != NULL) {
 574                 (void) snprintf(buf, sizeof (buf), "%s#%d: %s",
 575                     ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip),
 576                     fmt);
 577         } else {
 578                 (void) snprintf(buf, sizeof (buf), "audio: %s", fmt);
 579         }
 580 
 581         vcmn_err(CE_WARN, buf, va);
 582 }
 583 
 584 
 585 void
 586 audio_dev_warn(audio_dev_t *dev, const char *fmt, ...)
 587 {
 588         va_list va;
 589 
 590         va_start(va, fmt);
 591         auimpl_dev_vwarn(dev, fmt, va);
 592         va_end(va);
 593 }
 594 
 595 /*
 596  * _init, _info, and _fini DDI glue.
 597  */
 598 int
 599 _init(void)
 600 {
 601         int     rv;
 602 
 603         auimpl_client_init();
 604         auimpl_dev_init();
 605         auimpl_sun_init();
 606         auimpl_oss_init();
 607 
 608         audio_init_ops(&audio_dev_ops, "audio");
 609 
 610         if ((rv = mod_install(&modlinkage)) != 0) {
 611                 audio_fini_ops(&audio_dev_ops);
 612                 auimpl_dev_fini();
 613                 auimpl_client_fini();
 614         }
 615         return (rv);
 616 }
 617 
 618 int
 619 _info(struct modinfo *modinfop)
 620 {
 621         return (mod_info(&modlinkage, modinfop));
 622 }
 623 
 624 int
 625 _fini(void)
 626 {
 627         int rv;
 628 
 629         if ((rv = mod_remove(&modlinkage)) != 0)
 630                 return (rv);
 631 
 632         auimpl_dev_fini();
 633         auimpl_client_fini();
 634 
 635         return (rv);
 636 }