1 /* 2 * Copyright (c) 2001 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <dev/sound/pcm/sound.h> 30 #include <dev/sound/pcm/vchan.h> 31 #include <sys/sbuf.h> 32 33 #include "feeder_if.h" 34 35 static d_open_t sndstat_open; 36 static d_close_t sndstat_close; 37 static d_read_t sndstat_read; 38 39 static struct cdevsw sndstat_cdevsw = { 40 /* open */ sndstat_open, 41 /* close */ sndstat_close, 42 /* read */ sndstat_read, 43 /* write */ nowrite, 44 /* ioctl */ noioctl, 45 /* poll */ nopoll, 46 /* mmap */ nommap, 47 /* strategy */ nostrategy, 48 /* name */ "sndstat", 49 /* maj */ SND_CDEV_MAJOR, 50 /* dump */ nodump, 51 /* psize */ nopsize, 52 /* flags */ 0, 53 }; 54 55 static struct sbuf sndstat_sbuf; 56 static dev_t sndstat_dev = 0; 57 static int sndstat_isopen = 0; 58 static int sndstat_bufptr; 59 60 static int sndstat_verbose = 0; 61 #ifdef USING_MUTEX 62 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose); 63 #else 64 TUNABLE_INT_DECL("hw.snd.verbose", 0, sndstat_verbose); 65 #endif 66 67 static int sndstat_prepare(struct sbuf *s); 68 69 static int 70 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 71 { 72 int error, verbose; 73 74 verbose = sndstat_verbose; 75 error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req); 76 if (error == 0 && req->newptr != NULL) { 77 if (verbose == 0 || verbose == 1) 78 sndstat_verbose = verbose; 79 else 80 error = EINVAL; 81 } 82 return error; 83 } 84 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 85 0, sizeof(int), sysctl_hw_sndverbose, "I", ""); 86 87 static int 88 sndstat_open(dev_t i_dev, int flags, int mode, struct proc *p) 89 { 90 intrmask_t s; 91 int err; 92 93 s = spltty(); 94 if (sndstat_isopen) { 95 splx(s); 96 return EBUSY; 97 } 98 if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) { 99 splx(s); 100 return ENXIO; 101 } 102 sndstat_bufptr = 0; 103 err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM; 104 if (!err) 105 sndstat_isopen = 1; 106 107 splx(s); 108 return err; 109 } 110 111 static int 112 sndstat_close(dev_t i_dev, int flags, int mode, struct proc *p) 113 { 114 intrmask_t s; 115 116 s = spltty(); 117 if (!sndstat_isopen) { 118 splx(s); 119 return EBADF; 120 } 121 sbuf_delete(&sndstat_sbuf); 122 sndstat_isopen = 0; 123 124 splx(s); 125 return 0; 126 } 127 128 static int 129 sndstat_read(dev_t i_dev, struct uio *buf, int flag) 130 { 131 intrmask_t s; 132 int l, err; 133 134 s = spltty(); 135 if (!sndstat_isopen) { 136 splx(s); 137 return EBADF; 138 } 139 l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 140 err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 141 sndstat_bufptr += l; 142 143 splx(s); 144 return err; 145 } 146 147 static int 148 sndstat_prepare(struct sbuf *s) 149 { 150 int i, pc, rc, vc; 151 device_t dev; 152 struct snddev_info *d; 153 struct snddev_channel *sce; 154 struct pcm_channel *c; 155 struct pcm_feeder *f; 156 157 sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\n", __DATE__, __TIME__); 158 if (!pcm_devclass || devclass_get_maxunit(pcm_devclass) == 0) { 159 sbuf_printf(s, "No devices installed.\n"); 160 sbuf_finish(s); 161 return sbuf_len(s); 162 } else 163 sbuf_printf(s, "Installed devices:\n"); 164 165 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 166 d = devclass_get_softc(pcm_devclass, i); 167 if (!d) 168 continue; 169 snd_mtxlock(d->lock); 170 dev = devclass_get_device(pcm_devclass, i); 171 sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); 172 if (!SLIST_EMPTY(&d->channels)) { 173 pc = rc = vc = 0; 174 SLIST_FOREACH(sce, &d->channels, link) { 175 c = sce->channel; 176 if (c->direction == PCMDIR_PLAY) { 177 if (c->flags & CHN_F_VIRTUAL) 178 vc++; 179 else 180 pc++; 181 } else 182 rc++; 183 } 184 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc, 185 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 186 #ifdef USING_DEVFS 187 (i == snd_unit)? " default" : "" 188 #else 189 "" 190 #endif 191 ); 192 if (!sndstat_verbose) 193 goto skipverbose; 194 SLIST_FOREACH(sce, &d->channels, link) { 195 c = sce->channel; 196 sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x", 197 c->parentchannel? c->parentchannel->name : "", 198 c->name, c->speed, c->format, c->flags); 199 if (c->pid != -1) 200 sbuf_printf(s, ", pid %d", c->pid); 201 sbuf_printf(s, "\n\t"); 202 f = c->feeder; 203 while (f) { 204 sbuf_printf(s, "%s", f->class->name); 205 if (f->desc->type == FEEDER_FMT) 206 sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in); 207 if (f->desc->type == FEEDER_RATE) 208 sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC)); 209 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 210 sbuf_printf(s, "(%08x)", f->desc->out); 211 if (f->source) 212 sbuf_printf(s, " <- "); 213 f = f->source; 214 } 215 sbuf_printf(s, "\n"); 216 } 217 skipverbose: 218 } else 219 sbuf_printf(s, " (mixer only)\n"); 220 snd_mtxunlock(d->lock); 221 } 222 sbuf_finish(s); 223 return sbuf_len(s); 224 } 225 226 static int 227 sndstat_init(void) 228 { 229 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat"); 230 231 return (sndstat_dev != 0)? 0 : ENXIO; 232 } 233 234 static int 235 sndstat_uninit(void) 236 { 237 intrmask_t s; 238 239 s = spltty(); 240 if (sndstat_isopen) { 241 splx(s); 242 return EBUSY; 243 } 244 245 if (sndstat_dev) 246 destroy_dev(sndstat_dev); 247 sndstat_dev = 0; 248 249 splx(s); 250 return 0; 251 } 252 253 static void 254 sndstat_sysinit(void *p) 255 { 256 sndstat_init(); 257 } 258 259 static void 260 sndstat_sysuninit(void *p) 261 { 262 sndstat_uninit(); 263 } 264 265 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysinit, NULL); 266 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysuninit, NULL); 267 268 269