1 /* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <dev/sound/pcm/sound.h> 31 #include <dev/sound/pcm/vchan.h> 32 #include <sys/sysctl.h> 33 34 devclass_t pcm_devclass; 35 36 #ifdef USING_DEVFS 37 int snd_unit = 0; 38 TUNABLE_INT("hw.snd.unit", &snd_unit); 39 #endif 40 int snd_autovchans = 0; 41 TUNABLE_INT("hw.snd.autovchans", &snd_autovchans); 42 int snd_maxvchans = 0; 43 TUNABLE_INT("hw.snd.maxvchans", &snd_maxvchans); 44 45 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 46 47 void * 48 snd_mtxcreate(const char *desc) 49 { 50 #ifdef USING_MUTEX 51 struct mtx *m; 52 53 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 54 if (m == NULL) 55 return NULL; 56 mtx_init(m, desc, MTX_RECURSE); 57 return m; 58 #else 59 return (void *)0xcafebabe; 60 #endif 61 } 62 63 void 64 snd_mtxfree(void *m) 65 { 66 #ifdef USING_MUTEX 67 struct mtx *mtx = m; 68 69 mtx_assert(mtx, MA_OWNED); 70 mtx_destroy(mtx); 71 free(mtx, M_DEVBUF); 72 #endif 73 } 74 75 void 76 snd_mtxassert(void *m) 77 { 78 #ifdef USING_MUTEX 79 #ifdef INVARIANTS 80 struct mtx *mtx = m; 81 82 mtx_assert(mtx, MA_OWNED); 83 #endif 84 #endif 85 } 86 87 void 88 snd_mtxlock(void *m) 89 { 90 #ifdef USING_MUTEX 91 struct mtx *mtx = m; 92 93 mtx_lock(mtx); 94 #endif 95 } 96 97 void 98 snd_mtxunlock(void *m) 99 { 100 #ifdef USING_MUTEX 101 struct mtx *mtx = m; 102 103 mtx_unlock(mtx); 104 #endif 105 } 106 107 int 108 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 109 { 110 #ifdef USING_MUTEX 111 flags &= INTR_MPSAFE; 112 flags |= INTR_TYPE_AV; 113 #else 114 flags = INTR_TYPE_AV; 115 #endif 116 return bus_setup_intr(dev, res, flags, hand, param, cookiep); 117 } 118 119 /* return a locked channel */ 120 struct pcm_channel * 121 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid) 122 { 123 struct pcm_channel *c; 124 struct snddev_channel *sce; 125 int err; 126 127 snd_mtxassert(d->lock); 128 129 /* scan for a free channel */ 130 SLIST_FOREACH(sce, &d->channels, link) { 131 c = sce->channel; 132 CHN_LOCK(c); 133 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 134 c->flags |= CHN_F_BUSY; 135 c->pid = pid; 136 return c; 137 } 138 CHN_UNLOCK(c); 139 } 140 141 /* no channel available */ 142 if (direction == PCMDIR_PLAY) { 143 if ((d->vchancount > 0) && (d->vchancount < snd_maxvchans)) { 144 /* try to create a vchan */ 145 SLIST_FOREACH(sce, &d->channels, link) { 146 c = sce->channel; 147 if (!SLIST_EMPTY(&c->children)) { 148 err = vchan_create(c); 149 if (!err) 150 return pcm_chnalloc(d, direction, pid); 151 else 152 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 153 } 154 } 155 } 156 } 157 158 return NULL; 159 } 160 161 /* release a locked channel and unlock it */ 162 int 163 pcm_chnrelease(struct pcm_channel *c) 164 { 165 CHN_LOCKASSERT(c); 166 c->flags &= ~CHN_F_BUSY; 167 c->pid = -1; 168 CHN_UNLOCK(c); 169 return 0; 170 } 171 172 int 173 pcm_chnref(struct pcm_channel *c, int ref) 174 { 175 int r; 176 177 CHN_LOCKASSERT(c); 178 c->refcount += ref; 179 r = c->refcount; 180 return r; 181 } 182 183 #ifdef USING_DEVFS 184 static int 185 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 186 { 187 struct snddev_info *d; 188 int error, unit; 189 190 unit = snd_unit; 191 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 192 if (error == 0 && req->newptr != NULL) { 193 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 194 return EINVAL; 195 d = devclass_get_softc(pcm_devclass, unit); 196 if (d == NULL || SLIST_EMPTY(&d->channels)) 197 return EINVAL; 198 snd_unit = unit; 199 } 200 return (error); 201 } 202 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 203 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 204 #endif 205 206 static int 207 sysctl_hw_snd_autovchans(SYSCTL_HANDLER_ARGS) 208 { 209 int v, error; 210 211 v = snd_autovchans; 212 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 213 if (error == 0 && req->newptr != NULL) { 214 if (v < 0 || v >= SND_MAXVCHANS) 215 return EINVAL; 216 snd_autovchans = v; 217 } 218 return (error); 219 } 220 SYSCTL_PROC(_hw_snd, OID_AUTO, autovchans, CTLTYPE_INT | CTLFLAG_RW, 221 0, sizeof(int), sysctl_hw_snd_autovchans, "I", ""); 222 223 static int 224 sysctl_hw_snd_maxvchans(SYSCTL_HANDLER_ARGS) 225 { 226 int v, error; 227 228 v = snd_maxvchans; 229 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 230 if (error == 0 && req->newptr != NULL) { 231 if (v < 0 || v >= SND_MAXVCHANS) 232 return EINVAL; 233 snd_maxvchans = v; 234 } 235 return (error); 236 } 237 SYSCTL_PROC(_hw_snd, OID_AUTO, maxvchans, CTLTYPE_INT | CTLFLAG_RW, 238 0, sizeof(int), sysctl_hw_snd_maxvchans, "I", ""); 239 240 struct pcm_channel * 241 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 242 { 243 struct pcm_channel *ch; 244 char *dirs; 245 int err; 246 247 switch(dir) { 248 case PCMDIR_PLAY: 249 dirs = "play"; 250 break; 251 case PCMDIR_REC: 252 dirs = "record"; 253 break; 254 case PCMDIR_VIRTUAL: 255 dirs = "virtual"; 256 dir = PCMDIR_PLAY; 257 break; 258 default: 259 return NULL; 260 } 261 262 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 263 if (!ch) 264 return NULL; 265 266 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 267 if (!ch->methods) { 268 free(ch, M_DEVBUF); 269 return NULL; 270 } 271 272 ch->pid = -1; 273 ch->parentsnddev = d; 274 ch->parentchannel = parent; 275 snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 276 277 err = chn_init(ch, devinfo, dir); 278 if (err) { 279 device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 280 kobj_delete(ch->methods, M_DEVBUF); 281 free(ch, M_DEVBUF); 282 return NULL; 283 } 284 285 return ch; 286 } 287 288 int 289 pcm_chn_destroy(struct pcm_channel *ch) 290 { 291 int err; 292 293 err = chn_kill(ch); 294 if (err) { 295 device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 296 return err; 297 } 298 299 kobj_delete(ch->methods, M_DEVBUF); 300 free(ch, M_DEVBUF); 301 302 return 0; 303 } 304 305 int 306 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 307 { 308 struct snddev_channel *sce; 309 int unit = device_get_unit(d->dev); 310 311 snd_mtxlock(d->lock); 312 313 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 314 if (!sce) { 315 snd_mtxunlock(d->lock); 316 return ENOMEM; 317 } 318 319 sce->channel = ch; 320 SLIST_INSERT_HEAD(&d->channels, sce, link); 321 322 if (mkdev) 323 dsp_register(unit, d->devcount++); 324 d->chancount++; 325 if (ch->flags & CHN_F_VIRTUAL) 326 d->vchancount++; 327 328 snd_mtxunlock(d->lock); 329 330 return 0; 331 } 332 333 int 334 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 335 { 336 struct snddev_channel *sce; 337 int unit = device_get_unit(d->dev); 338 339 snd_mtxlock(d->lock); 340 SLIST_FOREACH(sce, &d->channels, link) { 341 if (sce->channel == ch) 342 goto gotit; 343 } 344 snd_mtxunlock(d->lock); 345 return EINVAL; 346 gotit: 347 if (ch->flags & CHN_F_VIRTUAL) 348 d->vchancount--; 349 d->chancount--; 350 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 351 free(sce, M_DEVBUF); 352 353 if (rmdev) 354 dsp_unregister(unit, --d->devcount); 355 snd_mtxunlock(d->lock); 356 357 return 0; 358 } 359 360 int 361 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 362 { 363 struct snddev_info *d = device_get_softc(dev); 364 struct pcm_channel *ch, *child; 365 struct pcmchan_children *pce; 366 int i, err; 367 368 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 369 if (!ch) { 370 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 371 return ENODEV; 372 } 373 374 err = pcm_chn_add(d, ch, 1); 375 if (err) { 376 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 377 pcm_chn_destroy(ch); 378 return err; 379 } 380 381 if ((dir == PCMDIR_PLAY) && (d->flags & SD_F_AUTOVCHAN) && (snd_autovchans > 0)) { 382 ch->flags |= CHN_F_BUSY; 383 for (i = 0; err == 0 && i < snd_autovchans; i++) 384 err = vchan_create(ch); 385 if (err) { 386 device_printf(d->dev, "vchan_create(%d) failed, err=%d\n", i - 1, err); 387 SLIST_FOREACH(pce, &ch->children, link) { 388 child = pce->channel; 389 vchan_destroy(child); 390 } 391 return err; 392 } 393 } 394 395 return err; 396 } 397 398 static int 399 pcm_killchan(device_t dev) 400 { 401 struct snddev_info *d = device_get_softc(dev); 402 struct snddev_channel *sce; 403 404 snd_mtxlock(d->lock); 405 sce = SLIST_FIRST(&d->channels); 406 snd_mtxunlock(d->lock); 407 408 return pcm_chn_remove(d, sce->channel, 1); 409 } 410 411 int 412 pcm_setstatus(device_t dev, char *str) 413 { 414 struct snddev_info *d = device_get_softc(dev); 415 416 snd_mtxlock(d->lock); 417 strncpy(d->status, str, SND_STATUSLEN); 418 snd_mtxunlock(d->lock); 419 return 0; 420 } 421 422 u_int32_t 423 pcm_getflags(device_t dev) 424 { 425 struct snddev_info *d = device_get_softc(dev); 426 427 return d->flags; 428 } 429 430 void 431 pcm_setflags(device_t dev, u_int32_t val) 432 { 433 struct snddev_info *d = device_get_softc(dev); 434 435 d->flags = val; 436 } 437 438 void * 439 pcm_getdevinfo(device_t dev) 440 { 441 struct snddev_info *d = device_get_softc(dev); 442 443 return d->devinfo; 444 } 445 446 /* This is the generic init routine */ 447 int 448 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 449 { 450 struct snddev_info *d = device_get_softc(dev); 451 452 d->lock = snd_mtxcreate(device_get_nameunit(dev)); 453 snd_mtxlock(d->lock); 454 455 d->flags = 0; 456 d->dev = dev; 457 d->devinfo = devinfo; 458 d->devcount = 0; 459 d->chancount = 0; 460 d->vchancount = 0; 461 d->inprog = 0; 462 463 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 464 d->flags |= SD_F_SIMPLEX; 465 466 d->fakechan = fkchan_setup(dev); 467 chn_init(d->fakechan, NULL, 0); 468 469 #ifdef SND_DYNSYSCTL 470 sysctl_ctx_init(&d->sysctl_tree); 471 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 472 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 473 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 474 if (d->sysctl_tree_top == NULL) { 475 sysctl_ctx_free(&d->sysctl_tree); 476 goto no; 477 } 478 #endif 479 if (numplay > 0) 480 vchan_initsys(d); 481 if (numplay == 1) 482 d->flags |= SD_F_AUTOVCHAN; 483 484 snd_mtxunlock(d->lock); 485 return 0; 486 no: 487 snd_mtxfree(d->lock); 488 return ENXIO; 489 } 490 491 int 492 pcm_unregister(device_t dev) 493 { 494 struct snddev_info *d = device_get_softc(dev); 495 struct snddev_channel *sce; 496 497 snd_mtxlock(d->lock); 498 if (d->inprog) { 499 device_printf(dev, "unregister: operation in progress"); 500 snd_mtxunlock(d->lock); 501 return EBUSY; 502 } 503 SLIST_FOREACH(sce, &d->channels, link) { 504 if (sce->channel->refcount > 0) { 505 device_printf(dev, "unregister: channel busy"); 506 snd_mtxunlock(d->lock); 507 return EBUSY; 508 } 509 } 510 if (mixer_uninit(dev)) { 511 device_printf(dev, "unregister: mixer busy"); 512 snd_mtxunlock(d->lock); 513 return EBUSY; 514 } 515 516 #ifdef SND_DYNSYSCTL 517 d->sysctl_tree_top = NULL; 518 sysctl_ctx_free(&d->sysctl_tree); 519 #endif 520 while (!SLIST_EMPTY(&d->channels)) 521 pcm_killchan(dev); 522 523 chn_kill(d->fakechan); 524 fkchan_kill(d->fakechan); 525 526 snd_mtxfree(d->lock); 527 return 0; 528 } 529 530 static moduledata_t sndpcm_mod = { 531 "snd_pcm", 532 NULL, 533 NULL 534 }; 535 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 536 MODULE_VERSION(snd_pcm, PCM_MODVER); 537