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 2010 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/atomic.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39
40 #include "audio_impl.h"
41
42 /*
43 * Audio DDI glue implementation.
44 */
45
46 /*
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.)
50 */
51 static int
52 audio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
53 {
54 audio_dev_t *adev;
55
56 /* pseudo devices don't need S/R support */
57 if ((cmd != DDI_ATTACH) || (dip == NULL)) {
58 return (DDI_FAILURE);
59 }
60
61 if (ddi_get_instance(dip) != 0) {
62 return (DDI_FAILURE);
63 }
64
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);
71
72 /* look up our properties! */
73
74 if (audio_dev_register(adev) != NULL) {
75 audio_dev_free(adev);
76 return (DDI_FAILURE);
77 }
78
79 ddi_report_dev(dip);
80
81 return (DDI_SUCCESS);
82 }
83
84 static int
85 audio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
86 {
87 audio_dev_t *adev;
88
89 /* pseudo devices don't need S/R support */
90 if (cmd != DDI_DETACH) {
91 return (DDI_FAILURE);
92 }
93
94 if (dip == NULL) {
95 return (DDI_FAILURE);
96 }
97
98 if ((adev = ddi_get_driver_private(dip)) == NULL) {
99 return (DDI_FAILURE);
100 }
101
102 if (audio_dev_unregister(adev) != DDI_SUCCESS) {
103 return (DDI_FAILURE);
104 }
105
106 audio_dev_free(adev);
107
108 return (DDI_SUCCESS);
109 }
110
111 static int
112 audio_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
113 {
114 dip = NULL;
115
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);
122 }
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);
128 }
129 }
130
131 if (dip == NULL) {
132 return (DDI_FAILURE);
133 }
134
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);
145 }
146 return (DDI_SUCCESS);
147 }
148
149 static int
150 audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp)
151 {
152 int rv;
153 audio_client_t *c;
154
155 if (otyp == OTYP_BLK) {
156 return (ENXIO);
157 }
158
159 if ((c = auimpl_client_create(*devp)) == NULL) {
160 audio_dev_warn(NULL, "client create failed");
161 return (ENXIO);
162 }
163
164 c->c_omode = oflag;
165 c->c_pid = ddi_get_pid();
166 c->c_cred = credp;
167
168 /*
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.
172 *
173 * Hmm... do we need to pass in the cred?
174 */
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);
179 }
180
181 /* we do device cloning! */
182 *devp = makedevice(c->c_major, c->c_minor);
183
184 /* now we can receive upcalls */
185 auimpl_client_activate(c);
186
187 atomic_inc_uint(&c->c_dev->d_serial);
188
189 return (0);
190 }
191
192 static int
193 audio_stropen(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
194 {
195 int rv;
196 audio_client_t *c;
197
198 if (sflag != 0) {
199 /* no direct clone or module opens */
200 return (ENXIO);
201 }
202
203 /*
204 * Make sure its a STREAMS personality - only legacy Sun API uses
205 * STREAMS.
206 */
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);
213 }
214
215 if ((c = auimpl_client_create(*devp)) == NULL) {
216 audio_dev_warn(NULL, "client create failed");
217 return (ENXIO);
218 }
219
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);
226
227 /*
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.
231 *
232 * Hmm... do we need to pass in the cred?
233 */
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);
238 }
239
240 /* we do device cloning! */
241 *devp = makedevice(c->c_major, c->c_minor);
242
243 qprocson(rq);
244
245 /* now we can receive upcalls */
246 auimpl_client_activate(c);
247
248 atomic_inc_uint(&c->c_dev->d_serial);
249
250 return (0);
251 }
252
253 static int
254 audio_strclose(queue_t *rq, int flag, cred_t *credp)
255 {
256 audio_client_t *c;
257 audio_dev_t *d;
258 int rv;
259
260 _NOTE(ARGUNUSED(flag));
261 _NOTE(ARGUNUSED(credp));
262
263 if ((c = rq->q_ptr) == NULL) {
264 return (ENXIO);
265 }
266 if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
267 rv = auclnt_drain(c);
268 }
269
270 /* make sure we won't get any upcalls */
271 auimpl_client_deactivate(c);
272
273 /*
274 * Pick up any data sitting around in input buffers. This
275 * avoids leaving record data stuck in queues.
276 */
277 if (c->c_istream.s_engine != NULL)
278 auimpl_input_callback(c->c_istream.s_engine);
279
280 /* get a local hold on the device */
281 d = c->c_dev;
282 auimpl_dev_hold(c->c_dev);
283
284 /* Turn off queue processing... */
285 qprocsoff(rq);
286
287 /* Call personality specific close handler */
288 c->c_close(c);
289
290 auimpl_client_destroy(c);
291
292 /* notify peers that a change has occurred */
293 atomic_inc_uint(&d->d_serial);
294
295 /* now we can drop the release we had on the device */
296 auimpl_dev_release(d);
297
298 return (rv);
299 }
300
301 static int
302 audio_close(dev_t dev, int flag, int otyp, cred_t *credp)
303 {
304 audio_client_t *c;
305 audio_dev_t *d;
306
307 _NOTE(ARGUNUSED(flag));
308 _NOTE(ARGUNUSED(credp));
309 _NOTE(ARGUNUSED(otyp));
310
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);
315 }
316
317 /* we don't want any upcalls anymore */
318 auimpl_client_deactivate(c);
319
320 /*
321 * Pick up any data sitting around in input buffers. This
322 * avoids leaving record data stuck in queues.
323 */
324 if (c->c_istream.s_engine != NULL)
325 auimpl_input_callback(c->c_istream.s_engine);
326
327 /* get a local hold on the device */
328 d = c->c_dev;
329 auimpl_dev_hold(c->c_dev);
330
331 /*
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.
335 */
336 auclnt_release(c);
337
338 /* Call personality specific close handler */
339 c->c_close(c);
340
341 auimpl_client_destroy(c);
342
343 /* notify peers that a change has occurred */
344 atomic_inc_uint(&d->d_serial);
345
346 /* now we can drop the release we had on the device */
347 auimpl_dev_release(d);
348
349 return (0);
350 }
351
352 static int
353 audio_write(dev_t dev, struct uio *uio, cred_t *credp)
354 {
355 audio_client_t *c;
356 int rv;
357
358 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
359 return (ENXIO);
360 }
361 if ((rv = auclnt_serialize(c)) == 0) {
362 rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp);
363 auclnt_unserialize(c);
364 }
365 auclnt_release(c);
366
367 return (rv);
368 }
369
370 static int
371 audio_read(dev_t dev, struct uio *uio, cred_t *credp)
372 {
373 audio_client_t *c;
374 int rv;
375
376 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
377 return (ENXIO);
378 }
379 if ((rv = auclnt_serialize(c)) == 0) {
380 rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp);
381 auclnt_unserialize(c);
382 }
383 auclnt_release(c);
384
385 return (rv);
386 }
387
388 static int
389 audio_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
390 int *rvalp)
391 {
392 audio_client_t *c;
393 int rv;
394
395 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
396 return (ENXIO);
397 }
398 rv = (c->c_ioctl == NULL) ? ENXIO : c->c_ioctl(c, cmd, arg, mode,
399 credp, rvalp);
400 auclnt_release(c);
401
402 return (rv);
403 }
404
405 static int
406 audio_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
407 struct pollhead **phpp)
408 {
409 audio_client_t *c;
410 int rv;
411
412 if ((c = auclnt_hold_by_devt(dev)) == NULL) {
413 return (ENXIO);
414 }
415 rv = (c->c_chpoll == NULL) ?
416 ENXIO :
417 c->c_chpoll(c, events, anyyet, reventsp, phpp);
418 auclnt_release(c);
419
420 return (rv);
421 }
422
423 static int
424 audio_wput(queue_t *wq, mblk_t *mp)
425 {
426 audio_client_t *c;
427
428 c = wq->q_ptr;
429 if (c->c_wput) {
430 c->c_wput(c, mp);
431 } else {
432 freemsg(mp);
433 }
434 return (0);
435 }
436
437 static int
438 audio_wsrv(queue_t *wq)
439 {
440 audio_client_t *c;
441
442 c = wq->q_ptr;
443 if (c->c_wsrv) {
444 c->c_wsrv(c);
445 } else {
446 flushq(wq, FLUSHALL);
447 }
448 return (0);
449 }
450
451 static int
452 audio_rsrv(queue_t *rq)
453 {
454 audio_client_t *c;
455
456 c = rq->q_ptr;
457 if (c->c_rsrv) {
458 c->c_rsrv(c);
459 } else {
460 flushq(rq, FLUSHALL);
461 }
462 return (0);
463 }
464
465
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 */
478 };
479
480 static struct modldrv modldrv = {
481 &mod_driverops,
482 "Audio Framework",
483 &audio_dev_ops,
484 };
485
486 static struct modlinkage modlinkage = {
487 MODREV_1, /* MODREV_1 indicated by manual */
488 { &modldrv, NULL }
489 };
490
491 struct audio_ops_helper {
492 struct cb_ops cbops; /* NB: must be first */
493 struct streamtab strtab;
494 struct qinit rqinit;
495 struct qinit wqinit;
496 struct module_info minfo;
497 char name[MODMAXNAMELEN+1];
498 };
499
500 void
501 audio_init_ops(struct dev_ops *devops, const char *name)
502 {
503 struct audio_ops_helper *helper;
504
505 helper = kmem_zalloc(sizeof (*helper), KM_SLEEP);
506
507 (void) strlcpy(helper->name, name, sizeof (helper->name));
508
509 helper->minfo.mi_idnum = 0; /* only for strlog(1M) */
510 helper->minfo.mi_idname = helper->name;
511 helper->minfo.mi_minpsz = 0;
512 helper->minfo.mi_maxpsz = 8192;
513 helper->minfo.mi_hiwat = 65536;
514 helper->minfo.mi_lowat = 32768;
515
516 helper->wqinit.qi_putp = audio_wput;
517 helper->wqinit.qi_srvp = audio_wsrv;
518 helper->wqinit.qi_qopen = NULL;
519 helper->wqinit.qi_qclose = NULL;
520 helper->wqinit.qi_qadmin = NULL;
521 helper->wqinit.qi_minfo = &helper->minfo;
522 helper->wqinit.qi_mstat = NULL;
523
524 helper->rqinit.qi_putp = putq;
525 helper->rqinit.qi_srvp = audio_rsrv;
526 helper->rqinit.qi_qopen = audio_stropen;
527 helper->rqinit.qi_qclose = audio_strclose;
528 helper->rqinit.qi_qadmin = NULL;
529 helper->rqinit.qi_minfo = &helper->minfo;
530 helper->rqinit.qi_mstat = NULL;
531
532 helper->strtab.st_rdinit = &helper->rqinit;
533 helper->strtab.st_wrinit = &helper->wqinit;
534 helper->strtab.st_muxrinit = NULL;
535 helper->strtab.st_muxwinit = NULL;
536
537 helper->cbops.cb_open = audio_open;
538 helper->cbops.cb_close = audio_close;
539 helper->cbops.cb_strategy = nodev;
540 helper->cbops.cb_print = nodev;
541 helper->cbops.cb_dump = nodev;
542 helper->cbops.cb_read = audio_read;
543 helper->cbops.cb_write = audio_write;
544 helper->cbops.cb_ioctl = audio_ioctl;
545 helper->cbops.cb_devmap = nodev;
546 helper->cbops.cb_mmap = nodev;
547 helper->cbops.cb_segmap = nodev;
548 helper->cbops.cb_chpoll = audio_chpoll;
549 helper->cbops.cb_prop_op = ddi_prop_op;
550 helper->cbops.cb_str = &helper->strtab;
551 helper->cbops.cb_flag = D_MP | D_64BIT;
552 helper->cbops.cb_rev = CB_REV;
553 helper->cbops.cb_aread = nodev;
554 helper->cbops.cb_awrite = nodev;
555
556 devops->devo_cb_ops = &helper->cbops;
557 devops->devo_getinfo = audio_getinfo;
558 }
559
560 void
561 audio_fini_ops(struct dev_ops *devops)
562 {
563 kmem_free(devops->devo_cb_ops, sizeof (struct audio_ops_helper));
564 devops->devo_cb_ops = NULL;
565 devops->devo_getinfo = NULL;
566 }
567
568 void
569 auimpl_dev_vwarn(audio_dev_t *dev, const char *fmt, va_list va)
570 {
571 char buf[256];
572
573 if (dev != NULL) {
574 (void) snprintf(buf, sizeof (buf), "%s#%d: %s",
575 ddi_driver_name(dev->d_dip), ddi_get_instance(dev->d_dip),
576 fmt);
577 } else {
578 (void) snprintf(buf, sizeof (buf), "audio: %s", fmt);
579 }
580
581 vcmn_err(CE_WARN, buf, va);
582 }
583
584
585 void
586 audio_dev_warn(audio_dev_t *dev, const char *fmt, ...)
587 {
588 va_list va;
589
590 va_start(va, fmt);
591 auimpl_dev_vwarn(dev, fmt, va);
592 va_end(va);
593 }
594
595 /*
596 * _init, _info, and _fini DDI glue.
597 */
598 int
599 _init(void)
600 {
601 int rv;
602
603 auimpl_client_init();
604 auimpl_dev_init();
605 auimpl_sun_init();
606 auimpl_oss_init();
607
608 audio_init_ops(&audio_dev_ops, "audio");
609
610 if ((rv = mod_install(&modlinkage)) != 0) {
611 audio_fini_ops(&audio_dev_ops);
612 auimpl_dev_fini();
613 auimpl_client_fini();
614 }
615 return (rv);
616 }
617
618 int
619 _info(struct modinfo *modinfop)
620 {
621 return (mod_info(&modlinkage, modinfop));
622 }
623
624 int
625 _fini(void)
626 {
627 int rv;
628
629 if ((rv = mod_remove(&modlinkage)) != 0)
630 return (rv);
631
632 auimpl_dev_fini();
633 auimpl_client_fini();
634
635 return (rv);
636 }