audio_ddi.c revision 9484:fbd5ddc28e96
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 */
50static int
51audio_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
83static int
84audio_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
110static int
111audio_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
148static int
149audio_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	mutex_enter(&c->c_lock);
184	c->c_is_open = B_TRUE;
185	mutex_exit(&c->c_lock);
186
187	auclnt_notify_dev(c->c_dev);
188
189	return (0);
190}
191
192static int
193audio_close(dev_t dev, int flag, int otyp, cred_t *credp)
194{
195	audio_client_t	*c;
196	audio_dev_t	*d;
197
198	_NOTE(ARGUNUSED(flag));
199	_NOTE(ARGUNUSED(credp));
200	_NOTE(ARGUNUSED(otyp));
201
202	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
203		audio_dev_warn(NULL, "close on bugs devt %x,%x",
204		    getmajor(dev), getminor(dev));
205		return (ENXIO);
206	}
207
208	mutex_enter(&c->c_lock);
209	c->c_is_open = B_FALSE;
210	mutex_exit(&c->c_lock);
211
212	/*
213	 * Pick up any data sitting around in input buffers.  This
214	 * avoids leaving record data stuck in queues.
215	 */
216	if (c->c_istream.s_engine != NULL)
217		audio_engine_produce(c->c_istream.s_engine);
218
219	/* get a local hold on the device */
220	d = c->c_dev;
221	auimpl_dev_hold(c->c_dev);
222
223	/*
224	 * NB: This must be done before c->c_close, since it calls
225	 * auclnt_close which will block waiting for the refence count
226	 * to drop to zero.
227	 */
228	auclnt_release(c);
229
230	/* Call personality specific close handler */
231	c->c_close(c);
232
233	auimpl_client_destroy(c);
234
235	/* notify peers that a change has occurred */
236	auclnt_notify_dev(d);
237
238	/* now we can drop the release we had on the device */
239	auimpl_dev_release(d);
240
241	return (0);
242}
243
244static int
245audio_write(dev_t dev, struct uio *uio, cred_t *credp)
246{
247	audio_client_t *c;
248	int rv;
249
250	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
251		return (ENXIO);
252	}
253	rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp);
254	auclnt_release(c);
255
256	return (rv);
257}
258
259static int
260audio_read(dev_t dev, struct uio *uio, cred_t *credp)
261{
262	audio_client_t *c;
263	int rv;
264
265	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
266		return (ENXIO);
267	}
268	rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp);
269	auclnt_release(c);
270
271	return (rv);
272}
273
274static int
275audio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
276    int *rvalp)
277{
278	audio_client_t *c;
279	int rv;
280
281	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
282		return (ENXIO);
283	}
284	rv = (c->c_ioctl == NULL) ? ENXIO : c->c_ioctl(c, cmd, arg, mode,
285	    credp, rvalp);
286	auclnt_release(c);
287
288	return (rv);
289}
290
291static int
292audio_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
293    struct pollhead **phpp)
294{
295	audio_client_t *c;
296	int rv;
297
298	if ((c = auclnt_hold_by_devt(dev)) == NULL) {
299		return (ENXIO);
300	}
301	rv = (c->c_chpoll == NULL) ?
302	    ENXIO :
303	    c->c_chpoll(c, events, anyyet, reventsp, phpp);
304	auclnt_release(c);
305
306	return (rv);
307}
308
309struct cb_ops audio_cb_ops = {
310	audio_open,		/* open */
311	audio_close,		/* close */
312	nodev,		/* strategy */
313	nodev,		/* print */
314	nodev,		/* dump */
315	audio_read,		/* read */
316	audio_write,		/* write */
317	audio_ioctl,		/* ioctl */
318	nodev,		/* devmap */
319	nodev,		/* mmap */
320	nodev,		/* segmap */
321	audio_chpoll,	/* chpoll */
322	ddi_prop_op,	/* prop_op */
323	NULL,		/* str */
324	D_MP | D_64BIT,		/* flag */
325	CB_REV, 	/* rev */
326	nodev,		/* aread */
327	nodev,		/* awrite */
328};
329
330static struct dev_ops audio_dev_ops = {
331	DEVO_REV,		/* rev */
332	0,			/* refcnt */
333	audio_getinfo,		/* getinfo */
334	nulldev,		/* identify */
335	nulldev,		/* probe */
336	audio_attach,		/* attach */
337	audio_detach,		/* detach */
338	nodev,			/* reset */
339	&audio_cb_ops,		/* cb_ops */
340	NULL,			/* bus_ops */
341	NULL,			/* power */
342};
343
344static struct modldrv modldrv = {
345	&mod_driverops,
346	"Audio Framework",
347	&audio_dev_ops,
348};
349
350static struct modlinkage modlinkage = {
351	MODREV_1,			/* MODREV_1 indicated by manual */
352	&modldrv,
353	NULL
354};
355
356struct audio_ops_helper {
357	struct cb_ops		cbops;
358};
359
360void
361audio_init_ops(struct dev_ops *devops, const char *name)
362{
363	_NOTE(ARGUNUSED(name));
364
365	struct audio_ops_helper	*helper;
366
367	helper = kmem_zalloc(sizeof (*helper), KM_SLEEP);
368
369	helper->cbops.cb_open = audio_open;
370	helper->cbops.cb_close = audio_close;
371	helper->cbops.cb_strategy = nodev;
372	helper->cbops.cb_print = nodev;
373	helper->cbops.cb_dump = nodev;
374	helper->cbops.cb_read = audio_read;
375	helper->cbops.cb_write = audio_write;
376	helper->cbops.cb_ioctl = audio_ioctl;
377	helper->cbops.cb_devmap = nodev;
378	helper->cbops.cb_mmap = nodev;
379	helper->cbops.cb_segmap = nodev;
380	helper->cbops.cb_chpoll = audio_chpoll;
381	helper->cbops.cb_prop_op = ddi_prop_op;
382	helper->cbops.cb_str = NULL;
383	helper->cbops.cb_flag = D_MP | D_64BIT;
384	helper->cbops.cb_rev = CB_REV;
385	helper->cbops.cb_aread = nodev;
386	helper->cbops.cb_awrite = nodev;
387
388	devops->devo_cb_ops = &helper->cbops;
389	devops->devo_getinfo = audio_getinfo;
390}
391
392void
393audio_fini_ops(struct dev_ops *devops)
394{
395	kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper));
396	devops->devo_cb_ops = NULL;
397	devops->devo_getinfo = NULL;
398}
399
400void
401auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va)
402{
403	char	buf[256];
404
405	if (dev != NULL) {
406		(void) snprintf(buf, sizeof (buf), "%s#%d: %s",
407		    ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip),
408		    fmt);
409	} else {
410		(void) snprintf(buf, sizeof (buf), "audio: %s", fmt);
411	}
412
413	vcmn_err(CE_WARN, buf, va);
414}
415
416
417void
418audio_dev_warn(audio_dev_t *dev, const char *fmt, ...)
419{
420	va_list	va;
421
422	va_start(va, fmt);
423	auimpl_dev_vwarn(dev, fmt, va);
424	va_end(va);
425}
426
427/*
428 * _init, _info, and _fini DDI glue.
429 */
430int
431_init(void)
432{
433	int	rv;
434
435	auimpl_client_init();
436	auimpl_dev_init();
437	auimpl_sun_init();
438	auimpl_oss_init();
439
440	if ((rv = mod_install(&modlinkage)) != 0) {
441		auimpl_dev_fini();
442		auimpl_client_fini();
443	}
444	return (rv);
445}
446
447int
448_info(struct modinfo *modinfop)
449{
450	return (mod_info(&modlinkage, modinfop));
451}
452
453int
454_fini(void)
455{
456	int rv;
457
458	if ((rv = mod_remove(&modlinkage)) != 0)
459		return (rv);
460
461	auimpl_dev_fini();
462	auimpl_client_fini();
463
464	return (rv);
465}
466