sndstat.c revision 78395
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: head/sys/dev/sound/pcm/sndstat.c 78395 2001-06-17 23:23:06Z cg $ 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 35static d_open_t sndstat_open; 36static d_close_t sndstat_close; 37static d_read_t sndstat_read; 38 39static 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 55static struct sbuf sndstat_sbuf; 56static dev_t sndstat_dev = 0; 57static int sndstat_isopen = 0; 58static int sndstat_bufptr; 59 60static int sndstat_verbose = 0; 61TUNABLE_INT("hw.snd.verbose", &sndstat_verbose); 62 63static int sndstat_prepare(struct sbuf *s); 64 65static int 66sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 67{ 68 int error, verbose; 69 70 verbose = sndstat_verbose; 71 error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req); 72 if (error == 0 && req->newptr != NULL) { 73 if (verbose == 0 || verbose == 1) 74 sndstat_verbose = verbose; 75 else 76 error = EINVAL; 77 } 78 return error; 79} 80SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 81 0, sizeof(int), sysctl_hw_sndverbose, "I", ""); 82 83static int 84sndstat_open(dev_t i_dev, int flags, int mode, struct proc *p) 85{ 86 int err; 87 88 if (sndstat_isopen) 89 return EBUSY; 90 if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) 91 return ENXIO; 92 sndstat_bufptr = 0; 93 err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM; 94 if (!err) 95 sndstat_isopen = 1; 96 97 return err; 98} 99 100static int 101sndstat_close(dev_t i_dev, int flags, int mode, struct proc *p) 102{ 103 if (!sndstat_isopen) 104 return EBADF; 105 sbuf_delete(&sndstat_sbuf); 106 sndstat_isopen = 0; 107 108 return 0; 109} 110 111static int 112sndstat_read(dev_t i_dev, struct uio *buf, int flag) 113{ 114 int l, err; 115 116 if (!sndstat_isopen) 117 return EBADF; 118 l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 119 err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 120 sndstat_bufptr += l; 121 122 return err; 123} 124 125static int 126sndstat_prepare(struct sbuf *s) 127{ 128 int i, pc, rc, vc; 129 device_t dev; 130 struct snddev_info *d; 131 struct snddev_channel *sce; 132 struct pcm_channel *c; 133 struct pcm_feeder *f; 134 135 sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\n", __DATE__, __TIME__); 136 if (!pcm_devclass || devclass_get_maxunit(pcm_devclass) == 0) { 137 sbuf_printf(s, "No devices installed.\n"); 138 sbuf_finish(s); 139 return sbuf_len(s); 140 } else 141 sbuf_printf(s, "Installed devices:\n"); 142 143 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 144 d = devclass_get_softc(pcm_devclass, i); 145 if (!d) 146 continue; 147 snd_mtxlock(d->lock); 148 dev = devclass_get_device(pcm_devclass, i); 149 sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); 150 if (!SLIST_EMPTY(&d->channels)) { 151 pc = rc = vc = 0; 152 SLIST_FOREACH(sce, &d->channels, link) { 153 c = sce->channel; 154 if (c->direction == PCMDIR_PLAY) { 155 if (c->flags & CHN_F_VIRTUAL) 156 vc++; 157 else 158 pc++; 159 } else 160 rc++; 161 } 162 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc, 163 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 164#ifdef USING_DEVFS 165 (i == snd_unit)? " default" : "" 166#else 167 "" 168#endif 169 ); 170 if (!sndstat_verbose) 171 goto skipverbose; 172 SLIST_FOREACH(sce, &d->channels, link) { 173 c = sce->channel; 174 sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x", 175 c->parentchannel? c->parentchannel->name : "", 176 c->name, c->speed, c->format, c->flags); 177 if (c->pid != -1) 178 sbuf_printf(s, ", pid %d", c->pid); 179 sbuf_printf(s, "\n\t"); 180 f = c->feeder; 181 while (f) { 182 sbuf_printf(s, "%s", f->class->name); 183 if (f->desc->type == FEEDER_FMT) 184 sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in); 185 if (f->desc->type == FEEDER_RATE) 186 sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC)); 187 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 188 sbuf_printf(s, "(%08x)", f->desc->out); 189 if (f->source) 190 sbuf_printf(s, " <- "); 191 f = f->source; 192 } 193 sbuf_printf(s, "\n"); 194 } 195skipverbose: 196 } else 197 sbuf_printf(s, " (mixer only)\n"); 198 snd_mtxunlock(d->lock); 199 } 200 sbuf_finish(s); 201 return sbuf_len(s); 202} 203 204static int 205sndstat_init(void) 206{ 207 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat"); 208 209 return (sndstat_dev != 0)? 0 : ENXIO; 210} 211 212static int 213sndstat_uninit(void) 214{ 215 if (sndstat_isopen) 216 return EBUSY; 217 218 if (sndstat_dev) 219 destroy_dev(sndstat_dev); 220 sndstat_dev = 0; 221 222 return 0; 223} 224 225static void 226sndstat_sysinit(void *p) 227{ 228 sndstat_init(); 229} 230 231static void 232sndstat_sysuninit(void *p) 233{ 234 sndstat_uninit(); 235} 236 237SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysinit, NULL); 238SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_sysuninit, NULL); 239 240 241