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 32 static int status_isopen = 0; 33 static int status_init(char *buf, int size); 34 static int status_read(struct uio *buf); 35 36 MODULE_VERSION(snd_pcm, PCM_MODVER); 37 38 static d_open_t sndopen; 39 static d_close_t sndclose; 40 static d_ioctl_t sndioctl; 41 static d_read_t sndread; 42 static d_write_t sndwrite; 43 static d_mmap_t sndmmap; 44 static d_poll_t sndpoll; 45 46 #define CDEV_MAJOR 30 47 static struct cdevsw snd_cdevsw = { 48 /* open */ sndopen, 49 /* close */ sndclose, 50 /* read */ sndread, 51 /* write */ sndwrite, 52 /* ioctl */ sndioctl, 53 /* poll */ sndpoll, 54 /* mmap */ sndmmap, 55 /* strategy */ nostrategy, 56 /* name */ "snd", 57 /* maj */ CDEV_MAJOR, 58 /* dump */ nodump, 59 /* psize */ nopsize, 60 /* flags */ 0, 61 /* bmaj */ -1 62 }; 63 64 /* 65 PROPOSAL: 66 each unit needs: 67 status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices 68 dspW and audio are deprecated. 69 dsp needs min 64 channels, will give it 256 70 71 minor = (unit << 20) + (dev << 16) + channel 72 currently minor = (channel << 16) + (unit << 4) + dev 73 74 nomenclature: 75 /dev/pcmX/dsp.(0..255) 76 /dev/pcmX/dspW 77 /dev/pcmX/audio 78 /dev/pcmX/status 79 /dev/pcmX/mixer 80 [etc.] 81 */ 82 83 #define PCMMINOR(x) (minor(x)) 84 #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) 85 #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) 86 #define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) 87 #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) 88 89 static devclass_t pcm_devclass; 90 91 static snddev_info * 92 gsd(int unit) 93 { 94 return devclass_get_softc(pcm_devclass, unit); 95 } 96 97 int 98 pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo) 99 { 100 int unit = device_get_unit(dev); 101 snddev_info *d = device_get_softc(dev); 102 pcm_channel *ch; 103 104 if (((dir == PCMDIR_PLAY)? d->play : d->rec) == NULL) { 105 device_printf(dev, "bad channel add (%s)\n", 106 (dir == PCMDIR_PLAY)? "play" : "record"); 107 return 1; 108 } 109 ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount] : &d->rec[d->reccount]; 110 *ch = *templ; 111 ch->parent = d; 112 if (chn_init(ch, devinfo, dir)) { 113 device_printf(dev, "chn_init() for %s:%d failed\n", 114 (dir == PCMDIR_PLAY)? "play" : "record", 115 (dir == PCMDIR_PLAY)? d->playcount : d->reccount); 116 return 1; 117 } 118 if (dir == PCMDIR_PLAY) d->playcount++; else d->reccount++; 119 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 120 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 121 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 122 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 123 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 124 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 125 /* XXX SND_DEV_NORESET? */ 126 d->chancount++; 127 return 0; 128 } 129 130 int 131 pcm_setstatus(device_t dev, char *str) 132 { 133 snddev_info *d = device_get_softc(dev); 134 strncpy(d->status, str, SND_STATUSLEN); 135 return 0; 136 } 137 138 u_int32_t 139 pcm_getflags(device_t dev) 140 { 141 snddev_info *d = device_get_softc(dev); 142 return d->flags; 143 } 144 145 void 146 pcm_setflags(device_t dev, u_int32_t val) 147 { 148 snddev_info *d = device_get_softc(dev); 149 d->flags = val; 150 } 151 152 void * 153 pcm_getdevinfo(device_t dev) 154 { 155 snddev_info *d = device_get_softc(dev); 156 return d->devinfo; 157 } 158 159 void 160 pcm_setswap(device_t dev, pcm_swap_t *swap) 161 { 162 snddev_info *d = device_get_softc(dev); 163 d->swap = swap; 164 } 165 /* This is the generic init routine */ 166 int 167 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 168 { 169 int sz, unit = device_get_unit(dev); 170 snddev_info *d = device_get_softc(dev); 171 172 if (!pcm_devclass) { 173 pcm_devclass = device_get_devclass(dev); 174 make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 175 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 176 } 177 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 178 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 179 180 d->dev = dev; 181 d->devinfo = devinfo; 182 d->chancount = d->playcount = d->reccount = 0; 183 sz = (numplay + numrec) * sizeof(pcm_channel *); 184 185 if (sz > 0) { 186 d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 187 if (!d->aplay) goto no; 188 bzero(d->aplay, sz); 189 190 d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 191 if (!d->arec) goto no; 192 bzero(d->arec, sz); 193 194 sz = (numplay + numrec) * sizeof(int); 195 d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); 196 if (!d->ref) goto no; 197 bzero(d->ref, sz); 198 } 199 200 if (numplay > 0) { 201 d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), 202 M_DEVBUF, M_NOWAIT); 203 if (!d->play) goto no; 204 bzero(d->play, numplay * sizeof(pcm_channel)); 205 } else 206 d->play = NULL; 207 208 if (numrec > 0) { 209 d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), 210 M_DEVBUF, M_NOWAIT); 211 if (!d->rec) goto no; 212 bzero(d->rec, numrec * sizeof(pcm_channel)); 213 } else 214 d->rec = NULL; 215 216 if (numplay == 0 || numrec == 0) 217 d->flags |= SD_F_SIMPLEX; 218 219 fkchan_setup(&d->fakechan); 220 chn_init(&d->fakechan, NULL, 0); 221 d->magic = MAGIC(unit); /* debugging... */ 222 d->swap = NULL; 223 224 return 0; 225 no: 226 if (d->aplay) free(d->aplay, M_DEVBUF); 227 if (d->play) free(d->play, M_DEVBUF); 228 if (d->arec) free(d->arec, M_DEVBUF); 229 if (d->rec) free(d->rec, M_DEVBUF); 230 return ENXIO; 231 } 232 233 /* 234 * a small utility function which, given a device number, returns 235 * a pointer to the associated snddev_info struct, and sets the unit 236 * number. 237 */ 238 static snddev_info * 239 get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 240 { 241 int u, d, c; 242 243 u = PCMUNIT(i_dev); 244 d = PCMDEV(i_dev); 245 c = PCMCHAN(i_dev); 246 if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 247 if (unit) *unit = u; 248 if (dev) *dev = d; 249 if (chan) *chan = c; 250 if (u < 0) return NULL; 251 252 switch(d) { 253 case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 254 case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 255 case SND_DEV_DSP: 256 case SND_DEV_DSP16: 257 case SND_DEV_AUDIO: 258 return gsd(u); 259 260 case SND_DEV_SEQ: /* XXX when enabled... */ 261 case SND_DEV_SEQ2: 262 case SND_DEV_MIDIN: 263 case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 264 default: 265 printf("unsupported subdevice %d\n", d); 266 return NULL; 267 } 268 } 269 270 static int 271 sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 272 { 273 int dev, unit, chan; 274 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 275 276 DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 277 unit, dev, flags, mode)); 278 279 switch(dev) { 280 case SND_DEV_STATUS: 281 if (status_isopen) return EBUSY; 282 status_isopen = 1; 283 return 0; 284 285 case SND_DEV_CTL: 286 return d? 0 : ENXIO; 287 288 case SND_DEV_AUDIO: 289 case SND_DEV_DSP: 290 case SND_DEV_DSP16: 291 case SND_DEV_NORESET: 292 return d? dsp_open(d, chan, flags, dev) : ENXIO; 293 294 default: 295 return ENXIO; 296 } 297 } 298 299 static int 300 sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 301 { 302 int dev, unit, chan; 303 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 304 305 DEB(printf("close snd%d subdev %d\n", unit, dev)); 306 307 switch(dev) { /* only those for which close makes sense */ 308 case SND_DEV_STATUS: 309 if (!status_isopen) return EBADF; 310 status_isopen = 0; 311 return 0; 312 313 case SND_DEV_CTL: 314 return d? 0 : ENXIO; 315 316 case SND_DEV_AUDIO: 317 case SND_DEV_DSP: 318 case SND_DEV_DSP16: 319 return d? dsp_close(d, chan, dev) : ENXIO; 320 321 default: 322 return ENXIO; 323 } 324 } 325 326 static int 327 sndread(dev_t i_dev, struct uio *buf, int flag) 328 { 329 int dev, unit, chan; 330 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 331 DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 332 333 switch(dev) { 334 case SND_DEV_STATUS: 335 return status_isopen? status_read(buf) : EBADF; 336 337 case SND_DEV_AUDIO: 338 case SND_DEV_DSP: 339 case SND_DEV_DSP16: 340 return d? dsp_read(d, chan, buf, flag) : EBADF; 341 342 default: 343 return ENXIO; 344 } 345 } 346 347 static int 348 sndwrite(dev_t i_dev, struct uio *buf, int flag) 349 { 350 int dev, unit, chan; 351 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 352 353 DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 354 355 switch(dev) { /* only writeable devices */ 356 case SND_DEV_DSP: 357 case SND_DEV_DSP16: 358 case SND_DEV_AUDIO: 359 return d? dsp_write(d, chan, buf, flag) : EBADF; 360 361 default: 362 return EPERM; /* for non-writeable devices ; */ 363 } 364 } 365 366 static int 367 sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 368 { 369 int dev, chan; 370 snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 371 372 if (d == NULL) return ENXIO; 373 374 switch(dev) { 375 case SND_DEV_CTL: 376 return mixer_ioctl(d, cmd, arg); 377 378 case SND_DEV_AUDIO: 379 case SND_DEV_DSP: 380 case SND_DEV_DSP16: 381 if (IOCGROUP(cmd) == 'M') 382 return mixer_ioctl(d, cmd, arg); 383 else 384 return dsp_ioctl(d, chan, cmd, arg); 385 386 default: 387 return ENXIO; 388 } 389 } 390 391 static int 392 sndpoll(dev_t i_dev, int events, struct proc *p) 393 { 394 int dev, chan; 395 snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 396 397 DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 398 399 if (d == NULL) return ENXIO; 400 401 switch(dev) { 402 case SND_DEV_AUDIO: 403 case SND_DEV_DSP: 404 case SND_DEV_DSP16: 405 return dsp_poll(d, chan, events, p); 406 407 default: 408 return (events & 409 (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 410 } 411 } 412 413 /* 414 * The mmap interface allows access to the play and read buffer, 415 * plus the device descriptor. 416 * The various blocks are accessible at the following offsets: 417 * 418 * 0x00000000 ( 0 ) : write buffer ; 419 * 0x01000000 (16 MB) : read buffer ; 420 * 0x02000000 (32 MB) : device descriptor (dangerous!) 421 * 422 * WARNING: the mmap routines assume memory areas are aligned. This 423 * is true (probably) for the dma buffers, but likely false for the 424 * device descriptor. As a consequence, we do not know where it is 425 * located in the requested area. 426 */ 427 static int 428 sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 429 { 430 int unit, dev, chan; 431 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 432 433 DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 434 d, dev, offset, nprot)); 435 436 if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 437 438 switch(dev) { 439 case SND_DEV_AUDIO: 440 case SND_DEV_DSP: 441 case SND_DEV_DSP16: 442 return dsp_mmap(d, chan, offset, nprot); 443 444 default: 445 return -1; 446 } 447 } 448 449 static int 450 status_init(char *buf, int size) 451 { 452 int i; 453 device_t dev; 454 snddev_info *d; 455 456 snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" 457 "Installed devices:\n", __DATE__, __TIME__); 458 459 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 460 d = gsd(i); 461 if (!d) continue; 462 dev = devclass_get_device(pcm_devclass, i); 463 if (1) { 464 snprintf(buf + strlen(buf), size - strlen(buf), 465 "pcm%d: <%s> %s", 466 i, device_get_desc(dev), d->status); 467 if (d->chancount > 0) 468 snprintf(buf + strlen(buf), size - strlen(buf), 469 " (%dp/%dr channels%s)\n", 470 d->playcount, d->reccount, 471 (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); 472 else 473 snprintf(buf + strlen(buf), size - strlen(buf), 474 " (mixer only)\n"); 475 } 476 } 477 return strlen(buf); 478 } 479 480 static int 481 status_read(struct uio *buf) 482 { 483 static char status_buf[4096]; 484 static int bufptr = 0, buflen = 0; 485 int l; 486 487 if (status_isopen == 1) { 488 status_isopen++; 489 bufptr = 0; 490 buflen = status_init(status_buf, sizeof status_buf); 491 } 492 493 l = min(buf->uio_resid, buflen - bufptr); 494 bufptr += l; 495 return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; 496 } 497