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