xref: /onnv-gate/usr/src/uts/common/io/audio/impl/audio_ddi.c (revision 11936:54dc8a89ba0d)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
229484Sgarrett.damore@Sun.COM  * Copyright (C) 4Front Technologies 1996-2008.
239484Sgarrett.damore@Sun.COM  *
24*11936Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
259484Sgarrett.damore@Sun.COM  * Use is subject to license terms.
269484Sgarrett.damore@Sun.COM  */
279484Sgarrett.damore@Sun.COM 
289484Sgarrett.damore@Sun.COM #include <sys/types.h>
299484Sgarrett.damore@Sun.COM #include <sys/sysmacros.h>
309484Sgarrett.damore@Sun.COM #include <sys/stropts.h>
319484Sgarrett.damore@Sun.COM #include <sys/strsun.h>
329484Sgarrett.damore@Sun.COM #include <sys/list.h>
339484Sgarrett.damore@Sun.COM #include <sys/mkdev.h>
349484Sgarrett.damore@Sun.COM #include <sys/conf.h>
359484Sgarrett.damore@Sun.COM #include <sys/note.h>
3610632Sgdamore@opensolaris.org #include <sys/atomic.h>
379484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
389484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
399484Sgarrett.damore@Sun.COM 
409484Sgarrett.damore@Sun.COM #include "audio_impl.h"
419484Sgarrett.damore@Sun.COM 
429484Sgarrett.damore@Sun.COM /*
439484Sgarrett.damore@Sun.COM  * Audio DDI glue implementation.
449484Sgarrett.damore@Sun.COM  */
459484Sgarrett.damore@Sun.COM 
469484Sgarrett.damore@Sun.COM /*
479484Sgarrett.damore@Sun.COM  * The audio module is itself a pseudo driver, as it contains the
489484Sgarrett.damore@Sun.COM  * logic to support un-associated nodes.  (Think generic /dev/mixer
499484Sgarrett.damore@Sun.COM  * and /dev/sndstat used by OSS.)
509484Sgarrett.damore@Sun.COM  */
519484Sgarrett.damore@Sun.COM static int
audio_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)529484Sgarrett.damore@Sun.COM audio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
539484Sgarrett.damore@Sun.COM {
549484Sgarrett.damore@Sun.COM 	audio_dev_t	*adev;
559484Sgarrett.damore@Sun.COM 
569484Sgarrett.damore@Sun.COM 	/* pseudo devices don't need S/R support */
579484Sgarrett.damore@Sun.COM 	if ((cmd != DDI_ATTACH) || (dip == NULL)) {
589484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
599484Sgarrett.damore@Sun.COM 	}
609484Sgarrett.damore@Sun.COM 
619484Sgarrett.damore@Sun.COM 	if (ddi_get_instance(dip) != 0) {
629484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
639484Sgarrett.damore@Sun.COM 	}
649484Sgarrett.damore@Sun.COM 
659484Sgarrett.damore@Sun.COM 	/* this can't fail */
669484Sgarrett.damore@Sun.COM 	adev = audio_dev_alloc(dip, 0);
679484Sgarrett.damore@Sun.COM 	adev->d_flags = DEV_SNDSTAT_CAP;
689484Sgarrett.damore@Sun.COM 	audio_dev_set_description(adev, "Audio Common Code");
699484Sgarrett.damore@Sun.COM 	audio_dev_set_version(adev, "pseudo");
709484Sgarrett.damore@Sun.COM 	ddi_set_driver_private(dip, adev);
719484Sgarrett.damore@Sun.COM 
729484Sgarrett.damore@Sun.COM 	/* look up our properties! */
739484Sgarrett.damore@Sun.COM 
749484Sgarrett.damore@Sun.COM 	if (audio_dev_register(adev) != NULL) {
759484Sgarrett.damore@Sun.COM 		audio_dev_free(adev);
769484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
779484Sgarrett.damore@Sun.COM 	}
789484Sgarrett.damore@Sun.COM 
799484Sgarrett.damore@Sun.COM 	ddi_report_dev(dip);
809484Sgarrett.damore@Sun.COM 
819484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
829484Sgarrett.damore@Sun.COM }
839484Sgarrett.damore@Sun.COM 
849484Sgarrett.damore@Sun.COM static int
audio_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)859484Sgarrett.damore@Sun.COM audio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
869484Sgarrett.damore@Sun.COM {
879484Sgarrett.damore@Sun.COM 	audio_dev_t	*adev;
889484Sgarrett.damore@Sun.COM 
899484Sgarrett.damore@Sun.COM 	/* pseudo devices don't need S/R support */
909484Sgarrett.damore@Sun.COM 	if (cmd != DDI_DETACH) {
919484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
929484Sgarrett.damore@Sun.COM 	}
939484Sgarrett.damore@Sun.COM 
949484Sgarrett.damore@Sun.COM 	if (dip == NULL) {
959484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
969484Sgarrett.damore@Sun.COM 	}
979484Sgarrett.damore@Sun.COM 
989484Sgarrett.damore@Sun.COM 	if ((adev = ddi_get_driver_private(dip)) == NULL) {
999484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
1009484Sgarrett.damore@Sun.COM 	}
1019484Sgarrett.damore@Sun.COM 
1029484Sgarrett.damore@Sun.COM 	if (audio_dev_unregister(adev) != DDI_SUCCESS) {
1039484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
1049484Sgarrett.damore@Sun.COM 	}
1059484Sgarrett.damore@Sun.COM 
1069484Sgarrett.damore@Sun.COM 	audio_dev_free(adev);
1079484Sgarrett.damore@Sun.COM 
1089484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
1099484Sgarrett.damore@Sun.COM }
1109484Sgarrett.damore@Sun.COM 
1119484Sgarrett.damore@Sun.COM static int
audio_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resp)1129484Sgarrett.damore@Sun.COM audio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
1139484Sgarrett.damore@Sun.COM {
1149484Sgarrett.damore@Sun.COM 	dip = NULL;
1159484Sgarrett.damore@Sun.COM 
1169484Sgarrett.damore@Sun.COM 	if (getminor((dev_t)arg) & AUDIO_MN_CLONE_MASK) {
1179484Sgarrett.damore@Sun.COM 		audio_client_t *c;
1189484Sgarrett.damore@Sun.COM 		c = auclnt_hold_by_devt((dev_t)arg);
1199484Sgarrett.damore@Sun.COM 		if (c != NULL) {
1209484Sgarrett.damore@Sun.COM 			dip = c->c_dev->d_dip;
1219484Sgarrett.damore@Sun.COM 			auclnt_release(c);
1229484Sgarrett.damore@Sun.COM 		}
1239484Sgarrett.damore@Sun.COM 	} else {
1249484Sgarrett.damore@Sun.COM 		audio_dev_t	*adev;
1259484Sgarrett.damore@Sun.COM 		if ((adev = auimpl_dev_hold_by_devt((dev_t)arg)) != NULL) {
1269484Sgarrett.damore@Sun.COM 			dip = adev->d_dip;
1279484Sgarrett.damore@Sun.COM 			auimpl_dev_release(adev);
1289484Sgarrett.damore@Sun.COM 		}
1299484Sgarrett.damore@Sun.COM 	}
1309484Sgarrett.damore@Sun.COM 
1319484Sgarrett.damore@Sun.COM 	if (dip == NULL) {
1329484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
1339484Sgarrett.damore@Sun.COM 	}
1349484Sgarrett.damore@Sun.COM 
1359484Sgarrett.damore@Sun.COM 	switch (cmd) {
1369484Sgarrett.damore@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
1379484Sgarrett.damore@Sun.COM 		*resp = dip;
1389484Sgarrett.damore@Sun.COM 		break;
1399484Sgarrett.damore@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
1409484Sgarrett.damore@Sun.COM 		*resp = (void *)(uintptr_t)ddi_get_instance(dip);
1419484Sgarrett.damore@Sun.COM 		break;
1429484Sgarrett.damore@Sun.COM 	default:
1439484Sgarrett.damore@Sun.COM 		*resp = NULL;
1449484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
1459484Sgarrett.damore@Sun.COM 	}
1469484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
1479484Sgarrett.damore@Sun.COM }
1489484Sgarrett.damore@Sun.COM 
1499484Sgarrett.damore@Sun.COM static int
audio_open(dev_t * devp,int oflag,int otyp,cred_t * credp)1509484Sgarrett.damore@Sun.COM audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp)
1519484Sgarrett.damore@Sun.COM {
1529484Sgarrett.damore@Sun.COM 	int			rv;
1539484Sgarrett.damore@Sun.COM 	audio_client_t		*c;
1549484Sgarrett.damore@Sun.COM 
1559484Sgarrett.damore@Sun.COM 	if (otyp == OTYP_BLK) {
1569484Sgarrett.damore@Sun.COM 		return (ENXIO);
1579484Sgarrett.damore@Sun.COM 	}
1589484Sgarrett.damore@Sun.COM 
1599484Sgarrett.damore@Sun.COM 	if ((c = auimpl_client_create(*devp)) == NULL) {
1609484Sgarrett.damore@Sun.COM 		audio_dev_warn(NULL, "client create failed");
1619484Sgarrett.damore@Sun.COM 		return (ENXIO);
1629484Sgarrett.damore@Sun.COM 	}
1639484Sgarrett.damore@Sun.COM 
1649484Sgarrett.damore@Sun.COM 	c->c_omode = oflag;
1659484Sgarrett.damore@Sun.COM 	c->c_pid = ddi_get_pid();
1669484Sgarrett.damore@Sun.COM 	c->c_cred = credp;
1679484Sgarrett.damore@Sun.COM 
1689484Sgarrett.damore@Sun.COM 	/*
1699484Sgarrett.damore@Sun.COM 	 * Call client/personality specific open handler.  Note that
1709484Sgarrett.damore@Sun.COM 	 * we "insist" that there is an open.  The personality layer
1719484Sgarrett.damore@Sun.COM 	 * will initialize/allocate any engines required.
1729484Sgarrett.damore@Sun.COM 	 *
1739484Sgarrett.damore@Sun.COM 	 * Hmm... do we need to pass in the cred?
1749484Sgarrett.damore@Sun.COM 	 */
1759484Sgarrett.damore@Sun.COM 	if ((rv = c->c_open(c, oflag)) != 0) {
1769484Sgarrett.damore@Sun.COM 		audio_dev_warn(c->c_dev, "open failed (rv %d)", rv);
1779484Sgarrett.damore@Sun.COM 		auimpl_client_destroy(c);
1789484Sgarrett.damore@Sun.COM 		return (rv);
1799484Sgarrett.damore@Sun.COM 	}
1809484Sgarrett.damore@Sun.COM 
1819484Sgarrett.damore@Sun.COM 	/* we do device cloning! */
1829484Sgarrett.damore@Sun.COM 	*devp = makedevice(c->c_major, c->c_minor);
1839484Sgarrett.damore@Sun.COM 
18410570Sgdamore@opensolaris.org 	/* now we can receive upcalls */
18510570Sgdamore@opensolaris.org 	auimpl_client_activate(c);
1869484Sgarrett.damore@Sun.COM 
18710632Sgdamore@opensolaris.org 	atomic_inc_uint(&c->c_dev->d_serial);
1889484Sgarrett.damore@Sun.COM 
1899484Sgarrett.damore@Sun.COM 	return (0);
1909484Sgarrett.damore@Sun.COM }
1919484Sgarrett.damore@Sun.COM 
1929484Sgarrett.damore@Sun.COM static int
audio_stropen(queue_t * rq,dev_t * devp,int oflag,int sflag,cred_t * credp)19310157Sgdamore@opensolaris.org audio_stropen(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
19410157Sgdamore@opensolaris.org {
19510157Sgdamore@opensolaris.org 	int			rv;
19610157Sgdamore@opensolaris.org 	audio_client_t		*c;
19710157Sgdamore@opensolaris.org 
19810157Sgdamore@opensolaris.org 	if (sflag != 0) {
19910157Sgdamore@opensolaris.org 		/* no direct clone or module opens */
20010157Sgdamore@opensolaris.org 		return (ENXIO);
20110157Sgdamore@opensolaris.org 	}
20210157Sgdamore@opensolaris.org 
20310157Sgdamore@opensolaris.org 	/*
20410157Sgdamore@opensolaris.org 	 * Make sure its a STREAMS personality - only legacy Sun API uses
20510157Sgdamore@opensolaris.org 	 * STREAMS.
20610157Sgdamore@opensolaris.org 	 */
20710157Sgdamore@opensolaris.org 	switch (AUDIO_MN_TYPE_MASK & getminor(*devp)) {
20810157Sgdamore@opensolaris.org 	case AUDIO_MINOR_DEVAUDIO:
20910157Sgdamore@opensolaris.org 	case AUDIO_MINOR_DEVAUDIOCTL:
21010157Sgdamore@opensolaris.org 		break;
21110157Sgdamore@opensolaris.org 	default:
21210157Sgdamore@opensolaris.org 		return (ENOSTR);
21310157Sgdamore@opensolaris.org 	}
21410157Sgdamore@opensolaris.org 
21510157Sgdamore@opensolaris.org 	if ((c = auimpl_client_create(*devp)) == NULL) {
21610157Sgdamore@opensolaris.org 		audio_dev_warn(NULL, "client create failed");
21710157Sgdamore@opensolaris.org 		return (ENXIO);
21810157Sgdamore@opensolaris.org 	}
21910157Sgdamore@opensolaris.org 
22010157Sgdamore@opensolaris.org 	rq->q_ptr = WR(rq)->q_ptr = c;
22110157Sgdamore@opensolaris.org 	c->c_omode = oflag;
22210157Sgdamore@opensolaris.org 	c->c_pid = ddi_get_pid();
22310157Sgdamore@opensolaris.org 	c->c_cred = credp;
22410157Sgdamore@opensolaris.org 	c->c_rq = rq;
22510157Sgdamore@opensolaris.org 	c->c_wq = WR(rq);
22610157Sgdamore@opensolaris.org 
22710157Sgdamore@opensolaris.org 	/*
22810157Sgdamore@opensolaris.org 	 * Call client/personality specific open handler.  Note that
22910157Sgdamore@opensolaris.org 	 * we "insist" that there is an open.  The personality layer
23010157Sgdamore@opensolaris.org 	 * will initialize/allocate any engines required.
23110157Sgdamore@opensolaris.org 	 *
23210157Sgdamore@opensolaris.org 	 * Hmm... do we need to pass in the cred?
23310157Sgdamore@opensolaris.org 	 */
23410157Sgdamore@opensolaris.org 	if ((rv = c->c_open(c, oflag)) != 0) {
23510157Sgdamore@opensolaris.org 		audio_dev_warn(c->c_dev, "open failed (rv %d)", rv);
23610157Sgdamore@opensolaris.org 		auimpl_client_destroy(c);
23710157Sgdamore@opensolaris.org 		return (rv);
23810157Sgdamore@opensolaris.org 	}
23910157Sgdamore@opensolaris.org 
24010157Sgdamore@opensolaris.org 	/* we do device cloning! */
24110157Sgdamore@opensolaris.org 	*devp = makedevice(c->c_major, c->c_minor);
24210157Sgdamore@opensolaris.org 
24310570Sgdamore@opensolaris.org 	qprocson(rq);
24410570Sgdamore@opensolaris.org 
24510570Sgdamore@opensolaris.org 	/* now we can receive upcalls */
24610570Sgdamore@opensolaris.org 	auimpl_client_activate(c);
24710157Sgdamore@opensolaris.org 
24810632Sgdamore@opensolaris.org 	atomic_inc_uint(&c->c_dev->d_serial);
24910157Sgdamore@opensolaris.org 
25010157Sgdamore@opensolaris.org 	return (0);
25110157Sgdamore@opensolaris.org }
25210157Sgdamore@opensolaris.org 
25310157Sgdamore@opensolaris.org static int
audio_strclose(queue_t * rq,int flag,cred_t * credp)25410157Sgdamore@opensolaris.org audio_strclose(queue_t *rq, int flag, cred_t *credp)
25510157Sgdamore@opensolaris.org {
25610157Sgdamore@opensolaris.org 	audio_client_t	*c;
25710157Sgdamore@opensolaris.org 	audio_dev_t	*d;
25810157Sgdamore@opensolaris.org 	int		rv;
25910157Sgdamore@opensolaris.org 
26010157Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(flag));
26110157Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(credp));
26210157Sgdamore@opensolaris.org 
26310157Sgdamore@opensolaris.org 	if ((c = rq->q_ptr) == NULL) {
26410157Sgdamore@opensolaris.org 		return (ENXIO);
26510157Sgdamore@opensolaris.org 	}
26610157Sgdamore@opensolaris.org 	if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
26710157Sgdamore@opensolaris.org 		rv = auclnt_drain(c);
26810157Sgdamore@opensolaris.org 	}
26910157Sgdamore@opensolaris.org 
27010570Sgdamore@opensolaris.org 	/* make sure we won't get any upcalls */
27110570Sgdamore@opensolaris.org 	auimpl_client_deactivate(c);
27210157Sgdamore@opensolaris.org 
27310157Sgdamore@opensolaris.org 	/*
27410157Sgdamore@opensolaris.org 	 * Pick up any data sitting around in input buffers.  This
27510157Sgdamore@opensolaris.org 	 * avoids leaving record data stuck in queues.
27610157Sgdamore@opensolaris.org 	 */
27710157Sgdamore@opensolaris.org 	if (c->c_istream.s_engine != NULL)
278*11936Sgdamore@opensolaris.org 		auimpl_input_callback(c->c_istream.s_engine);
27910157Sgdamore@opensolaris.org 
28010157Sgdamore@opensolaris.org 	/* get a local hold on the device */
28110157Sgdamore@opensolaris.org 	d = c->c_dev;
28210157Sgdamore@opensolaris.org 	auimpl_dev_hold(c->c_dev);
28310157Sgdamore@opensolaris.org 
28410157Sgdamore@opensolaris.org 	/* Turn off queue processing... */
28510157Sgdamore@opensolaris.org 	qprocsoff(rq);
28610157Sgdamore@opensolaris.org 
28710157Sgdamore@opensolaris.org 	/* Call personality specific close handler */
28810157Sgdamore@opensolaris.org 	c->c_close(c);
28910157Sgdamore@opensolaris.org 
29010157Sgdamore@opensolaris.org 	auimpl_client_destroy(c);
29110157Sgdamore@opensolaris.org 
29210157Sgdamore@opensolaris.org 	/* notify peers that a change has occurred */
29310632Sgdamore@opensolaris.org 	atomic_inc_uint(&d->d_serial);
29410157Sgdamore@opensolaris.org 
29510157Sgdamore@opensolaris.org 	/* now we can drop the release we had on the device */
29610157Sgdamore@opensolaris.org 	auimpl_dev_release(d);
29710157Sgdamore@opensolaris.org 
29810157Sgdamore@opensolaris.org 	return (rv);
29910157Sgdamore@opensolaris.org }
30010157Sgdamore@opensolaris.org 
30110157Sgdamore@opensolaris.org static int
audio_close(dev_t dev,int flag,int otyp,cred_t * credp)3029484Sgarrett.damore@Sun.COM audio_close(dev_t dev, int flag, int otyp, cred_t *credp)
3039484Sgarrett.damore@Sun.COM {
3049484Sgarrett.damore@Sun.COM 	audio_client_t	*c;
3059484Sgarrett.damore@Sun.COM 	audio_dev_t	*d;
3069484Sgarrett.damore@Sun.COM 
3079484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(flag));
3089484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(credp));
3099484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(otyp));
3109484Sgarrett.damore@Sun.COM 
3119484Sgarrett.damore@Sun.COM 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
31210157Sgdamore@opensolaris.org 		audio_dev_warn(NULL, "close on bogus devt %x,%x",
3139484Sgarrett.damore@Sun.COM 		    getmajor(dev), getminor(dev));
3149484Sgarrett.damore@Sun.COM 		return (ENXIO);
3159484Sgarrett.damore@Sun.COM 	}
3169484Sgarrett.damore@Sun.COM 
31710570Sgdamore@opensolaris.org 	/* we don't want any upcalls anymore */
31810570Sgdamore@opensolaris.org 	auimpl_client_deactivate(c);
3199484Sgarrett.damore@Sun.COM 
3209484Sgarrett.damore@Sun.COM 	/*
3219484Sgarrett.damore@Sun.COM 	 * Pick up any data sitting around in input buffers.  This
3229484Sgarrett.damore@Sun.COM 	 * avoids leaving record data stuck in queues.
3239484Sgarrett.damore@Sun.COM 	 */
3249484Sgarrett.damore@Sun.COM 	if (c->c_istream.s_engine != NULL)
325*11936Sgdamore@opensolaris.org 		auimpl_input_callback(c->c_istream.s_engine);
3269484Sgarrett.damore@Sun.COM 
3279484Sgarrett.damore@Sun.COM 	/* get a local hold on the device */
3289484Sgarrett.damore@Sun.COM 	d = c->c_dev;
3299484Sgarrett.damore@Sun.COM 	auimpl_dev_hold(c->c_dev);
3309484Sgarrett.damore@Sun.COM 
3319484Sgarrett.damore@Sun.COM 	/*
3329484Sgarrett.damore@Sun.COM 	 * NB: This must be done before c->c_close, since it calls
3339484Sgarrett.damore@Sun.COM 	 * auclnt_close which will block waiting for the refence count
3349484Sgarrett.damore@Sun.COM 	 * to drop to zero.
3359484Sgarrett.damore@Sun.COM 	 */
3369484Sgarrett.damore@Sun.COM 	auclnt_release(c);
3379484Sgarrett.damore@Sun.COM 
3389484Sgarrett.damore@Sun.COM 	/* Call personality specific close handler */
3399484Sgarrett.damore@Sun.COM 	c->c_close(c);
3409484Sgarrett.damore@Sun.COM 
3419484Sgarrett.damore@Sun.COM 	auimpl_client_destroy(c);
3429484Sgarrett.damore@Sun.COM 
3439484Sgarrett.damore@Sun.COM 	/* notify peers that a change has occurred */
34410632Sgdamore@opensolaris.org 	atomic_inc_uint(&d->d_serial);
3459484Sgarrett.damore@Sun.COM 
3469484Sgarrett.damore@Sun.COM 	/* now we can drop the release we had on the device */
3479484Sgarrett.damore@Sun.COM 	auimpl_dev_release(d);
3489484Sgarrett.damore@Sun.COM 
3499484Sgarrett.damore@Sun.COM 	return (0);
3509484Sgarrett.damore@Sun.COM }
3519484Sgarrett.damore@Sun.COM 
3529484Sgarrett.damore@Sun.COM static int
audio_write(dev_t dev,struct uio * uio,cred_t * credp)3539484Sgarrett.damore@Sun.COM audio_write(dev_t dev, struct uio *uio, cred_t *credp)
3549484Sgarrett.damore@Sun.COM {
3559484Sgarrett.damore@Sun.COM 	audio_client_t *c;
3569484Sgarrett.damore@Sun.COM 	int rv;
3579484Sgarrett.damore@Sun.COM 
3589484Sgarrett.damore@Sun.COM 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
3599484Sgarrett.damore@Sun.COM 		return (ENXIO);
3609484Sgarrett.damore@Sun.COM 	}
361*11936Sgdamore@opensolaris.org 	if ((rv = auclnt_serialize(c)) == 0) {
362*11936Sgdamore@opensolaris.org 		rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp);
363*11936Sgdamore@opensolaris.org 		auclnt_unserialize(c);
364*11936Sgdamore@opensolaris.org 	}
3659484Sgarrett.damore@Sun.COM 	auclnt_release(c);
3669484Sgarrett.damore@Sun.COM 
3679484Sgarrett.damore@Sun.COM 	return (rv);
3689484Sgarrett.damore@Sun.COM }
3699484Sgarrett.damore@Sun.COM 
3709484Sgarrett.damore@Sun.COM static int
audio_read(dev_t dev,struct uio * uio,cred_t * credp)3719484Sgarrett.damore@Sun.COM audio_read(dev_t dev, struct uio *uio, cred_t *credp)
3729484Sgarrett.damore@Sun.COM {
3739484Sgarrett.damore@Sun.COM 	audio_client_t *c;
3749484Sgarrett.damore@Sun.COM 	int rv;
3759484Sgarrett.damore@Sun.COM 
3769484Sgarrett.damore@Sun.COM 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
3779484Sgarrett.damore@Sun.COM 		return (ENXIO);
3789484Sgarrett.damore@Sun.COM 	}
379*11936Sgdamore@opensolaris.org 	if ((rv = auclnt_serialize(c)) == 0) {
380*11936Sgdamore@opensolaris.org 		rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp);
381*11936Sgdamore@opensolaris.org 		auclnt_unserialize(c);
382*11936Sgdamore@opensolaris.org 	}
3839484Sgarrett.damore@Sun.COM 	auclnt_release(c);
3849484Sgarrett.damore@Sun.COM 
3859484Sgarrett.damore@Sun.COM 	return (rv);
3869484Sgarrett.damore@Sun.COM }
3879484Sgarrett.damore@Sun.COM 
3889484Sgarrett.damore@Sun.COM static int
audio_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)3899484Sgarrett.damore@Sun.COM audio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
3909484Sgarrett.damore@Sun.COM     int *rvalp)
3919484Sgarrett.damore@Sun.COM {
3929484Sgarrett.damore@Sun.COM 	audio_client_t *c;
3939484Sgarrett.damore@Sun.COM 	int rv;
3949484Sgarrett.damore@Sun.COM 
3959484Sgarrett.damore@Sun.COM 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
3969484Sgarrett.damore@Sun.COM 		return (ENXIO);
3979484Sgarrett.damore@Sun.COM 	}
3989484Sgarrett.damore@Sun.COM 	rv = (c->c_ioctl == NULL) ? ENXIO : c->c_ioctl(c, cmd, arg, mode,
3999484Sgarrett.damore@Sun.COM 	    credp, rvalp);
4009484Sgarrett.damore@Sun.COM 	auclnt_release(c);
4019484Sgarrett.damore@Sun.COM 
4029484Sgarrett.damore@Sun.COM 	return (rv);
4039484Sgarrett.damore@Sun.COM }
4049484Sgarrett.damore@Sun.COM 
4059484Sgarrett.damore@Sun.COM static int
audio_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)4069484Sgarrett.damore@Sun.COM audio_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
4079484Sgarrett.damore@Sun.COM     struct pollhead **phpp)
4089484Sgarrett.damore@Sun.COM {
4099484Sgarrett.damore@Sun.COM 	audio_client_t *c;
4109484Sgarrett.damore@Sun.COM 	int rv;
4119484Sgarrett.damore@Sun.COM 
4129484Sgarrett.damore@Sun.COM 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
4139484Sgarrett.damore@Sun.COM 		return (ENXIO);
4149484Sgarrett.damore@Sun.COM 	}
4159484Sgarrett.damore@Sun.COM 	rv = (c->c_chpoll == NULL) ?
4169484Sgarrett.damore@Sun.COM 	    ENXIO :
4179484Sgarrett.damore@Sun.COM 	    c->c_chpoll(c, events, anyyet, reventsp, phpp);
4189484Sgarrett.damore@Sun.COM 	auclnt_release(c);
4199484Sgarrett.damore@Sun.COM 
4209484Sgarrett.damore@Sun.COM 	return (rv);
4219484Sgarrett.damore@Sun.COM }
4229484Sgarrett.damore@Sun.COM 
42310157Sgdamore@opensolaris.org static int
audio_wput(queue_t * wq,mblk_t * mp)42410157Sgdamore@opensolaris.org audio_wput(queue_t *wq, mblk_t *mp)
42510157Sgdamore@opensolaris.org {
42610157Sgdamore@opensolaris.org 	audio_client_t	*c;
42710157Sgdamore@opensolaris.org 
42810157Sgdamore@opensolaris.org 	c = wq->q_ptr;
42910157Sgdamore@opensolaris.org 	if (c->c_wput) {
43010157Sgdamore@opensolaris.org 		c->c_wput(c, mp);
43110157Sgdamore@opensolaris.org 	} else {
43210157Sgdamore@opensolaris.org 		freemsg(mp);
43310157Sgdamore@opensolaris.org 	}
43410157Sgdamore@opensolaris.org 	return (0);
43510157Sgdamore@opensolaris.org }
43610157Sgdamore@opensolaris.org 
43710157Sgdamore@opensolaris.org static int
audio_wsrv(queue_t * wq)43810157Sgdamore@opensolaris.org audio_wsrv(queue_t *wq)
43910157Sgdamore@opensolaris.org {
44010157Sgdamore@opensolaris.org 	audio_client_t	*c;
44110157Sgdamore@opensolaris.org 
44210157Sgdamore@opensolaris.org 	c = wq->q_ptr;
44310157Sgdamore@opensolaris.org 	if (c->c_wsrv) {
44410157Sgdamore@opensolaris.org 		c->c_wsrv(c);
44510157Sgdamore@opensolaris.org 	} else {
44610157Sgdamore@opensolaris.org 		flushq(wq, FLUSHALL);
44710157Sgdamore@opensolaris.org 	}
44810157Sgdamore@opensolaris.org 	return (0);
44910157Sgdamore@opensolaris.org }
4509484Sgarrett.damore@Sun.COM 
45110632Sgdamore@opensolaris.org static int
audio_rsrv(queue_t * rq)45210632Sgdamore@opensolaris.org audio_rsrv(queue_t *rq)
45310632Sgdamore@opensolaris.org {
45410632Sgdamore@opensolaris.org 	audio_client_t	*c;
45510632Sgdamore@opensolaris.org 
45610632Sgdamore@opensolaris.org 	c = rq->q_ptr;
45710632Sgdamore@opensolaris.org 	if (c->c_rsrv) {
45810632Sgdamore@opensolaris.org 		c->c_rsrv(c);
45910632Sgdamore@opensolaris.org 	} else {
46010632Sgdamore@opensolaris.org 		flushq(rq, FLUSHALL);
46110632Sgdamore@opensolaris.org 	}
46210632Sgdamore@opensolaris.org 	return (0);
46310632Sgdamore@opensolaris.org }
46410632Sgdamore@opensolaris.org 
46510632Sgdamore@opensolaris.org 
4669484Sgarrett.damore@Sun.COM static struct dev_ops audio_dev_ops = {
4679484Sgarrett.damore@Sun.COM 	DEVO_REV,		/* rev */
4689484Sgarrett.damore@Sun.COM 	0,			/* refcnt */
46910157Sgdamore@opensolaris.org 	NULL,			/* getinfo */
4709484Sgarrett.damore@Sun.COM 	nulldev,		/* identify */
4719484Sgarrett.damore@Sun.COM 	nulldev,		/* probe */
4729484Sgarrett.damore@Sun.COM 	audio_attach,		/* attach */
4739484Sgarrett.damore@Sun.COM 	audio_detach,		/* detach */
4749484Sgarrett.damore@Sun.COM 	nodev,			/* reset */
47510157Sgdamore@opensolaris.org 	NULL,			/* cb_ops */
4769484Sgarrett.damore@Sun.COM 	NULL,			/* bus_ops */
4779484Sgarrett.damore@Sun.COM 	NULL,			/* power */
4789484Sgarrett.damore@Sun.COM };
4799484Sgarrett.damore@Sun.COM 
4809484Sgarrett.damore@Sun.COM static struct modldrv modldrv = {
4819484Sgarrett.damore@Sun.COM 	&mod_driverops,
4829484Sgarrett.damore@Sun.COM 	"Audio Framework",
4839484Sgarrett.damore@Sun.COM 	&audio_dev_ops,
4849484Sgarrett.damore@Sun.COM };
4859484Sgarrett.damore@Sun.COM 
4869484Sgarrett.damore@Sun.COM static struct modlinkage modlinkage = {
4879484Sgarrett.damore@Sun.COM 	MODREV_1,			/* MODREV_1 indicated by manual */
4889484Sgarrett.damore@Sun.COM 	&modldrv,
4899484Sgarrett.damore@Sun.COM 	NULL
4909484Sgarrett.damore@Sun.COM };
4919484Sgarrett.damore@Sun.COM 
4929484Sgarrett.damore@Sun.COM struct audio_ops_helper {
49310157Sgdamore@opensolaris.org 	struct cb_ops		cbops;	/* NB: must be first */
49410157Sgdamore@opensolaris.org 	struct streamtab	strtab;
49510157Sgdamore@opensolaris.org 	struct qinit		rqinit;
49610157Sgdamore@opensolaris.org 	struct qinit		wqinit;
49710157Sgdamore@opensolaris.org 	struct module_info	minfo;
49810157Sgdamore@opensolaris.org 	char			name[MODMAXNAMELEN+1];
4999484Sgarrett.damore@Sun.COM };
5009484Sgarrett.damore@Sun.COM 
5019484Sgarrett.damore@Sun.COM void
audio_init_ops(struct dev_ops * devops,const char * name)5029484Sgarrett.damore@Sun.COM audio_init_ops(struct dev_ops *devops, const char *name)
5039484Sgarrett.damore@Sun.COM {
5049484Sgarrett.damore@Sun.COM 	struct audio_ops_helper	*helper;
5059484Sgarrett.damore@Sun.COM 
5069484Sgarrett.damore@Sun.COM 	helper = kmem_zalloc(sizeof (*helper), KM_SLEEP);
5079484Sgarrett.damore@Sun.COM 
50810157Sgdamore@opensolaris.org 	(void) strlcpy(helper->name, name, sizeof (helper->name));
50910157Sgdamore@opensolaris.org 
51010157Sgdamore@opensolaris.org 	helper->minfo.mi_idnum = 0;	/* only for strlog(1M) */
51110157Sgdamore@opensolaris.org 	helper->minfo.mi_idname = helper->name;
51210157Sgdamore@opensolaris.org 	helper->minfo.mi_minpsz = 0;
51310632Sgdamore@opensolaris.org 	helper->minfo.mi_maxpsz = 8192;
51410157Sgdamore@opensolaris.org 	helper->minfo.mi_hiwat = 65536;
51510157Sgdamore@opensolaris.org 	helper->minfo.mi_lowat = 32768;
51610157Sgdamore@opensolaris.org 
51710157Sgdamore@opensolaris.org 	helper->wqinit.qi_putp = audio_wput;
51810157Sgdamore@opensolaris.org 	helper->wqinit.qi_srvp = audio_wsrv;
51910157Sgdamore@opensolaris.org 	helper->wqinit.qi_qopen = NULL;
52010157Sgdamore@opensolaris.org 	helper->wqinit.qi_qclose = NULL;
52110157Sgdamore@opensolaris.org 	helper->wqinit.qi_qadmin = NULL;
52210157Sgdamore@opensolaris.org 	helper->wqinit.qi_minfo = &helper->minfo;
52310157Sgdamore@opensolaris.org 	helper->wqinit.qi_mstat = NULL;
52410157Sgdamore@opensolaris.org 
52510632Sgdamore@opensolaris.org 	helper->rqinit.qi_putp = putq;
52610632Sgdamore@opensolaris.org 	helper->rqinit.qi_srvp = audio_rsrv;
52710157Sgdamore@opensolaris.org 	helper->rqinit.qi_qopen = audio_stropen;
52810157Sgdamore@opensolaris.org 	helper->rqinit.qi_qclose = audio_strclose;
52910157Sgdamore@opensolaris.org 	helper->rqinit.qi_qadmin = NULL;
53010157Sgdamore@opensolaris.org 	helper->rqinit.qi_minfo = &helper->minfo;
53110157Sgdamore@opensolaris.org 	helper->rqinit.qi_mstat = NULL;
53210157Sgdamore@opensolaris.org 
53310157Sgdamore@opensolaris.org 	helper->strtab.st_rdinit = &helper->rqinit;
53410157Sgdamore@opensolaris.org 	helper->strtab.st_wrinit = &helper->wqinit;
53510157Sgdamore@opensolaris.org 	helper->strtab.st_muxrinit = NULL;
53610157Sgdamore@opensolaris.org 	helper->strtab.st_muxwinit = NULL;
53710157Sgdamore@opensolaris.org 
5389484Sgarrett.damore@Sun.COM 	helper->cbops.cb_open = audio_open;
5399484Sgarrett.damore@Sun.COM 	helper->cbops.cb_close = audio_close;
5409484Sgarrett.damore@Sun.COM 	helper->cbops.cb_strategy = nodev;
5419484Sgarrett.damore@Sun.COM 	helper->cbops.cb_print = nodev;
5429484Sgarrett.damore@Sun.COM 	helper->cbops.cb_dump = nodev;
5439484Sgarrett.damore@Sun.COM 	helper->cbops.cb_read = audio_read;
5449484Sgarrett.damore@Sun.COM 	helper->cbops.cb_write = audio_write;
5459484Sgarrett.damore@Sun.COM 	helper->cbops.cb_ioctl = audio_ioctl;
5469484Sgarrett.damore@Sun.COM 	helper->cbops.cb_devmap = nodev;
5479484Sgarrett.damore@Sun.COM 	helper->cbops.cb_mmap = nodev;
5489484Sgarrett.damore@Sun.COM 	helper->cbops.cb_segmap = nodev;
5499484Sgarrett.damore@Sun.COM 	helper->cbops.cb_chpoll = audio_chpoll;
5509484Sgarrett.damore@Sun.COM 	helper->cbops.cb_prop_op = ddi_prop_op;
55110157Sgdamore@opensolaris.org 	helper->cbops.cb_str = &helper->strtab;
5529484Sgarrett.damore@Sun.COM 	helper->cbops.cb_flag = D_MP | D_64BIT;
5539484Sgarrett.damore@Sun.COM 	helper->cbops.cb_rev = CB_REV;
5549484Sgarrett.damore@Sun.COM 	helper->cbops.cb_aread = nodev;
5559484Sgarrett.damore@Sun.COM 	helper->cbops.cb_awrite = nodev;
5569484Sgarrett.damore@Sun.COM 
5579484Sgarrett.damore@Sun.COM 	devops->devo_cb_ops = &helper->cbops;
5589484Sgarrett.damore@Sun.COM 	devops->devo_getinfo = audio_getinfo;
5599484Sgarrett.damore@Sun.COM }
5609484Sgarrett.damore@Sun.COM 
5619484Sgarrett.damore@Sun.COM void
audio_fini_ops(struct dev_ops * devops)5629484Sgarrett.damore@Sun.COM audio_fini_ops(struct dev_ops *devops)
5639484Sgarrett.damore@Sun.COM {
5649484Sgarrett.damore@Sun.COM 	kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper));
5659484Sgarrett.damore@Sun.COM 	devops->devo_cb_ops = NULL;
5669484Sgarrett.damore@Sun.COM 	devops->devo_getinfo = NULL;
5679484Sgarrett.damore@Sun.COM }
5689484Sgarrett.damore@Sun.COM 
5699484Sgarrett.damore@Sun.COM void
auimpl_dev_vwarn(audio_dev_t * dev,const char * fmt,va_list va)5709484Sgarrett.damore@Sun.COM auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va)
5719484Sgarrett.damore@Sun.COM {
5729484Sgarrett.damore@Sun.COM 	char	buf[256];
5739484Sgarrett.damore@Sun.COM 
5749484Sgarrett.damore@Sun.COM 	if (dev != NULL) {
5759484Sgarrett.damore@Sun.COM 		(void) snprintf(buf, sizeof (buf), "%s#%d: %s",
5769484Sgarrett.damore@Sun.COM 		    ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip),
5779484Sgarrett.damore@Sun.COM 		    fmt);
5789484Sgarrett.damore@Sun.COM 	} else {
5799484Sgarrett.damore@Sun.COM 		(void) snprintf(buf, sizeof (buf), "audio: %s", fmt);
5809484Sgarrett.damore@Sun.COM 	}
5819484Sgarrett.damore@Sun.COM 
5829484Sgarrett.damore@Sun.COM 	vcmn_err(CE_WARN, buf, va);
5839484Sgarrett.damore@Sun.COM }
5849484Sgarrett.damore@Sun.COM 
5859484Sgarrett.damore@Sun.COM 
5869484Sgarrett.damore@Sun.COM void
audio_dev_warn(audio_dev_t * dev,const char * fmt,...)5879484Sgarrett.damore@Sun.COM audio_dev_warn(audio_dev_t *dev, const char *fmt, ...)
5889484Sgarrett.damore@Sun.COM {
5899484Sgarrett.damore@Sun.COM 	va_list	va;
5909484Sgarrett.damore@Sun.COM 
5919484Sgarrett.damore@Sun.COM 	va_start(va, fmt);
5929484Sgarrett.damore@Sun.COM 	auimpl_dev_vwarn(dev, fmt, va);
5939484Sgarrett.damore@Sun.COM 	va_end(va);
5949484Sgarrett.damore@Sun.COM }
5959484Sgarrett.damore@Sun.COM 
5969484Sgarrett.damore@Sun.COM /*
5979484Sgarrett.damore@Sun.COM  * _init, _info, and _fini DDI glue.
5989484Sgarrett.damore@Sun.COM  */
5999484Sgarrett.damore@Sun.COM int
_init(void)6009484Sgarrett.damore@Sun.COM _init(void)
6019484Sgarrett.damore@Sun.COM {
6029484Sgarrett.damore@Sun.COM 	int	rv;
6039484Sgarrett.damore@Sun.COM 
6049484Sgarrett.damore@Sun.COM 	auimpl_client_init();
6059484Sgarrett.damore@Sun.COM 	auimpl_dev_init();
6069484Sgarrett.damore@Sun.COM 	auimpl_sun_init();
6079484Sgarrett.damore@Sun.COM 	auimpl_oss_init();
6089484Sgarrett.damore@Sun.COM 
60910157Sgdamore@opensolaris.org 	audio_init_ops(&audio_dev_ops, "audio");
61010157Sgdamore@opensolaris.org 
6119484Sgarrett.damore@Sun.COM 	if ((rv = mod_install(&modlinkage)) != 0) {
61210157Sgdamore@opensolaris.org 		audio_fini_ops(&audio_dev_ops);
6139484Sgarrett.damore@Sun.COM 		auimpl_dev_fini();
6149484Sgarrett.damore@Sun.COM 		auimpl_client_fini();
6159484Sgarrett.damore@Sun.COM 	}
6169484Sgarrett.damore@Sun.COM 	return (rv);
6179484Sgarrett.damore@Sun.COM }
6189484Sgarrett.damore@Sun.COM 
6199484Sgarrett.damore@Sun.COM int
_info(struct modinfo * modinfop)6209484Sgarrett.damore@Sun.COM _info(struct modinfo *modinfop)
6219484Sgarrett.damore@Sun.COM {
6229484Sgarrett.damore@Sun.COM 	return (mod_info(&modlinkage, modinfop));
6239484Sgarrett.damore@Sun.COM }
6249484Sgarrett.damore@Sun.COM 
6259484Sgarrett.damore@Sun.COM int
_fini(void)6269484Sgarrett.damore@Sun.COM _fini(void)
6279484Sgarrett.damore@Sun.COM {
6289484Sgarrett.damore@Sun.COM 	int rv;
6299484Sgarrett.damore@Sun.COM 
6309484Sgarrett.damore@Sun.COM 	if ((rv = mod_remove(&modlinkage)) != 0)
6319484Sgarrett.damore@Sun.COM 		return (rv);
6329484Sgarrett.damore@Sun.COM 
6339484Sgarrett.damore@Sun.COM 	auimpl_dev_fini();
6349484Sgarrett.damore@Sun.COM 	auimpl_client_fini();
6359484Sgarrett.damore@Sun.COM 
6369484Sgarrett.damore@Sun.COM 	return (rv);
6379484Sgarrett.damore@Sun.COM }
638