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.8 2005/06/10 23:07:01 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.8 2005/06/10 23:07:01 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 /* clone */ NULL, 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 int sndstat_isopen = 0; 75 static int sndstat_bufptr; 76 static int sndstat_maxunit = -1; 77 static int sndstat_files = 0; 78 79 static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none); 80 81 static int sndstat_verbose = 1; 82 #ifdef USING_MUTEX 83 TUNABLE_INT("hw.snd.verbose", &sndstat_verbose); 84 #else 85 TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose); 86 #endif 87 88 static int sndstat_prepare(struct sbuf *s); 89 90 static int 91 sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 92 { 93 int error, verbose; 94 95 verbose = sndstat_verbose; 96 error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req); 97 if (error == 0 && req->newptr != NULL) { 98 crit_enter(); 99 if (verbose < 0 || verbose > 3) 100 error = EINVAL; 101 else 102 sndstat_verbose = verbose; 103 crit_exit(); 104 } 105 return error; 106 } 107 SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 108 0, sizeof(int), sysctl_hw_sndverbose, "I", ""); 109 110 static int 111 sndstat_open(dev_t i_dev, int flags, int mode, struct thread *td) 112 { 113 int err; 114 115 crit_enter(); 116 if (sndstat_isopen) { 117 crit_exit(); 118 return EBUSY; 119 } 120 if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) { 121 crit_exit(); 122 return ENXIO; 123 } 124 sndstat_bufptr = 0; 125 err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM; 126 if (!err) 127 sndstat_isopen = 1; 128 129 crit_exit(); 130 return err; 131 } 132 133 static int 134 sndstat_close(dev_t i_dev, int flags, int mode, struct thread *td) 135 { 136 crit_enter(); 137 if (!sndstat_isopen) { 138 crit_exit(); 139 return EBADF; 140 } 141 sbuf_delete(&sndstat_sbuf); 142 sndstat_isopen = 0; 143 144 crit_exit(); 145 return 0; 146 } 147 148 static int 149 sndstat_read(dev_t i_dev, struct uio *buf, int flag) 150 { 151 int l, err; 152 153 crit_enter(); 154 if (!sndstat_isopen) { 155 crit_exit(); 156 return EBADF; 157 } 158 l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 159 err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 160 sndstat_bufptr += l; 161 162 crit_exit(); 163 return err; 164 } 165 166 /************************************************************************/ 167 168 static struct sndstat_entry * 169 sndstat_find(int type, int unit) 170 { 171 struct sndstat_entry *ent; 172 173 SLIST_FOREACH(ent, &sndstat_devlist, link) { 174 if (ent->type == type && ent->unit == unit) 175 return ent; 176 } 177 178 return NULL; 179 } 180 181 int 182 sndstat_register(device_t dev, char *str, sndstat_handler handler) 183 { 184 struct sndstat_entry *ent; 185 const char *devtype; 186 int type, unit; 187 188 if (dev) { 189 unit = device_get_unit(dev); 190 devtype = device_get_name(dev); 191 if (!strcmp(devtype, "pcm")) 192 type = SS_TYPE_PCM; 193 else if (!strcmp(devtype, "midi")) 194 type = SS_TYPE_MIDI; 195 else if (!strcmp(devtype, "sequencer")) 196 type = SS_TYPE_SEQUENCER; 197 else 198 return EINVAL; 199 } else { 200 type = SS_TYPE_MODULE; 201 unit = -1; 202 } 203 204 ent = malloc(sizeof *ent, M_DEVBUF, M_ZERO | M_WAITOK); 205 if (!ent) 206 return ENOSPC; 207 208 ent->dev = dev; 209 ent->str = str; 210 ent->type = type; 211 ent->unit = unit; 212 ent->handler = handler; 213 214 crit_enter(); 215 SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); 216 if (type == SS_TYPE_MODULE) 217 sndstat_files++; 218 sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; 219 crit_exit(); 220 221 return 0; 222 } 223 224 int 225 sndstat_registerfile(char *str) 226 { 227 return sndstat_register(NULL, str, NULL); 228 } 229 230 int 231 sndstat_unregister(device_t dev) 232 { 233 struct sndstat_entry *ent; 234 235 crit_enter(); 236 SLIST_FOREACH(ent, &sndstat_devlist, link) { 237 if (ent->dev == dev) { 238 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 239 free(ent, M_DEVBUF); 240 crit_exit(); 241 242 return 0; 243 } 244 } 245 crit_exit(); 246 247 return ENXIO; 248 } 249 250 int 251 sndstat_unregisterfile(char *str) 252 { 253 struct sndstat_entry *ent; 254 255 crit_enter(); 256 SLIST_FOREACH(ent, &sndstat_devlist, link) { 257 if (ent->dev == NULL && ent->str == str) { 258 SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 259 free(ent, M_DEVBUF); 260 sndstat_files--; 261 crit_exit(); 262 263 return 0; 264 } 265 } 266 crit_exit(); 267 268 return ENXIO; 269 } 270 271 /************************************************************************/ 272 273 static int 274 sndstat_prepare(struct sbuf *s) 275 { 276 struct sndstat_entry *ent; 277 int i, j; 278 279 sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n"); 280 if (SLIST_EMPTY(&sndstat_devlist)) { 281 sbuf_printf(s, "No devices installed.\n"); 282 sbuf_finish(s); 283 return sbuf_len(s); 284 } 285 286 sbuf_printf(s, "Installed devices:\n"); 287 288 for (i = 0; i <= sndstat_maxunit; i++) { 289 for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { 290 ent = sndstat_find(j, i); 291 if (!ent) 292 continue; 293 sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 294 sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 295 sbuf_printf(s, " %s", ent->str); 296 if (ent->handler) 297 ent->handler(s, ent->dev, sndstat_verbose); 298 else 299 sbuf_printf(s, " [no handler]"); 300 sbuf_printf(s, "\n"); 301 } 302 } 303 304 if (sndstat_verbose >= 3 && sndstat_files > 0) { 305 sbuf_printf(s, "\nFile Versions:\n"); 306 307 SLIST_FOREACH(ent, &sndstat_devlist, link) { 308 if (ent->dev == NULL && ent->str != NULL) 309 sbuf_printf(s, "%s\n", ent->str); 310 } 311 } 312 313 sbuf_finish(s); 314 return sbuf_len(s); 315 } 316 317 static int 318 sndstat_init(void) 319 { 320 cdevsw_add(&sndstat_cdevsw, -1, SND_DEV_STATUS); 321 make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 322 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 323 return (0); 324 } 325 326 static int 327 sndstat_uninit(void) 328 { 329 crit_enter(); 330 if (sndstat_isopen) { 331 crit_exit(); 332 return EBUSY; 333 } 334 cdevsw_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS); 335 crit_exit(); 336 return 0; 337 } 338 339 int 340 sndstat_busy(void) 341 { 342 return (sndstat_isopen); 343 } 344 345 static void 346 sndstat_sysinit(void *p) 347 { 348 sndstat_init(); 349 } 350 351 static void 352 sndstat_sysuninit(void *p) 353 { 354 sndstat_uninit(); 355 } 356 357 SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 358 SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 359 360 361