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