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: src/sys/dev/sound/pcm/sndstat.c,v 1.4.2.2 2002/04/22 15:49:36 cg Exp $ 27 * $DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.4 2003/07/21 05:50:36 dillon Exp $ 28 */ 29 30 #include <dev/sound/pcm/sound.h> 31 #include <dev/sound/pcm/vchan.h> 32 33 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.4 2003/07/21 05:50:36 dillon Exp $"); 34 35 #define SS_TYPE_MODULE 0 36 #define SS_TYPE_FIRST 1 37 #define SS_TYPE_PCM 1 38 #define SS_TYPE_MIDI 2 39 #define SS_TYPE_SEQUENCER 3 40 #define SS_TYPE_LAST 3 41 42 static d_open_t sndstat_open; 43 static d_close_t sndstat_close; 44 static d_read_t sndstat_read; 45 46 static struct cdevsw sndstat_cdevsw = { 47 /* name */ "sndstat", 48 /* maj */ SND_CDEV_MAJOR, 49 /* flags */ 0, 50 /* port */ NULL, 51 /* autoq */ 0, 52 53 /* open */ sndstat_open, 54 /* close */ sndstat_close, 55 /* read */ sndstat_read, 56 /* write */ nowrite, 57 /* ioctl */ noioctl, 58 /* poll */ nopoll, 59 /* mmap */ nommap, 60 /* strategy */ nostrategy, 61 /* dump */ nodump, 62 /* psize */ nopsize 63 }; 64 65 struct sndstat_entry { 66 SLIST_ENTRY(sndstat_entry) link; 67 device_t dev; 68 char *str; 69 sndstat_handler handler; 70 int type, unit; 71 }; 72 73 static struct sbuf sndstat_sbuf; 74 static dev_t sndstat_dev = 0; 75 static int sndstat_isopen = 0; 76 static int sndstat_bufptr; 77 static int sndstat_maxunit = -1; 78 static int sndstat_files = 0; 79 80 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none); 81 82 static int sndstat_verbose = 1; 83 #ifdef USING_MUTEX 84 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose); 85 #else 86 TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose); 87 #endif 88 89 static int sndstat_prepare(struct sbuf *s); 90 91 static int 92 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 93 { 94 intrmask_t s; 95 int error, verbose; 96 97 verbose = sndstat_verbose; 98 error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req); 99 if (error == 0 && req->newptr != NULL) { 100 s = spltty(); 101 if (verbose < 0 || verbose > 3) 102 error = EINVAL; 103 else 104 sndstat_verbose = verbose; 105 splx(s); 106 } 107 return error; 108 } 109 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 110 0, sizeof(int), sysctl_hw_sndverbose, "I", ""); 111 112 static int 113 sndstat_open(dev_t i_dev, int flags, int mode, struct thread *td) 114 { 115 intrmask_t s; 116 int err; 117 118 s = spltty(); 119 if (sndstat_isopen) { 120 splx(s); 121 return EBUSY; 122 } 123 if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) { 124 splx(s); 125 return ENXIO; 126 } 127 sndstat_bufptr = 0; 128 err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM; 129 if (!err) 130 sndstat_isopen = 1; 131 132 splx(s); 133 return err; 134 } 135 136 static int 137 sndstat_close(dev_t i_dev, int flags, int mode, struct thread *td) 138 { 139 intrmask_t s; 140 141 s = spltty(); 142 if (!sndstat_isopen) { 143 splx(s); 144 return EBADF; 145 } 146 sbuf_delete(&sndstat_sbuf); 147 sndstat_isopen = 0; 148 149 splx(s); 150 return 0; 151 } 152 153 static int 154 sndstat_read(dev_t i_dev, struct uio *buf, int flag) 155 { 156 intrmask_t s; 157 int l, err; 158 159 s = spltty(); 160 if (!sndstat_isopen) { 161 splx(s); 162 return EBADF; 163 } 164 l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 165 err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 166 sndstat_bufptr += l; 167 168 splx(s); 169 return err; 170 } 171 172 /************************************************************************/ 173 174 static struct sndstat_entry * 175 sndstat_find(int type, int unit) 176 { 177 struct sndstat_entry *ent; 178 179 SLIST_FOREACH(ent, &sndstat_devlist, link) { 180 if (ent->type == type && ent->unit == unit) 181 return ent; 182 } 183 184 return NULL; 185 } 186 187 int 188 sndstat_register(device_t dev, char *str, sndstat_handler handler) 189 { 190 intrmask_t s; 191 struct sndstat_entry *ent; 192 const char *devtype; 193 int type, unit; 194 195 if (dev) { 196 unit = device_get_unit(dev); 197 devtype = device_get_name(dev); 198 if (!strcmp(devtype, "pcm")) 199 type = SS_TYPE_PCM; 200 else if (!strcmp(devtype, "midi")) 201 type = SS_TYPE_MIDI; 202 else if (!strcmp(devtype, "sequencer")) 203 type = SS_TYPE_SEQUENCER; 204 else 205 return EINVAL; 206 } else { 207 type = SS_TYPE_MODULE; 208 unit = -1; 209 } 210 211 ent = malloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK); 212 if (!ent) 213 return ENOSPC; 214 215 ent->dev = dev; 216 ent->str = str; 217 ent->type = type; 218 ent->unit = unit; 219 ent->handler = handler; 220 221 s = spltty(); 222 SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); 223 if (type == SS_TYPE_MODULE) 224 sndstat_files++; 225 sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; 226 splx(s); 227 228 return 0; 229 } 230 231 int 232 sndstat_registerfile(char *str) 233 { 234 return sndstat_register(NULL, str, NULL); 235 } 236 237 int 238 sndstat_unregister(device_t dev) 239 { 240 intrmask_t s; 241 struct sndstat_entry *ent; 242 243 s = spltty(); 244 SLIST_FOREACH(ent, &sndstat_devlist, link) { 245 if (ent->dev == dev) { 246 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 247 free(ent, M_DEVBUF); 248 splx(s); 249 250 return 0; 251 } 252 } 253 splx(s); 254 255 return ENXIO; 256 } 257 258 int 259 sndstat_unregisterfile(char *str) 260 { 261 intrmask_t s; 262 struct sndstat_entry *ent; 263 264 s = spltty(); 265 SLIST_FOREACH(ent, &sndstat_devlist, link) { 266 if (ent->dev == NULL && ent->str == str) { 267 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 268 free(ent, M_DEVBUF); 269 sndstat_files--; 270 splx(s); 271 272 return 0; 273 } 274 } 275 splx(s); 276 277 return ENXIO; 278 } 279 280 /************************************************************************/ 281 282 static int 283 sndstat_prepare(struct sbuf *s) 284 { 285 struct sndstat_entry *ent; 286 int i, j; 287 288 sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n"); 289 if (SLIST_EMPTY(&sndstat_devlist)) { 290 sbuf_printf(s, "No devices installed.\n"); 291 sbuf_finish(s); 292 return sbuf_len(s); 293 } 294 295 sbuf_printf(s, "Installed devices:\n"); 296 297 for (i = 0; i <= sndstat_maxunit; i++) { 298 for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { 299 ent = sndstat_find(j, i); 300 if (!ent) 301 continue; 302 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 303 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 304 sbuf_printf(s, " %s", ent->str); 305 if (ent->handler) 306 ent->handler(s, ent->dev, sndstat_verbose); 307 else 308 sbuf_printf(s, " [no handler]"); 309 sbuf_printf(s, "\n"); 310 } 311 } 312 313 if (sndstat_verbose >= 3 && sndstat_files > 0) { 314 sbuf_printf(s, "\nFile Versions:\n"); 315 316 SLIST_FOREACH(ent, &sndstat_devlist, link) { 317 if (ent->dev == NULL && ent->str != NULL) 318 sbuf_printf(s, "%s\n", ent->str); 319 } 320 } 321 322 sbuf_finish(s); 323 return sbuf_len(s); 324 } 325 326 static int 327 sndstat_init(void) 328 { 329 sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat"); 330 331 return (sndstat_dev != 0)? 0 : ENXIO; 332 } 333 334 static int 335 sndstat_uninit(void) 336 { 337 intrmask_t s; 338 339 s = spltty(); 340 if (sndstat_isopen) { 341 splx(s); 342 return EBUSY; 343 } 344 345 if (sndstat_dev) 346 destroy_dev(sndstat_dev); 347 sndstat_dev = 0; 348 349 splx(s); 350 return 0; 351 } 352 353 int 354 sndstat_busy(void) 355 { 356 return (sndstat_isopen); 357 } 358 359 static void 360 sndstat_sysinit(void *p) 361 { 362 sndstat_init(); 363 } 364 365 static void 366 sndstat_sysuninit(void *p) 367 { 368 sndstat_uninit(); 369 } 370 371 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 372 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 373 374 375