xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_ddi.c (revision 56e2cc86321ec889bf83a888d902c60d6fb2ef8d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (C) 4Front Technologies 1996-2008.
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/sysmacros.h>
30 #include <sys/stropts.h>
31 #include <sys/strsun.h>
32 #include <sys/list.h>
33 #include <sys/mkdev.h>
34 #include <sys/conf.h>
35 #include <sys/note.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 
39 #include "audio_impl.h"
40 
41 /*
42  * Audio DDI glue implementation.
43  */
44 
45 /*
46  * The audio module is itself a pseudo driver, as it contains the
47  * logic to support un-associated nodes.  (Think generic /dev/mixer
48  * and /dev/sndstat used by OSS.)
49  */
50 static int
51 audio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
52 {
53 	audio_dev_t	*adev;
54 
55 	/* pseudo devices don't need S/R support */
56 	if ((cmd != DDI_ATTACH) || (dip == NULL)) {
57 		return (DDI_FAILURE);
58 	}
59 
60 	if (ddi_get_instance(dip) != 0) {
61 		return (DDI_FAILURE);
62 	}
63 
64 	/* this can't fail */
65 	adev = audio_dev_alloc(dip, 0);
66 	adev->d_flags = DEV_SNDSTAT_CAP;
67 	audio_dev_set_description(adev, "Audio Common Code");
68 	audio_dev_set_version(adev, "pseudo");
69 	ddi_set_driver_private(dip, adev);
70 
71 	/* look up our properties! */
72 
73 	if (audio_dev_register(adev) != NULL) {
74 		audio_dev_free(adev);
75 		return (DDI_FAILURE);
76 	}
77 
78 	ddi_report_dev(dip);
79 
80 	return (DDI_SUCCESS);
81 }
82 
83 static int
84 audio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
85 {
86 	audio_dev_t	*adev;
87 
88 	/* pseudo devices don't need S/R support */
89 	if (cmd != DDI_DETACH) {
90 		return (DDI_FAILURE);
91 	}
92 
93 	if (dip == NULL) {
94 		return (DDI_FAILURE);
95 	}
96 
97 	if ((adev = ddi_get_driver_private(dip)) == NULL) {
98 		return (DDI_FAILURE);
99 	}
100 
101 	if (audio_dev_unregister(adev) != DDI_SUCCESS) {
102 		return (DDI_FAILURE);
103 	}
104 
105 	audio_dev_free(adev);
106 
107 	return (DDI_SUCCESS);
108 }
109 
110 static int
111 audio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
112 {
113 	dip = NULL;
114 
115 	if (getminor((dev_t)arg) & AUDIO_MN_CLONE_MASK) {
116 		audio_client_t *c;
117 		c = auclnt_hold_by_devt((dev_t)arg);
118 		if (c != NULL) {
119 			dip = c->c_dev->d_dip;
120 			auclnt_release(c);
121 		}
122 	} else {
123 		audio_dev_t	*adev;
124 		if ((adev = auimpl_dev_hold_by_devt((dev_t)arg)) != NULL) {
125 			dip = adev->d_dip;
126 			auimpl_dev_release(adev);
127 		}
128 	}
129 
130 	if (dip == NULL) {
131 		return (DDI_FAILURE);
132 	}
133 
134 	switch (cmd) {
135 	case DDI_INFO_DEVT2DEVINFO:
136 		*resp = dip;
137 		break;
138 	case DDI_INFO_DEVT2INSTANCE:
139 		*resp = (void *)(uintptr_t)ddi_get_instance(dip);
140 		break;
141 	default:
142 		*resp = NULL;
143 		return (DDI_FAILURE);
144 	}
145 	return (DDI_SUCCESS);
146 }
147 
148 static int
149 audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp)
150 {
151 	int			rv;
152 	audio_client_t		*c;
153 
154 	if (otyp == OTYP_BLK) {
155 		return (ENXIO);
156 	}
157 
158 	if ((c = auimpl_client_create(*devp)) == NULL) {
159 		audio_dev_warn(NULL, "client create failed");
160 		return (ENXIO);
161 	}
162 
163 	c->c_omode = oflag;
164 	c->c_pid = ddi_get_pid();
165 	c->c_cred = credp;
166 
167 	/*
168 	 * Call client/personality specific open handler.  Note that
169 	 * we "insist" that there is an open.  The personality layer
170 	 * will initialize/allocate any engines required.
171 	 *
172 	 * Hmm... do we need to pass in the cred?
173 	 */
174 	if ((rv = c->c_open(c, oflag)) != 0) {
175 		audio_dev_warn(c->c_dev, "open failed (rv %d)", rv);
176 		auimpl_client_destroy(c);
177 		return (rv);
178 	}
179 
180 	/* we do device cloning! */
181 	*devp = makedevice(c->c_major, c->c_minor);
182 
183 	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 
192 static int
193 audio_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 
244 static int
245 audio_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 
259 static int
260 audio_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 
274 static int
275 audio_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 
291 static int
292 audio_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 
309 struct 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 
330 static 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 
344 static struct modldrv modldrv = {
345 	&mod_driverops,
346 	"Audio Framework",
347 	&audio_dev_ops,
348 };
349 
350 static struct modlinkage modlinkage = {
351 	MODREV_1,			/* MODREV_1 indicated by manual */
352 	&modldrv,
353 	NULL
354 };
355 
356 struct audio_ops_helper {
357 	struct cb_ops		cbops;
358 };
359 
360 void
361 audio_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 
392 void
393 audio_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 
400 void
401 auimpl_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 
417 void
418 audio_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  */
430 int
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 
447 int
448 _info(struct modinfo *modinfop)
449 {
450 	return (mod_info(&modlinkage, modinfop));
451 }
452 
453 int
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