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