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]
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>
33 #include <sys/mkdev.h>
36 #include <sys/atomic.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.)
52 audio_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
56 /* pseudo devices don't need S/R support */
57 if ((cmd
!= DDI_ATTACH
) || (dip
== NULL
)) {
61 if (ddi_get_instance(dip
) != 0) {
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) {
85 audio_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
89 /* pseudo devices don't need S/R support */
90 if (cmd
!= DDI_DETACH
) {
98 if ((adev
= ddi_get_driver_private(dip
)) == NULL
) {
102 if (audio_dev_unregister(adev
) != DDI_SUCCESS
) {
103 return (DDI_FAILURE
);
106 audio_dev_free(adev
);
108 return (DDI_SUCCESS
);
112 audio_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **resp
)
116 if (getminor((dev_t
)arg
) & AUDIO_MN_CLONE_MASK
) {
118 c
= auclnt_hold_by_devt((dev_t
)arg
);
120 dip
= c
->c_dev
->d_dip
;
125 if ((adev
= auimpl_dev_hold_by_devt((dev_t
)arg
)) != NULL
) {
127 auimpl_dev_release(adev
);
132 return (DDI_FAILURE
);
136 case DDI_INFO_DEVT2DEVINFO
:
139 case DDI_INFO_DEVT2INSTANCE
:
140 *resp
= (void *)(uintptr_t)ddi_get_instance(dip
);
144 return (DDI_FAILURE
);
146 return (DDI_SUCCESS
);
150 audio_open(dev_t
*devp
, int oflag
, int otyp
, cred_t
*credp
)
155 if (otyp
== OTYP_BLK
) {
159 if ((c
= auimpl_client_create(*devp
)) == NULL
) {
160 audio_dev_warn(NULL
, "client create failed");
165 c
->c_pid
= ddi_get_pid();
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
);
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
);
193 audio_stropen(queue_t
*rq
, dev_t
*devp
, int oflag
, int sflag
, cred_t
*credp
)
199 /* no direct clone or module opens */
204 * Make sure its a STREAMS personality - only legacy Sun API uses
207 switch (AUDIO_MN_TYPE_MASK
& getminor(*devp
)) {
208 case AUDIO_MINOR_DEVAUDIO
:
209 case AUDIO_MINOR_DEVAUDIOCTL
:
215 if ((c
= auimpl_client_create(*devp
)) == NULL
) {
216 audio_dev_warn(NULL
, "client create failed");
220 rq
->q_ptr
= WR(rq
)->q_ptr
= c
;
222 c
->c_pid
= ddi_get_pid();
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
);
240 /* we do device cloning! */
241 *devp
= makedevice(c
->c_major
, c
->c_minor
);
245 /* now we can receive upcalls */
246 auimpl_client_activate(c
);
248 atomic_inc_uint(&c
->c_dev
->d_serial
);
254 audio_strclose(queue_t
*rq
, int flag
, cred_t
*credp
)
260 _NOTE(ARGUNUSED(flag
));
261 _NOTE(ARGUNUSED(credp
));
263 if ((c
= rq
->q_ptr
) == NULL
) {
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 */
282 auimpl_dev_hold(c
->c_dev
);
284 /* Turn off queue processing... */
287 /* Call personality specific close handler */
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
);
302 audio_close(dev_t dev
, int flag
, int otyp
, cred_t
*credp
)
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
));
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 */
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
338 /* Call personality specific close handler */
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
);
353 audio_write(dev_t dev
, struct uio
*uio
, cred_t
*credp
)
358 if ((c
= auclnt_hold_by_devt(dev
)) == NULL
) {
361 if ((rv
= auclnt_serialize(c
)) == 0) {
362 rv
= (c
->c_write
== NULL
) ? ENXIO
: c
->c_write(c
, uio
, credp
);
363 auclnt_unserialize(c
);
371 audio_read(dev_t dev
, struct uio
*uio
, cred_t
*credp
)
376 if ((c
= auclnt_hold_by_devt(dev
)) == NULL
) {
379 if ((rv
= auclnt_serialize(c
)) == 0) {
380 rv
= (c
->c_read
== NULL
) ? ENXIO
: c
->c_read(c
, uio
, credp
);
381 auclnt_unserialize(c
);
389 audio_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
, cred_t
*credp
,
395 if ((c
= auclnt_hold_by_devt(dev
)) == NULL
) {
398 rv
= (c
->c_ioctl
== NULL
) ? ENXIO
: c
->c_ioctl(c
, cmd
, arg
, mode
,
406 audio_chpoll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
407 struct pollhead
**phpp
)
412 if ((c
= auclnt_hold_by_devt(dev
)) == NULL
) {
415 rv
= (c
->c_chpoll
== NULL
) ?
417 c
->c_chpoll(c
, events
, anyyet
, reventsp
, phpp
);
424 audio_wput(queue_t
*wq
, mblk_t
*mp
)
438 audio_wsrv(queue_t
*wq
)
446 flushq(wq
, FLUSHALL
);
452 audio_rsrv(queue_t
*rq
)
460 flushq(rq
, FLUSHALL
);
466 static struct dev_ops audio_dev_ops
= {
470 nulldev
, /* identify */
472 audio_attach
, /* attach */
473 audio_detach
, /* detach */
480 static struct modldrv modldrv
= {
486 static struct modlinkage modlinkage
= {
487 MODREV_1
, /* MODREV_1 indicated by manual */
492 struct audio_ops_helper
{
493 struct cb_ops cbops
; /* NB: must be first */
494 struct streamtab strtab
;
497 struct module_info minfo
;
498 char name
[MODMAXNAMELEN
+1];
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
;
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
;
570 auimpl_dev_vwarn(audio_dev_t
*dev
, const char *fmt
, va_list va
)
575 (void) snprintf(buf
, sizeof (buf
), "%s#%d: %s",
576 ddi_driver_name(dev
->d_dip
), ddi_get_instance(dev
->d_dip
),
579 (void) snprintf(buf
, sizeof (buf
), "audio: %s", fmt
);
582 vcmn_err(CE_WARN
, buf
, va
);
587 audio_dev_warn(audio_dev_t
*dev
, const char *fmt
, ...)
592 auimpl_dev_vwarn(dev
, fmt
, va
);
597 * _init, _info, and _fini DDI glue.
604 auimpl_client_init();
609 audio_init_ops(&audio_dev_ops
, "audio");
611 if ((rv
= mod_install(&modlinkage
)) != 0) {
612 audio_fini_ops(&audio_dev_ops
);
614 auimpl_client_fini();
620 _info(struct modinfo
*modinfop
)
622 return (mod_info(&modlinkage
, modinfop
));
630 if ((rv
= mod_remove(&modlinkage
)) != 0)
634 auimpl_client_fini();