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