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