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