Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / audio / impl / audio_ddi.c
bloba672b7f68ada6e7d7bdd9f49e8f464c4a38df42b
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (C) 4Front Technologies 1996-2008.
24 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
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>
40 #include "audio_impl.h"
43 * Audio DDI glue implementation.
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.)
51 static int
52 audio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
54 audio_dev_t *adev;
56 /* pseudo devices don't need S/R support */
57 if ((cmd != DDI_ATTACH) || (dip == NULL)) {
58 return (DDI_FAILURE);
61 if (ddi_get_instance(dip) != 0) {
62 return (DDI_FAILURE);
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);
72 /* look up our properties! */
74 if (audio_dev_register(adev) != 0) {
75 audio_dev_free(adev);
76 return (DDI_FAILURE);
79 ddi_report_dev(dip);
81 return (DDI_SUCCESS);
84 static int
85 audio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
87 audio_dev_t *adev;
89 /* pseudo devices don't need S/R support */
90 if (cmd != DDI_DETACH) {
91 return (DDI_FAILURE);
94 if (dip == NULL) {
95 return (DDI_FAILURE);
98 if ((adev = ddi_get_driver_private(dip)) == NULL) {
99 return (DDI_FAILURE);
102 if (audio_dev_unregister(adev) != DDI_SUCCESS) {
103 return (DDI_FAILURE);
106 audio_dev_free(adev);
108 return (DDI_SUCCESS);
111 static int
112 audio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
114 dip = NULL;
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);
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);
131 if (dip == NULL) {
132 return (DDI_FAILURE);
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);
146 return (DDI_SUCCESS);
149 static int
150 audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp)
152 int rv;
153 audio_client_t *c;
155 if (otyp == OTYP_BLK) {
156 return (ENXIO);
159 if ((c = auimpl_client_create(*devp)) == NULL) {
160 audio_dev_warn(NULL, "client create failed");
161 return (ENXIO);
164 c->c_omode = oflag;
165 c->c_pid = ddi_get_pid();
166 c->c_cred = credp;
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.
173 * Hmm... do we need to pass in the cred?
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);
181 /* we do device cloning! */
182 *devp = makedevice(c->c_major, c->c_minor);
184 /* now we can receive upcalls */
185 auimpl_client_activate(c);
187 atomic_inc_uint(&c->c_dev->d_serial);
189 return (0);
192 static int
193 audio_stropen(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
195 int rv;
196 audio_client_t *c;
198 if (sflag != 0) {
199 /* no direct clone or module opens */
200 return (ENXIO);
204 * Make sure its a STREAMS personality - only legacy Sun API uses
205 * STREAMS.
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);
215 if ((c = auimpl_client_create(*devp)) == NULL) {
216 audio_dev_warn(NULL, "client create failed");
217 return (ENXIO);
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);
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.
232 * Hmm... do we need to pass in the cred?
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);
240 /* we do device cloning! */
241 *devp = makedevice(c->c_major, c->c_minor);
243 qprocson(rq);
245 /* now we can receive upcalls */
246 auimpl_client_activate(c);
248 atomic_inc_uint(&c->c_dev->d_serial);
250 return (0);
253 static int
254 audio_strclose(queue_t *rq, int flag, cred_t *credp)
256 audio_client_t *c;
257 audio_dev_t *d;
258 int rv;
260 _NOTE(ARGUNUSED(flag));
261 _NOTE(ARGUNUSED(credp));
263 if ((c = rq->q_ptr) == NULL) {
264 return (ENXIO);
266 if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
267 rv = auclnt_drain(c);
270 /* make sure we won't get any upcalls */
271 auimpl_client_deactivate(c);
274 * Pick up any data sitting around in input buffers. This
275 * avoids leaving record data stuck in queues.
277 if (c->c_istream.s_engine != NULL)
278 auimpl_input_callback(c->c_istream.s_engine);
280 /* get a local hold on the device */
281 d = c->c_dev;
282 auimpl_dev_hold(c->c_dev);
284 /* Turn off queue processing... */
285 qprocsoff(rq);
287 /* Call personality specific close handler */
288 c->c_close(c);
290 auimpl_client_destroy(c);
292 /* notify peers that a change has occurred */
293 atomic_inc_uint(&d->d_serial);
295 /* now we can drop the release we had on the device */
296 auimpl_dev_release(d);
298 return (rv);
301 static int
302 audio_close(dev_t dev, int flag, int otyp, cred_t *credp)
304 audio_client_t *c;
305 audio_dev_t *d;
307 _NOTE(ARGUNUSED(flag));
308 _NOTE(ARGUNUSED(credp));
309 _NOTE(ARGUNUSED(otyp));
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);
317 /* we don't want any upcalls anymore */
318 auimpl_client_deactivate(c);
321 * Pick up any data sitting around in input buffers. This
322 * avoids leaving record data stuck in queues.
324 if (c->c_istream.s_engine != NULL)
325 auimpl_input_callback(c->c_istream.s_engine);
327 /* get a local hold on the device */
328 d = c->c_dev;
329 auimpl_dev_hold(c->c_dev);
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.
336 auclnt_release(c);
338 /* Call personality specific close handler */
339 c->c_close(c);
341 auimpl_client_destroy(c);
343 /* notify peers that a change has occurred */
344 atomic_inc_uint(&d->d_serial);
346 /* now we can drop the release we had on the device */
347 auimpl_dev_release(d);
349 return (0);
352 static int
353 audio_write(dev_t dev, struct uio *uio, cred_t *credp)
355 audio_client_t *c;
356 int rv;
358 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
359 return (ENXIO);
361 if ((rv = auclnt_serialize(c)) == 0) {
362 rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp);
363 auclnt_unserialize(c);
365 auclnt_release(c);
367 return (rv);
370 static int
371 audio_read(dev_t dev, struct uio *uio, cred_t *credp)
373 audio_client_t *c;
374 int rv;
376 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
377 return (ENXIO);
379 if ((rv = auclnt_serialize(c)) == 0) {
380 rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp);
381 auclnt_unserialize(c);
383 auclnt_release(c);
385 return (rv);
388 static int
389 audio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
390 int *rvalp)
392 audio_client_t *c;
393 int rv;
395 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
396 return (ENXIO);
398 rv = (c->c_ioctl == NULL) ? ENXIO : c->c_ioctl(c, cmd, arg, mode,
399 credp, rvalp);
400 auclnt_release(c);
402 return (rv);
405 static int
406 audio_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
407 struct pollhead **phpp)
409 audio_client_t *c;
410 int rv;
412 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
413 return (ENXIO);
415 rv = (c->c_chpoll == NULL) ?
416 ENXIO :
417 c->c_chpoll(c, events, anyyet, reventsp, phpp);
418 auclnt_release(c);
420 return (rv);
423 static int
424 audio_wput(queue_t *wq, mblk_t *mp)
426 audio_client_t *c;
428 c = wq->q_ptr;
429 if (c->c_wput) {
430 c->c_wput(c, mp);
431 } else {
432 freemsg(mp);
434 return (0);
437 static int
438 audio_wsrv(queue_t *wq)
440 audio_client_t *c;
442 c = wq->q_ptr;
443 if (c->c_wsrv) {
444 c->c_wsrv(c);
445 } else {
446 flushq(wq, FLUSHALL);
448 return (0);
451 static int
452 audio_rsrv(queue_t *rq)
454 audio_client_t *c;
456 c = rq->q_ptr;
457 if (c->c_rsrv) {
458 c->c_rsrv(c);
459 } else {
460 flushq(rq, FLUSHALL);
462 return (0);
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 */
480 static struct modldrv modldrv = {
481 &mod_driverops,
482 "Audio Framework",
483 &audio_dev_ops,
486 static struct modlinkage modlinkage = {
487 MODREV_1, /* MODREV_1 indicated by manual */
488 &modldrv,
489 NULL
492 struct audio_ops_helper {
493 struct cb_ops cbops; /* NB: must be first */
494 struct streamtab strtab;
495 struct qinit rqinit;
496 struct qinit wqinit;
497 struct module_info minfo;
498 char name[MODMAXNAMELEN+1];
501 void
502 audio_init_ops(struct dev_ops *devops, const char *name)
504 struct audio_ops_helper *helper;
506 helper = kmem_zalloc(sizeof (*helper), KM_SLEEP);
508 (void) strlcpy(helper->name, name, sizeof (helper->name));
510 helper->minfo.mi_idnum = 0; /* only for strlog(8) */
511 helper->minfo.mi_idname = helper->name;
512 helper->minfo.mi_minpsz = 0;
513 helper->minfo.mi_maxpsz = 8192;
514 helper->minfo.mi_hiwat = 65536;
515 helper->minfo.mi_lowat = 32768;
517 helper->wqinit.qi_putp = audio_wput;
518 helper->wqinit.qi_srvp = audio_wsrv;
519 helper->wqinit.qi_qopen = NULL;
520 helper->wqinit.qi_qclose = NULL;
521 helper->wqinit.qi_qadmin = NULL;
522 helper->wqinit.qi_minfo = &helper->minfo;
523 helper->wqinit.qi_mstat = NULL;
525 helper->rqinit.qi_putp = putq;
526 helper->rqinit.qi_srvp = audio_rsrv;
527 helper->rqinit.qi_qopen = audio_stropen;
528 helper->rqinit.qi_qclose = audio_strclose;
529 helper->rqinit.qi_qadmin = NULL;
530 helper->rqinit.qi_minfo = &helper->minfo;
531 helper->rqinit.qi_mstat = NULL;
533 helper->strtab.st_rdinit = &helper->rqinit;
534 helper->strtab.st_wrinit = &helper->wqinit;
535 helper->strtab.st_muxrinit = NULL;
536 helper->strtab.st_muxwinit = NULL;
538 helper->cbops.cb_open = audio_open;
539 helper->cbops.cb_close = audio_close;
540 helper->cbops.cb_strategy = nodev;
541 helper->cbops.cb_print = nodev;
542 helper->cbops.cb_dump = nodev;
543 helper->cbops.cb_read = audio_read;
544 helper->cbops.cb_write = audio_write;
545 helper->cbops.cb_ioctl = audio_ioctl;
546 helper->cbops.cb_devmap = nodev;
547 helper->cbops.cb_mmap = nodev;
548 helper->cbops.cb_segmap = nodev;
549 helper->cbops.cb_chpoll = audio_chpoll;
550 helper->cbops.cb_prop_op = ddi_prop_op;
551 helper->cbops.cb_str = &helper->strtab;
552 helper->cbops.cb_flag = D_MP | D_64BIT;
553 helper->cbops.cb_rev = CB_REV;
554 helper->cbops.cb_aread = nodev;
555 helper->cbops.cb_awrite = nodev;
557 devops->devo_cb_ops = &helper->cbops;
558 devops->devo_getinfo = audio_getinfo;
561 void
562 audio_fini_ops(struct dev_ops *devops)
564 kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper));
565 devops->devo_cb_ops = NULL;
566 devops->devo_getinfo = NULL;
569 void
570 auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va)
572 char buf[256];
574 if (dev != NULL) {
575 (void) snprintf(buf, sizeof (buf), "%s#%d: %s",
576 ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip),
577 fmt);
578 } else {
579 (void) snprintf(buf, sizeof (buf), "audio: %s", fmt);
582 vcmn_err(CE_WARN, buf, va);
586 void
587 audio_dev_warn(audio_dev_t *dev, const char *fmt, ...)
589 va_list va;
591 va_start(va, fmt);
592 auimpl_dev_vwarn(dev, fmt, va);
593 va_end(va);
597 * _init, _info, and _fini DDI glue.
600 _init(void)
602 int rv;
604 auimpl_client_init();
605 auimpl_dev_init();
606 auimpl_sun_init();
607 auimpl_oss_init();
609 audio_init_ops(&audio_dev_ops, "audio");
611 if ((rv = mod_install(&modlinkage)) != 0) {
612 audio_fini_ops(&audio_dev_ops);
613 auimpl_dev_fini();
614 auimpl_client_fini();
616 return (rv);
620 _info(struct modinfo *modinfop)
622 return (mod_info(&modlinkage, modinfop));
626 _fini(void)
628 int rv;
630 if ((rv = mod_remove(&modlinkage)) != 0)
631 return (rv);
633 auimpl_dev_fini();
634 auimpl_client_fini();
636 return (rv);