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