188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
2288447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
2388447a05SGarrett D'Amore  *
2468c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2588447a05SGarrett D'Amore  * Use is subject to license terms.
2688447a05SGarrett D'Amore  */
2788447a05SGarrett D'Amore 
2888447a05SGarrett D'Amore #include <sys/types.h>
2988447a05SGarrett D'Amore #include <sys/sysmacros.h>
3088447a05SGarrett D'Amore #include <sys/stropts.h>
3188447a05SGarrett D'Amore #include <sys/strsun.h>
3288447a05SGarrett D'Amore #include <sys/list.h>
3388447a05SGarrett D'Amore #include <sys/mkdev.h>
3488447a05SGarrett D'Amore #include <sys/conf.h>
3588447a05SGarrett D'Amore #include <sys/note.h>
36682cb104SGarrett D'Amore #include <sys/atomic.h>
3788447a05SGarrett D'Amore #include <sys/ddi.h>
3888447a05SGarrett D'Amore #include <sys/sunddi.h>
3988447a05SGarrett D'Amore 
4088447a05SGarrett D'Amore #include "audio_impl.h"
4188447a05SGarrett D'Amore 
4288447a05SGarrett D'Amore /*
4388447a05SGarrett D'Amore  * Audio DDI glue implementation.
4488447a05SGarrett D'Amore  */
4588447a05SGarrett D'Amore 
4688447a05SGarrett D'Amore /*
4788447a05SGarrett D'Amore  * The audio module is itself a pseudo driver, as it contains the
4888447a05SGarrett D'Amore  * logic to support un-associated nodes.  (Think generic /dev/mixer
4988447a05SGarrett D'Amore  * and /dev/sndstat used by OSS.)
5088447a05SGarrett D'Amore  */
5188447a05SGarrett D'Amore static int
audio_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5288447a05SGarrett D'Amore audio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5388447a05SGarrett D'Amore {
5488447a05SGarrett D'Amore 	audio_dev_t	*adev;
5588447a05SGarrett D'Amore 
5688447a05SGarrett D'Amore 	/* pseudo devices don't need S/R support */
5788447a05SGarrett D'Amore 	if ((cmd != DDI_ATTACH) || (dip == NULL)) {
5888447a05SGarrett D'Amore 		return (DDI_FAILURE);
5988447a05SGarrett D'Amore 	}
6088447a05SGarrett D'Amore 
6188447a05SGarrett D'Amore 	if (ddi_get_instance(dip) != 0) {
6288447a05SGarrett D'Amore 		return (DDI_FAILURE);
6388447a05SGarrett D'Amore 	}
6488447a05SGarrett D'Amore 
6588447a05SGarrett D'Amore 	/* this can't fail */
6688447a05SGarrett D'Amore 	adev = audio_dev_alloc(dip, 0);
6788447a05SGarrett D'Amore 	adev->d_flags = DEV_SNDSTAT_CAP;
6888447a05SGarrett D'Amore 	audio_dev_set_description(adev, "Audio Common Code");
6988447a05SGarrett D'Amore 	audio_dev_set_version(adev, "pseudo");
7088447a05SGarrett D'Amore 	ddi_set_driver_private(dip, adev);
7188447a05SGarrett D'Amore 
7288447a05SGarrett D'Amore 	/* look up our properties! */
7388447a05SGarrett D'Amore 
7417f65767SToomas Soome 	if (audio_dev_register(adev) != DDI_SUCCESS) {
7588447a05SGarrett D'Amore 		audio_dev_free(adev);
7688447a05SGarrett D'Amore 		return (DDI_FAILURE);
7788447a05SGarrett D'Amore 	}
7888447a05SGarrett D'Amore 
7988447a05SGarrett D'Amore 	ddi_report_dev(dip);
8088447a05SGarrett D'Amore 
8188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
8288447a05SGarrett D'Amore }
8388447a05SGarrett D'Amore 
8488447a05SGarrett D'Amore static int
audio_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)8588447a05SGarrett D'Amore audio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8688447a05SGarrett D'Amore {
8788447a05SGarrett D'Amore 	audio_dev_t	*adev;
8888447a05SGarrett D'Amore 
8988447a05SGarrett D'Amore 	/* pseudo devices don't need S/R support */
9088447a05SGarrett D'Amore 	if (cmd != DDI_DETACH) {
9188447a05SGarrett D'Amore 		return (DDI_FAILURE);
9288447a05SGarrett D'Amore 	}
9388447a05SGarrett D'Amore 
9488447a05SGarrett D'Amore 	if (dip == NULL) {
9588447a05SGarrett D'Amore 		return (DDI_FAILURE);
9688447a05SGarrett D'Amore 	}
9788447a05SGarrett D'Amore 
9888447a05SGarrett D'Amore 	if ((adev = ddi_get_driver_private(dip)) == NULL) {
9988447a05SGarrett D'Amore 		return (DDI_FAILURE);
10088447a05SGarrett D'Amore 	}
10188447a05SGarrett D'Amore 
10288447a05SGarrett D'Amore 	if (audio_dev_unregister(adev) != DDI_SUCCESS) {
10388447a05SGarrett D'Amore 		return (DDI_FAILURE);
10488447a05SGarrett D'Amore 	}
10588447a05SGarrett D'Amore 
10688447a05SGarrett D'Amore 	audio_dev_free(adev);
10788447a05SGarrett D'Amore 
10888447a05SGarrett D'Amore 	return (DDI_SUCCESS);
10988447a05SGarrett D'Amore }
11088447a05SGarrett D'Amore 
11188447a05SGarrett D'Amore static int
audio_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resp)11288447a05SGarrett D'Amore audio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
11388447a05SGarrett D'Amore {
11488447a05SGarrett D'Amore 	dip = NULL;
11588447a05SGarrett D'Amore 
11688447a05SGarrett D'Amore 	if (getminor((dev_t)arg) & AUDIO_MN_CLONE_MASK) {
11788447a05SGarrett D'Amore 		audio_client_t *c;
11888447a05SGarrett D'Amore 		c = auclnt_hold_by_devt((dev_t)arg);
11988447a05SGarrett D'Amore 		if (c != NULL) {
12088447a05SGarrett D'Amore 			dip = c->c_dev->d_dip;
12188447a05SGarrett D'Amore 			auclnt_release(c);
12288447a05SGarrett D'Amore 		}
12388447a05SGarrett D'Amore 	} else {
12488447a05SGarrett D'Amore 		audio_dev_t	*adev;
12588447a05SGarrett D'Amore 		if ((adev = auimpl_dev_hold_by_devt((dev_t)arg)) != NULL) {
12688447a05SGarrett D'Amore 			dip = adev->d_dip;
12788447a05SGarrett D'Amore 			auimpl_dev_release(adev);
12888447a05SGarrett D'Amore 		}
12988447a05SGarrett D'Amore 	}
13088447a05SGarrett D'Amore 
13188447a05SGarrett D'Amore 	if (dip == NULL) {
13288447a05SGarrett D'Amore 		return (DDI_FAILURE);
13388447a05SGarrett D'Amore 	}
13488447a05SGarrett D'Amore 
13588447a05SGarrett D'Amore 	switch (cmd) {
13688447a05SGarrett D'Amore 	case DDI_INFO_DEVT2DEVINFO:
13788447a05SGarrett D'Amore 		*resp = dip;
13888447a05SGarrett D'Amore 		break;
13988447a05SGarrett D'Amore 	case DDI_INFO_DEVT2INSTANCE:
14088447a05SGarrett D'Amore 		*resp = (void *)(uintptr_t)ddi_get_instance(dip);
14188447a05SGarrett D'Amore 		break;
14288447a05SGarrett D'Amore 	default:
14388447a05SGarrett D'Amore 		*resp = NULL;
14488447a05SGarrett D'Amore 		return (DDI_FAILURE);
14588447a05SGarrett D'Amore 	}
14688447a05SGarrett D'Amore 	return (DDI_SUCCESS);
14788447a05SGarrett D'Amore }
14888447a05SGarrett D'Amore 
14988447a05SGarrett D'Amore static int
audio_open(dev_t * devp,int oflag,int otyp,cred_t * credp)15088447a05SGarrett D'Amore audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp)
15188447a05SGarrett D'Amore {
15288447a05SGarrett D'Amore 	int			rv;
15388447a05SGarrett D'Amore 	audio_client_t		*c;
15488447a05SGarrett D'Amore 
15588447a05SGarrett D'Amore 	if (otyp == OTYP_BLK) {
15688447a05SGarrett D'Amore 		return (ENXIO);
15788447a05SGarrett D'Amore 	}
15888447a05SGarrett D'Amore 
15988447a05SGarrett D'Amore 	if ((c = auimpl_client_create(*devp)) == NULL) {
16088447a05SGarrett D'Amore 		audio_dev_warn(NULL, "client create failed");
16188447a05SGarrett D'Amore 		return (ENXIO);
16288447a05SGarrett D'Amore 	}
16388447a05SGarrett D'Amore 
16488447a05SGarrett D'Amore 	c->c_omode = oflag;
16588447a05SGarrett D'Amore 	c->c_pid = ddi_get_pid();
16688447a05SGarrett D'Amore 	c->c_cred = credp;
16788447a05SGarrett D'Amore 
16888447a05SGarrett D'Amore 	/*
16988447a05SGarrett D'Amore 	 * Call client/personality specific open handler.  Note that
17088447a05SGarrett D'Amore 	 * we "insist" that there is an open.  The personality layer
17188447a05SGarrett D'Amore 	 * will initialize/allocate any engines required.
17288447a05SGarrett D'Amore 	 *
17388447a05SGarrett D'Amore 	 * Hmm... do we need to pass in the cred?
17488447a05SGarrett D'Amore 	 */
17588447a05SGarrett D'Amore 	if ((rv = c->c_open(c, oflag)) != 0) {
17688447a05SGarrett D'Amore 		audio_dev_warn(c->c_dev, "open failed (rv %d)", rv);
17788447a05SGarrett D'Amore 		auimpl_client_destroy(c);
17888447a05SGarrett D'Amore 		return (rv);
17988447a05SGarrett D'Amore 	}
18088447a05SGarrett D'Amore 
18188447a05SGarrett D'Amore 	/* we do device cloning! */
18288447a05SGarrett D'Amore 	*devp = makedevice(c->c_major, c->c_minor);
18388447a05SGarrett D'Amore 
18453a539a7SGarrett D'Amore 	/* now we can receive upcalls */
18553a539a7SGarrett D'Amore 	auimpl_client_activate(c);
18688447a05SGarrett D'Amore 
187682cb104SGarrett D'Amore 	atomic_inc_uint(&c->c_dev->d_serial);
18888447a05SGarrett D'Amore 
18988447a05SGarrett D'Amore 	return (0);
19088447a05SGarrett D'Amore }
19188447a05SGarrett D'Amore 
192a19bb1faSGarrett D'Amore static int
audio_stropen(queue_t * rq,dev_t * devp,int oflag,int sflag,cred_t * credp)193a19bb1faSGarrett D'Amore audio_stropen(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
194a19bb1faSGarrett D'Amore {
195a19bb1faSGarrett D'Amore 	int			rv;
196a19bb1faSGarrett D'Amore 	audio_client_t		*c;
197a19bb1faSGarrett D'Amore 
198a19bb1faSGarrett D'Amore 	if (sflag != 0) {
199a19bb1faSGarrett D'Amore 		/* no direct clone or module opens */
200a19bb1faSGarrett D'Amore 		return (ENXIO);
201a19bb1faSGarrett D'Amore 	}
202a19bb1faSGarrett D'Amore 
203a19bb1faSGarrett D'Amore 	/*
204a19bb1faSGarrett D'Amore 	 * Make sure its a STREAMS personality - only legacy Sun API uses
205a19bb1faSGarrett D'Amore 	 * STREAMS.
206a19bb1faSGarrett D'Amore 	 */
207a19bb1faSGarrett D'Amore 	switch (AUDIO_MN_TYPE_MASK & getminor(*devp)) {
208a19bb1faSGarrett D'Amore 	case AUDIO_MINOR_DEVAUDIO:
209a19bb1faSGarrett D'Amore 	case AUDIO_MINOR_DEVAUDIOCTL:
210a19bb1faSGarrett D'Amore 		break;
211a19bb1faSGarrett D'Amore 	default:
212a19bb1faSGarrett D'Amore 		return (ENOSTR);
213a19bb1faSGarrett D'Amore 	}
214a19bb1faSGarrett D'Amore 
215a19bb1faSGarrett D'Amore 	if ((c = auimpl_client_create(*devp)) == NULL) {
216a19bb1faSGarrett D'Amore 		audio_dev_warn(NULL, "client create failed");
217a19bb1faSGarrett D'Amore 		return (ENXIO);
218a19bb1faSGarrett D'Amore 	}
219a19bb1faSGarrett D'Amore 
220a19bb1faSGarrett D'Amore 	rq->q_ptr = WR(rq)->q_ptr = c;
221a19bb1faSGarrett D'Amore 	c->c_omode = oflag;
222a19bb1faSGarrett D'Amore 	c->c_pid = ddi_get_pid();
223a19bb1faSGarrett D'Amore 	c->c_cred = credp;
224a19bb1faSGarrett D'Amore 	c->c_rq = rq;
225a19bb1faSGarrett D'Amore 	c->c_wq = WR(rq);
226a19bb1faSGarrett D'Amore 
227a19bb1faSGarrett D'Amore 	/*
228a19bb1faSGarrett D'Amore 	 * Call client/personality specific open handler.  Note that
229a19bb1faSGarrett D'Amore 	 * we "insist" that there is an open.  The personality layer
230a19bb1faSGarrett D'Amore 	 * will initialize/allocate any engines required.
231a19bb1faSGarrett D'Amore 	 *
232a19bb1faSGarrett D'Amore 	 * Hmm... do we need to pass in the cred?
233a19bb1faSGarrett D'Amore 	 */
234a19bb1faSGarrett D'Amore 	if ((rv = c->c_open(c, oflag)) != 0) {
235a19bb1faSGarrett D'Amore 		audio_dev_warn(c->c_dev, "open failed (rv %d)", rv);
236a19bb1faSGarrett D'Amore 		auimpl_client_destroy(c);
237a19bb1faSGarrett D'Amore 		return (rv);
238a19bb1faSGarrett D'Amore 	}
239a19bb1faSGarrett D'Amore 
240a19bb1faSGarrett D'Amore 	/* we do device cloning! */
241a19bb1faSGarrett D'Amore 	*devp = makedevice(c->c_major, c->c_minor);
242a19bb1faSGarrett D'Amore 
24353a539a7SGarrett D'Amore 	qprocson(rq);
244a19bb1faSGarrett D'Amore 
24553a539a7SGarrett D'Amore 	/* now we can receive upcalls */
24653a539a7SGarrett D'Amore 	auimpl_client_activate(c);
247a19bb1faSGarrett D'Amore 
248682cb104SGarrett D'Amore 	atomic_inc_uint(&c->c_dev->d_serial);
249a19bb1faSGarrett D'Amore 
250a19bb1faSGarrett D'Amore 	return (0);
251a19bb1faSGarrett D'Amore }
252a19bb1faSGarrett D'Amore 
253a19bb1faSGarrett D'Amore static int
audio_strclose(queue_t * rq,int flag,cred_t * credp)254a19bb1faSGarrett D'Amore audio_strclose(queue_t *rq, int flag, cred_t *credp)
255a19bb1faSGarrett D'Amore {
256a19bb1faSGarrett D'Amore 	audio_client_t	*c;
257a19bb1faSGarrett D'Amore 	audio_dev_t	*d;
258a19bb1faSGarrett D'Amore 	int		rv;
259a19bb1faSGarrett D'Amore 
260a19bb1faSGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
261a19bb1faSGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
262a19bb1faSGarrett D'Amore 
263a19bb1faSGarrett D'Amore 	if ((c = rq->q_ptr) == NULL) {
264a19bb1faSGarrett D'Amore 		return (ENXIO);
265a19bb1faSGarrett D'Amore 	}
266a19bb1faSGarrett D'Amore 	if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
267a19bb1faSGarrett D'Amore 		rv = auclnt_drain(c);
268a19bb1faSGarrett D'Amore 	}
269a19bb1faSGarrett D'Amore 
27053a539a7SGarrett D'Amore 	/* make sure we won't get any upcalls */
27153a539a7SGarrett D'Amore 	auimpl_client_deactivate(c);
272a19bb1faSGarrett D'Amore 
273a19bb1faSGarrett D'Amore 	/*
274a19bb1faSGarrett D'Amore 	 * Pick up any data sitting around in input buffers.  This
275a19bb1faSGarrett D'Amore 	 * avoids leaving record data stuck in queues.
276a19bb1faSGarrett D'Amore 	 */
277a19bb1faSGarrett D'Amore 	if (c->c_istream.s_engine != NULL)
27868c47f65SGarrett D'Amore 		auimpl_input_callback(c->c_istream.s_engine);
279a19bb1faSGarrett D'Amore 
280a19bb1faSGarrett D'Amore 	/* get a local hold on the device */
281a19bb1faSGarrett D'Amore 	d = c->c_dev;
282a19bb1faSGarrett D'Amore 	auimpl_dev_hold(c->c_dev);
283a19bb1faSGarrett D'Amore 
284a19bb1faSGarrett D'Amore 	/* Turn off queue processing... */
285a19bb1faSGarrett D'Amore 	qprocsoff(rq);
286a19bb1faSGarrett D'Amore 
287a19bb1faSGarrett D'Amore 	/* Call personality specific close handler */
288a19bb1faSGarrett D'Amore 	c->c_close(c);
289a19bb1faSGarrett D'Amore 
290a19bb1faSGarrett D'Amore 	auimpl_client_destroy(c);
291a19bb1faSGarrett D'Amore 
292a19bb1faSGarrett D'Amore 	/* notify peers that a change has occurred */
293682cb104SGarrett D'Amore 	atomic_inc_uint(&d->d_serial);
294a19bb1faSGarrett D'Amore 
295a19bb1faSGarrett D'Amore 	/* now we can drop the release we had on the device */
296a19bb1faSGarrett D'Amore 	auimpl_dev_release(d);
297a19bb1faSGarrett D'Amore 
298a19bb1faSGarrett D'Amore 	return (rv);
299a19bb1faSGarrett D'Amore }
300a19bb1faSGarrett D'Amore 
30188447a05SGarrett D'Amore static int
audio_close(dev_t dev,int flag,int otyp,cred_t * credp)30288447a05SGarrett D'Amore audio_close(dev_t dev, int flag, int otyp, cred_t *credp)
30388447a05SGarrett D'Amore {
30488447a05SGarrett D'Amore 	audio_client_t	*c;
30588447a05SGarrett D'Amore 	audio_dev_t	*d;
30688447a05SGarrett D'Amore 
30788447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
30888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
30988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(otyp));
31088447a05SGarrett D'Amore 
31188447a05SGarrett D'Amore 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
312a19bb1faSGarrett D'Amore 		audio_dev_warn(NULL, "close on bogus devt %x,%x",
31388447a05SGarrett D'Amore 		    getmajor(dev), getminor(dev));
31488447a05SGarrett D'Amore 		return (ENXIO);
31588447a05SGarrett D'Amore 	}
31688447a05SGarrett D'Amore 
31753a539a7SGarrett D'Amore 	/* we don't want any upcalls anymore */
31853a539a7SGarrett D'Amore 	auimpl_client_deactivate(c);
31988447a05SGarrett D'Amore 
32088447a05SGarrett D'Amore 	/*
32188447a05SGarrett D'Amore 	 * Pick up any data sitting around in input buffers.  This
32288447a05SGarrett D'Amore 	 * avoids leaving record data stuck in queues.
32388447a05SGarrett D'Amore 	 */
32488447a05SGarrett D'Amore 	if (c->c_istream.s_engine != NULL)
32568c47f65SGarrett D'Amore 		auimpl_input_callback(c->c_istream.s_engine);
32688447a05SGarrett D'Amore 
32788447a05SGarrett D'Amore 	/* get a local hold on the device */
32888447a05SGarrett D'Amore 	d = c->c_dev;
32988447a05SGarrett D'Amore 	auimpl_dev_hold(c->c_dev);
33088447a05SGarrett D'Amore 
33188447a05SGarrett D'Amore 	/*
33288447a05SGarrett D'Amore 	 * NB: This must be done before c->c_close, since it calls
33388447a05SGarrett D'Amore 	 * auclnt_close which will block waiting for the refence count
33488447a05SGarrett D'Amore 	 * to drop to zero.
33588447a05SGarrett D'Amore 	 */
33688447a05SGarrett D'Amore 	auclnt_release(c);
33788447a05SGarrett D'Amore 
33888447a05SGarrett D'Amore 	/* Call personality specific close handler */
33988447a05SGarrett D'Amore 	c->c_close(c);
34088447a05SGarrett D'Amore 
34188447a05SGarrett D'Amore 	auimpl_client_destroy(c);
34288447a05SGarrett D'Amore 
34388447a05SGarrett D'Amore 	/* notify peers that a change has occurred */
344682cb104SGarrett D'Amore 	atomic_inc_uint(&d->d_serial);
34588447a05SGarrett D'Amore 
34688447a05SGarrett D'Amore 	/* now we can drop the release we had on the device */
34788447a05SGarrett D'Amore 	auimpl_dev_release(d);
34888447a05SGarrett D'Amore 
34988447a05SGarrett D'Amore 	return (0);
35088447a05SGarrett D'Amore }
35188447a05SGarrett D'Amore 
35288447a05SGarrett D'Amore static int
audio_write(dev_t dev,struct uio * uio,cred_t * credp)35388447a05SGarrett D'Amore audio_write(dev_t dev, struct uio *uio, cred_t *credp)
35488447a05SGarrett D'Amore {
35588447a05SGarrett D'Amore 	audio_client_t *c;
35688447a05SGarrett D'Amore 	int rv;
35788447a05SGarrett D'Amore 
35888447a05SGarrett D'Amore 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
35988447a05SGarrett D'Amore 		return (ENXIO);
36088447a05SGarrett D'Amore 	}
36168c47f65SGarrett D'Amore 	if ((rv = auclnt_serialize(c)) == 0) {
36268c47f65SGarrett D'Amore 		rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp);
36368c47f65SGarrett D'Amore 		auclnt_unserialize(c);
36468c47f65SGarrett D'Amore 	}
36588447a05SGarrett D'Amore 	auclnt_release(c);
36688447a05SGarrett D'Amore 
36788447a05SGarrett D'Amore 	return (rv);
36888447a05SGarrett D'Amore }
36988447a05SGarrett D'Amore 
37088447a05SGarrett D'Amore static int
audio_read(dev_t dev,struct uio * uio,cred_t * credp)37188447a05SGarrett D'Amore audio_read(dev_t dev, struct uio *uio, cred_t *credp)
37288447a05SGarrett D'Amore {
37388447a05SGarrett D'Amore 	audio_client_t *c;
37488447a05SGarrett D'Amore 	int rv;
37588447a05SGarrett D'Amore 
37688447a05SGarrett D'Amore 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
37788447a05SGarrett D'Amore 		return (ENXIO);
37888447a05SGarrett D'Amore 	}
37968c47f65SGarrett D'Amore 	if ((rv = auclnt_serialize(c)) == 0) {
38068c47f65SGarrett D'Amore 		rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp);
38168c47f65SGarrett D'Amore 		auclnt_unserialize(c);
38268c47f65SGarrett D'Amore 	}
38388447a05SGarrett D'Amore 	auclnt_release(c);
38488447a05SGarrett D'Amore 
38588447a05SGarrett D'Amore 	return (rv);
38688447a05SGarrett D'Amore }
38788447a05SGarrett D'Amore 
38888447a05SGarrett D'Amore static int
audio_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)38988447a05SGarrett D'Amore audio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
39088447a05SGarrett D'Amore     int *rvalp)
39188447a05SGarrett D'Amore {
39288447a05SGarrett D'Amore 	audio_client_t *c;
39388447a05SGarrett D'Amore 	int rv;
39488447a05SGarrett D'Amore 
39588447a05SGarrett D'Amore 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
39688447a05SGarrett D'Amore 		return (ENXIO);
39788447a05SGarrett D'Amore 	}
39888447a05SGarrett D'Amore 	rv = (c->c_ioctl == NULL) ? ENXIO : c->c_ioctl(c, cmd, arg, mode,
39988447a05SGarrett D'Amore 	    credp, rvalp);
40088447a05SGarrett D'Amore 	auclnt_release(c);
40188447a05SGarrett D'Amore 
40288447a05SGarrett D'Amore 	return (rv);
40388447a05SGarrett D'Amore }
40488447a05SGarrett D'Amore 
40588447a05SGarrett D'Amore static int
audio_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)40688447a05SGarrett D'Amore audio_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
40788447a05SGarrett D'Amore     struct pollhead **phpp)
40888447a05SGarrett D'Amore {
40988447a05SGarrett D'Amore 	audio_client_t *c;
41088447a05SGarrett D'Amore 	int rv;
41188447a05SGarrett D'Amore 
41288447a05SGarrett D'Amore 	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
41388447a05SGarrett D'Amore 		return (ENXIO);
41488447a05SGarrett D'Amore 	}
41588447a05SGarrett D'Amore 	rv = (c->c_chpoll == NULL) ?
41688447a05SGarrett D'Amore 	    ENXIO :
41788447a05SGarrett D'Amore 	    c->c_chpoll(c, events, anyyet, reventsp, phpp);
41888447a05SGarrett D'Amore 	auclnt_release(c);
41988447a05SGarrett D'Amore 
42088447a05SGarrett D'Amore 	return (rv);
42188447a05SGarrett D'Amore }
42288447a05SGarrett D'Amore 
423a19bb1faSGarrett D'Amore static int
audio_wput(queue_t * wq,mblk_t * mp)424a19bb1faSGarrett D'Amore audio_wput(queue_t *wq, mblk_t *mp)
425a19bb1faSGarrett D'Amore {
426a19bb1faSGarrett D'Amore 	audio_client_t	*c;
427a19bb1faSGarrett D'Amore 
428a19bb1faSGarrett D'Amore 	c = wq->q_ptr;
429a19bb1faSGarrett D'Amore 	if (c->c_wput) {
430a19bb1faSGarrett D'Amore 		c->c_wput(c, mp);
431a19bb1faSGarrett D'Amore 	} else {
432a19bb1faSGarrett D'Amore 		freemsg(mp);
433a19bb1faSGarrett D'Amore 	}
434a19bb1faSGarrett D'Amore 	return (0);
435a19bb1faSGarrett D'Amore }
436a19bb1faSGarrett D'Amore 
437a19bb1faSGarrett D'Amore static int
audio_wsrv(queue_t * wq)438a19bb1faSGarrett D'Amore audio_wsrv(queue_t *wq)
439a19bb1faSGarrett D'Amore {
440a19bb1faSGarrett D'Amore 	audio_client_t	*c;
441a19bb1faSGarrett D'Amore 
442a19bb1faSGarrett D'Amore 	c = wq->q_ptr;
443a19bb1faSGarrett D'Amore 	if (c->c_wsrv) {
444a19bb1faSGarrett D'Amore 		c->c_wsrv(c);
445a19bb1faSGarrett D'Amore 	} else {
446a19bb1faSGarrett D'Amore 		flushq(wq, FLUSHALL);
447a19bb1faSGarrett D'Amore 	}
448a19bb1faSGarrett D'Amore 	return (0);
449a19bb1faSGarrett D'Amore }
45088447a05SGarrett D'Amore 
451682cb104SGarrett D'Amore static int
audio_rsrv(queue_t * rq)452682cb104SGarrett D'Amore audio_rsrv(queue_t *rq)
453682cb104SGarrett D'Amore {
454682cb104SGarrett D'Amore 	audio_client_t	*c;
455682cb104SGarrett D'Amore 
456682cb104SGarrett D'Amore 	c = rq->q_ptr;
457682cb104SGarrett D'Amore 	if (c->c_rsrv) {
458682cb104SGarrett D'Amore 		c->c_rsrv(c);
459682cb104SGarrett D'Amore 	} else {
460682cb104SGarrett D'Amore 		flushq(rq, FLUSHALL);
461682cb104SGarrett D'Amore 	}
462682cb104SGarrett D'Amore 	return (0);
463682cb104SGarrett D'Amore }
464682cb104SGarrett D'Amore 
465682cb104SGarrett D'Amore 
46688447a05SGarrett D'Amore static struct dev_ops audio_dev_ops = {
46788447a05SGarrett D'Amore 	DEVO_REV,		/* rev */
46888447a05SGarrett D'Amore 	0,			/* refcnt */
469a19bb1faSGarrett D'Amore 	NULL,			/* getinfo */
47088447a05SGarrett D'Amore 	nulldev,		/* identify */
47188447a05SGarrett D'Amore 	nulldev,		/* probe */
47288447a05SGarrett D'Amore 	audio_attach,		/* attach */
47388447a05SGarrett D'Amore 	audio_detach,		/* detach */
47488447a05SGarrett D'Amore 	nodev,			/* reset */
475a19bb1faSGarrett D'Amore 	NULL,			/* cb_ops */
47688447a05SGarrett D'Amore 	NULL,			/* bus_ops */
47788447a05SGarrett D'Amore 	NULL,			/* power */
47888447a05SGarrett D'Amore };
47988447a05SGarrett D'Amore 
48088447a05SGarrett D'Amore static struct modldrv modldrv = {
48188447a05SGarrett D'Amore 	&mod_driverops,
48288447a05SGarrett D'Amore 	"Audio Framework",
48388447a05SGarrett D'Amore 	&audio_dev_ops,
48488447a05SGarrett D'Amore };
48588447a05SGarrett D'Amore 
48688447a05SGarrett D'Amore static struct modlinkage modlinkage = {
48788447a05SGarrett D'Amore 	MODREV_1,			/* MODREV_1 indicated by manual */
48888447a05SGarrett D'Amore 	&modldrv,
48988447a05SGarrett D'Amore 	NULL
49088447a05SGarrett D'Amore };
49188447a05SGarrett D'Amore 
49288447a05SGarrett D'Amore struct audio_ops_helper {
493a19bb1faSGarrett D'Amore 	struct cb_ops		cbops;	/* NB: must be first */
494a19bb1faSGarrett D'Amore 	struct streamtab	strtab;
495a19bb1faSGarrett D'Amore 	struct qinit		rqinit;
496a19bb1faSGarrett D'Amore 	struct qinit		wqinit;
497a19bb1faSGarrett D'Amore 	struct module_info	minfo;
498a19bb1faSGarrett D'Amore 	char			name[MODMAXNAMELEN+1];
49988447a05SGarrett D'Amore };
50088447a05SGarrett D'Amore 
50188447a05SGarrett D'Amore void
audio_init_ops(struct dev_ops * devops,const char * name)50288447a05SGarrett D'Amore audio_init_ops(struct dev_ops *devops, const char *name)
50388447a05SGarrett D'Amore {
50488447a05SGarrett D'Amore 	struct audio_ops_helper	*helper;
50588447a05SGarrett D'Amore 
50688447a05SGarrett D'Amore 	helper = kmem_zalloc(sizeof (*helper), KM_SLEEP);
50788447a05SGarrett D'Amore 
508a19bb1faSGarrett D'Amore 	(void) strlcpy(helper->name, name, sizeof (helper->name));
509a19bb1faSGarrett D'Amore 
510*bbf21555SRichard Lowe 	helper->minfo.mi_idnum = 0;	/* only for strlog(8) */
511a19bb1faSGarrett D'Amore 	helper->minfo.mi_idname = helper->name;
512a19bb1faSGarrett D'Amore 	helper->minfo.mi_minpsz = 0;
513682cb104SGarrett D'Amore 	helper->minfo.mi_maxpsz = 8192;
514a19bb1faSGarrett D'Amore 	helper->minfo.mi_hiwat = 65536;
515a19bb1faSGarrett D'Amore 	helper->minfo.mi_lowat = 32768;
516a19bb1faSGarrett D'Amore 
517a19bb1faSGarrett D'Amore 	helper->wqinit.qi_putp = audio_wput;
518a19bb1faSGarrett D'Amore 	helper->wqinit.qi_srvp = audio_wsrv;
519a19bb1faSGarrett D'Amore 	helper->wqinit.qi_qopen = NULL;
520a19bb1faSGarrett D'Amore 	helper->wqinit.qi_qclose = NULL;
521a19bb1faSGarrett D'Amore 	helper->wqinit.qi_qadmin = NULL;
522a19bb1faSGarrett D'Amore 	helper->wqinit.qi_minfo = &helper->minfo;
523a19bb1faSGarrett D'Amore 	helper->wqinit.qi_mstat = NULL;
524a19bb1faSGarrett D'Amore 
525682cb104SGarrett D'Amore 	helper->rqinit.qi_putp = putq;
526682cb104SGarrett D'Amore 	helper->rqinit.qi_srvp = audio_rsrv;
527a19bb1faSGarrett D'Amore 	helper->rqinit.qi_qopen = audio_stropen;
528a19bb1faSGarrett D'Amore 	helper->rqinit.qi_qclose = audio_strclose;
529a19bb1faSGarrett D'Amore 	helper->rqinit.qi_qadmin = NULL;
530a19bb1faSGarrett D'Amore 	helper->rqinit.qi_minfo = &helper->minfo;
531a19bb1faSGarrett D'Amore 	helper->rqinit.qi_mstat = NULL;
532a19bb1faSGarrett D'Amore 
533a19bb1faSGarrett D'Amore 	helper->strtab.st_rdinit = &helper->rqinit;
534a19bb1faSGarrett D'Amore 	helper->strtab.st_wrinit = &helper->wqinit;
535a19bb1faSGarrett D'Amore 	helper->strtab.st_muxrinit = NULL;
536a19bb1faSGarrett D'Amore 	helper->strtab.st_muxwinit = NULL;
537a19bb1faSGarrett D'Amore 
53888447a05SGarrett D'Amore 	helper->cbops.cb_open = audio_open;
53988447a05SGarrett D'Amore 	helper->cbops.cb_close = audio_close;
54088447a05SGarrett D'Amore 	helper->cbops.cb_strategy = nodev;
54188447a05SGarrett D'Amore 	helper->cbops.cb_print = nodev;
54288447a05SGarrett D'Amore 	helper->cbops.cb_dump = nodev;
54388447a05SGarrett D'Amore 	helper->cbops.cb_read = audio_read;
54488447a05SGarrett D'Amore 	helper->cbops.cb_write = audio_write;
54588447a05SGarrett D'Amore 	helper->cbops.cb_ioctl = audio_ioctl;
54688447a05SGarrett D'Amore 	helper->cbops.cb_devmap = nodev;
54788447a05SGarrett D'Amore 	helper->cbops.cb_mmap = nodev;
54888447a05SGarrett D'Amore 	helper->cbops.cb_segmap = nodev;
54988447a05SGarrett D'Amore 	helper->cbops.cb_chpoll = audio_chpoll;
55088447a05SGarrett D'Amore 	helper->cbops.cb_prop_op = ddi_prop_op;
551a19bb1faSGarrett D'Amore 	helper->cbops.cb_str = &helper->strtab;
55288447a05SGarrett D'Amore 	helper->cbops.cb_flag = D_MP | D_64BIT;
55388447a05SGarrett D'Amore 	helper->cbops.cb_rev = CB_REV;
55488447a05SGarrett D'Amore 	helper->cbops.cb_aread = nodev;
55588447a05SGarrett D'Amore 	helper->cbops.cb_awrite = nodev;
55688447a05SGarrett D'Amore 
55788447a05SGarrett D'Amore 	devops->devo_cb_ops = &helper->cbops;
55888447a05SGarrett D'Amore 	devops->devo_getinfo = audio_getinfo;
55988447a05SGarrett D'Amore }
56088447a05SGarrett D'Amore 
56188447a05SGarrett D'Amore void
audio_fini_ops(struct dev_ops * devops)56288447a05SGarrett D'Amore audio_fini_ops(struct dev_ops *devops)
56388447a05SGarrett D'Amore {
56488447a05SGarrett D'Amore 	kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper));
56588447a05SGarrett D'Amore 	devops->devo_cb_ops = NULL;
56688447a05SGarrett D'Amore 	devops->devo_getinfo = NULL;
56788447a05SGarrett D'Amore }
56888447a05SGarrett D'Amore 
56988447a05SGarrett D'Amore void
auimpl_dev_vwarn(audio_dev_t * dev,const char * fmt,va_list va)57088447a05SGarrett D'Amore auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va)
57188447a05SGarrett D'Amore {
57288447a05SGarrett D'Amore 	char	buf[256];
57388447a05SGarrett D'Amore 
57488447a05SGarrett D'Amore 	if (dev != NULL) {
57588447a05SGarrett D'Amore 		(void) snprintf(buf, sizeof (buf), "%s#%d: %s",
57688447a05SGarrett D'Amore 		    ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip),
57788447a05SGarrett D'Amore 		    fmt);
57888447a05SGarrett D'Amore 	} else {
57988447a05SGarrett D'Amore 		(void) snprintf(buf, sizeof (buf), "audio: %s", fmt);
58088447a05SGarrett D'Amore 	}
58188447a05SGarrett D'Amore 
58288447a05SGarrett D'Amore 	vcmn_err(CE_WARN, buf, va);
58388447a05SGarrett D'Amore }
58488447a05SGarrett D'Amore 
58588447a05SGarrett D'Amore 
58688447a05SGarrett D'Amore void
audio_dev_warn(audio_dev_t * dev,const char * fmt,...)58788447a05SGarrett D'Amore audio_dev_warn(audio_dev_t *dev, const char *fmt, ...)
58888447a05SGarrett D'Amore {
58988447a05SGarrett D'Amore 	va_list	va;
59088447a05SGarrett D'Amore 
59188447a05SGarrett D'Amore 	va_start(va, fmt);
59288447a05SGarrett D'Amore 	auimpl_dev_vwarn(dev, fmt, va);
59388447a05SGarrett D'Amore 	va_end(va);
59488447a05SGarrett D'Amore }
59588447a05SGarrett D'Amore 
59688447a05SGarrett D'Amore /*
59788447a05SGarrett D'Amore  * _init, _info, and _fini DDI glue.
59888447a05SGarrett D'Amore  */
59988447a05SGarrett D'Amore int
_init(void)60088447a05SGarrett D'Amore _init(void)
60188447a05SGarrett D'Amore {
60288447a05SGarrett D'Amore 	int	rv;
60388447a05SGarrett D'Amore 
60488447a05SGarrett D'Amore 	auimpl_client_init();
60588447a05SGarrett D'Amore 	auimpl_dev_init();
60688447a05SGarrett D'Amore 	auimpl_sun_init();
60788447a05SGarrett D'Amore 	auimpl_oss_init();
60888447a05SGarrett D'Amore 
609a19bb1faSGarrett D'Amore 	audio_init_ops(&audio_dev_ops, "audio");
610a19bb1faSGarrett D'Amore 
61188447a05SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
612a19bb1faSGarrett D'Amore 		audio_fini_ops(&audio_dev_ops);
61388447a05SGarrett D'Amore 		auimpl_dev_fini();
61488447a05SGarrett D'Amore 		auimpl_client_fini();
61588447a05SGarrett D'Amore 	}
61688447a05SGarrett D'Amore 	return (rv);
61788447a05SGarrett D'Amore }
61888447a05SGarrett D'Amore 
61988447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)62088447a05SGarrett D'Amore _info(struct modinfo *modinfop)
62188447a05SGarrett D'Amore {
62288447a05SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
62388447a05SGarrett D'Amore }
62488447a05SGarrett D'Amore 
62588447a05SGarrett D'Amore int
_fini(void)62688447a05SGarrett D'Amore _fini(void)
62788447a05SGarrett D'Amore {
62888447a05SGarrett D'Amore 	int rv;
62988447a05SGarrett D'Amore 
63088447a05SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) != 0)
63188447a05SGarrett D'Amore 		return (rv);
63288447a05SGarrett D'Amore 
63388447a05SGarrett D'Amore 	auimpl_dev_fini();
63488447a05SGarrett D'Amore 	auimpl_client_fini();
63588447a05SGarrett D'Amore 
63688447a05SGarrett D'Amore 	return (rv);
63788447a05SGarrett D'Amore }
638