1139749Simp/*- 2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 478362Scg * All rights reserved. 578362Scg * 678362Scg * Redistribution and use in source and binary forms, with or without 778362Scg * modification, are permitted provided that the following conditions 878362Scg * are met: 978362Scg * 1. Redistributions of source code must retain the above copyright 1078362Scg * notice, this list of conditions and the following disclaimer. 1178362Scg * 2. Redistributions in binary form must reproduce the above copyright 1278362Scg * notice, this list of conditions and the following disclaimer in the 1378362Scg * documentation and/or other materials provided with the distribution. 1478362Scg * 1578362Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1678362Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1778362Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1878362Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1978362Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2078362Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2178362Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2278362Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2378362Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2478362Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2578362Scg * SUCH DAMAGE. 2678362Scg */ 2778362Scg 28193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 29193640Sariff#include "opt_snd.h" 30193640Sariff#endif 31193640Sariff 3278362Scg#include <dev/sound/pcm/sound.h> 33193640Sariff#include <dev/sound/pcm/pcm.h> 34170815Sariff#include <dev/sound/version.h> 35135033Struckman#include <sys/sx.h> 3678362Scg 3782180ScgSND_DECLARE_FILE("$FreeBSD$"); 3878362Scg 3982180Scg#define SS_TYPE_MODULE 0 4082180Scg#define SS_TYPE_FIRST 1 4182180Scg#define SS_TYPE_PCM 1 4282180Scg#define SS_TYPE_MIDI 2 4382180Scg#define SS_TYPE_SEQUENCER 3 4482180Scg#define SS_TYPE_LAST 3 4582180Scg 4678362Scgstatic d_open_t sndstat_open; 4778362Scgstatic d_close_t sndstat_close; 4878362Scgstatic d_read_t sndstat_read; 4978362Scg 5078362Scgstatic struct cdevsw sndstat_cdevsw = { 51126080Sphk .d_version = D_VERSION, 52111815Sphk .d_open = sndstat_open, 53111815Sphk .d_close = sndstat_close, 54111815Sphk .d_read = sndstat_read, 55111815Sphk .d_name = "sndstat", 56235157Spho .d_flags = D_TRACKCLOSE, 5778362Scg}; 5878362Scg 5982180Scgstruct sndstat_entry { 6082180Scg SLIST_ENTRY(sndstat_entry) link; 6182180Scg device_t dev; 6282180Scg char *str; 6382180Scg sndstat_handler handler; 6482180Scg int type, unit; 6582180Scg}; 6682180Scg 67235157Sphostatic struct sx sndstat_lock; 6878362Scgstatic struct sbuf sndstat_sbuf; 69170161Sariffstatic struct cdev *sndstat_dev = NULL; 70170161Sariffstatic int sndstat_bufptr = -1; 7182180Scgstatic int sndstat_maxunit = -1; 7282180Scgstatic int sndstat_files = 0; 7378362Scg 74170161Sariff#define SNDSTAT_PID(x) ((pid_t)((intptr_t)((x)->si_drv1))) 75170161Sariff#define SNDSTAT_PID_SET(x, y) (x)->si_drv1 = (void *)((intptr_t)(y)) 76170161Sariff#define SNDSTAT_FLUSH() do { \ 77170161Sariff if (sndstat_bufptr != -1) { \ 78170161Sariff sbuf_delete(&sndstat_sbuf); \ 79170161Sariff sndstat_bufptr = -1; \ 80170161Sariff } \ 81193640Sariff} while (0) 82170161Sariff 83201145Santoinestatic SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(sndstat_devlist); 8482180Scg 85202267Smavint snd_verbose = 0; 86164614SariffTUNABLE_INT("hw.snd.verbose", &snd_verbose); 8778362Scg 88170161Sariff#ifdef SND_DEBUG 89170161Sariffstatic int 90170161Sariffsysctl_hw_snd_sndstat_pid(SYSCTL_HANDLER_ARGS) 91170161Sariff{ 92170161Sariff int err, val; 93170161Sariff 94170161Sariff if (sndstat_dev == NULL) 95170161Sariff return (EINVAL); 96170161Sariff 97235157Spho sx_xlock(&sndstat_lock); 98170161Sariff val = (int)SNDSTAT_PID(sndstat_dev); 99170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 100170161Sariff if (err == 0 && req->newptr != NULL && val == 0) { 101170161Sariff SNDSTAT_FLUSH(); 102170161Sariff SNDSTAT_PID_SET(sndstat_dev, 0); 103170161Sariff } 104235157Spho sx_unlock(&sndstat_lock); 105170161Sariff return (err); 106170161Sariff} 107170161SariffSYSCTL_PROC(_hw_snd, OID_AUTO, sndstat_pid, CTLTYPE_INT | CTLFLAG_RW, 108170161Sariff 0, sizeof(int), sysctl_hw_snd_sndstat_pid, "I", "sndstat busy pid"); 109170161Sariff#endif 110170161Sariff 11178362Scgstatic int sndstat_prepare(struct sbuf *s); 11278362Scg 11378362Scgstatic int 11478362Scgsysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) 11578362Scg{ 11678362Scg int error, verbose; 11778362Scg 118164614Sariff verbose = snd_verbose; 119170289Sdwmalone error = sysctl_handle_int(oidp, &verbose, 0, req); 12078362Scg if (error == 0 && req->newptr != NULL) { 121164614Sariff if (verbose < 0 || verbose > 4) 12282180Scg error = EINVAL; 12382180Scg else 124164614Sariff snd_verbose = verbose; 12578362Scg } 12678362Scg return error; 12778362Scg} 12878362ScgSYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 129164614Sariff 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level"); 13078362Scg 13178362Scgstatic int 132130585Sphksndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 13378362Scg{ 134170161Sariff if (sndstat_dev == NULL || i_dev != sndstat_dev) 135170161Sariff return EBADF; 136170161Sariff 137235157Spho sx_xlock(&sndstat_lock); 138170161Sariff if (SNDSTAT_PID(i_dev) != 0) { 139235157Spho sx_unlock(&sndstat_lock); 14078362Scg return EBUSY; 14178670Scg } 142170161Sariff SNDSTAT_PID_SET(i_dev, td->td_proc->p_pid); 143167640Sariff if (sbuf_new(&sndstat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { 144170161Sariff SNDSTAT_PID_SET(i_dev, 0); 145235157Spho sx_unlock(&sndstat_lock); 146170815Sariff return ENXIO; 14778670Scg } 148170815Sariff sndstat_bufptr = 0; 149235157Spho sx_unlock(&sndstat_lock); 150170815Sariff return 0; 15178362Scg} 15278362Scg 15378362Scgstatic int 154130585Sphksndstat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 15578362Scg{ 156170161Sariff if (sndstat_dev == NULL || i_dev != sndstat_dev) 157170161Sariff return EBADF; 158170161Sariff 159235157Spho sx_xlock(&sndstat_lock); 160170161Sariff if (SNDSTAT_PID(i_dev) == 0) { 161235157Spho sx_unlock(&sndstat_lock); 16278362Scg return EBADF; 16378670Scg } 164167640Sariff 165170161Sariff SNDSTAT_FLUSH(); 166170161Sariff SNDSTAT_PID_SET(i_dev, 0); 167167640Sariff 168235157Spho sx_unlock(&sndstat_lock); 16978362Scg 17078362Scg return 0; 17178362Scg} 17278362Scg 17378362Scgstatic int 174130585Sphksndstat_read(struct cdev *i_dev, struct uio *buf, int flag) 17578362Scg{ 17678362Scg int l, err; 17778362Scg 178170161Sariff if (sndstat_dev == NULL || i_dev != sndstat_dev) 179170161Sariff return EBADF; 180170161Sariff 181235157Spho sx_xlock(&sndstat_lock); 182170161Sariff if (SNDSTAT_PID(i_dev) != buf->uio_td->td_proc->p_pid || 183170161Sariff sndstat_bufptr == -1) { 184235157Spho sx_unlock(&sndstat_lock); 18578362Scg return EBADF; 18678670Scg } 187167640Sariff 188170815Sariff if (sndstat_bufptr == 0) { 189170815Sariff err = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; 190170815Sariff if (err) { 191170815Sariff SNDSTAT_FLUSH(); 192235157Spho sx_unlock(&sndstat_lock); 193170815Sariff return err; 194170815Sariff } 195170815Sariff } 196170815Sariff 19778362Scg l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); 19878362Scg err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; 19978362Scg sndstat_bufptr += l; 200235157Spho sx_unlock(&sndstat_lock); 20178362Scg 20278362Scg return err; 20378362Scg} 20478362Scg 20582180Scg/************************************************************************/ 20682180Scg 20782180Scgstatic struct sndstat_entry * 20882180Scgsndstat_find(int type, int unit) 20982180Scg{ 21082180Scg struct sndstat_entry *ent; 21182180Scg 21282180Scg SLIST_FOREACH(ent, &sndstat_devlist, link) { 21382180Scg if (ent->type == type && ent->unit == unit) 21482180Scg return ent; 21582180Scg } 21682180Scg 21782180Scg return NULL; 21882180Scg} 21982180Scg 22082180Scgint 221170161Sariffsndstat_acquire(struct thread *td) 222150827Snetchild{ 223170161Sariff if (sndstat_dev == NULL) 224170161Sariff return EBADF; 225170161Sariff 226235157Spho sx_xlock(&sndstat_lock); 227170161Sariff if (SNDSTAT_PID(sndstat_dev) != 0) { 228235157Spho sx_unlock(&sndstat_lock); 229150827Snetchild return EBUSY; 230150827Snetchild } 231170161Sariff SNDSTAT_PID_SET(sndstat_dev, td->td_proc->p_pid); 232235157Spho sx_unlock(&sndstat_lock); 233150827Snetchild return 0; 234150827Snetchild} 235150827Snetchild 236150827Snetchildint 237170161Sariffsndstat_release(struct thread *td) 238150827Snetchild{ 239170161Sariff if (sndstat_dev == NULL) 240170161Sariff return EBADF; 241170161Sariff 242235157Spho sx_xlock(&sndstat_lock); 243170161Sariff if (SNDSTAT_PID(sndstat_dev) != td->td_proc->p_pid) { 244235157Spho sx_unlock(&sndstat_lock); 245150827Snetchild return EBADF; 246150827Snetchild } 247170161Sariff SNDSTAT_PID_SET(sndstat_dev, 0); 248235157Spho sx_unlock(&sndstat_lock); 249150827Snetchild return 0; 250150827Snetchild} 251150827Snetchild 252150827Snetchildint 25382180Scgsndstat_register(device_t dev, char *str, sndstat_handler handler) 25482180Scg{ 25582180Scg struct sndstat_entry *ent; 25682180Scg const char *devtype; 25782180Scg int type, unit; 25882180Scg 25982180Scg if (dev) { 26082180Scg unit = device_get_unit(dev); 26182180Scg devtype = device_get_name(dev); 26282180Scg if (!strcmp(devtype, "pcm")) 26382180Scg type = SS_TYPE_PCM; 26482180Scg else if (!strcmp(devtype, "midi")) 26582180Scg type = SS_TYPE_MIDI; 26682180Scg else if (!strcmp(devtype, "sequencer")) 26782180Scg type = SS_TYPE_SEQUENCER; 26882180Scg else 26982180Scg return EINVAL; 27082180Scg } else { 27182180Scg type = SS_TYPE_MODULE; 27282180Scg unit = -1; 27382180Scg } 27482180Scg 275167611Sariff ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); 27682180Scg ent->dev = dev; 27782180Scg ent->str = str; 27882180Scg ent->type = type; 27982180Scg ent->unit = unit; 28082180Scg ent->handler = handler; 28182180Scg 282235157Spho sx_xlock(&sndstat_lock); 28382180Scg SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); 28482180Scg if (type == SS_TYPE_MODULE) 28582180Scg sndstat_files++; 28682180Scg sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; 287235157Spho sx_unlock(&sndstat_lock); 28882180Scg 28982180Scg return 0; 29082180Scg} 29182180Scg 29282180Scgint 29382180Scgsndstat_registerfile(char *str) 29482180Scg{ 29582180Scg return sndstat_register(NULL, str, NULL); 29682180Scg} 29782180Scg 29882180Scgint 29982180Scgsndstat_unregister(device_t dev) 30082180Scg{ 30182180Scg struct sndstat_entry *ent; 30282180Scg 303235157Spho sx_xlock(&sndstat_lock); 30482180Scg SLIST_FOREACH(ent, &sndstat_devlist, link) { 30582180Scg if (ent->dev == dev) { 30682180Scg SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 307235157Spho sx_unlock(&sndstat_lock); 308107237Scg free(ent, M_DEVBUF); 30982180Scg 31082180Scg return 0; 31182180Scg } 31282180Scg } 313235157Spho sx_unlock(&sndstat_lock); 31482180Scg 31582180Scg return ENXIO; 31682180Scg} 31782180Scg 31882180Scgint 31982180Scgsndstat_unregisterfile(char *str) 32082180Scg{ 32182180Scg struct sndstat_entry *ent; 32282180Scg 323235157Spho sx_xlock(&sndstat_lock); 32482180Scg SLIST_FOREACH(ent, &sndstat_devlist, link) { 32582180Scg if (ent->dev == NULL && ent->str == str) { 32682180Scg SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); 32782180Scg sndstat_files--; 328235157Spho sx_unlock(&sndstat_lock); 329107237Scg free(ent, M_DEVBUF); 33082180Scg 33182180Scg return 0; 33282180Scg } 33382180Scg } 334235157Spho sx_unlock(&sndstat_lock); 33582180Scg 33682180Scg return ENXIO; 33782180Scg} 33882180Scg 33982180Scg/************************************************************************/ 34082180Scg 34178362Scgstatic int 34278362Scgsndstat_prepare(struct sbuf *s) 34378362Scg{ 34482180Scg struct sndstat_entry *ent; 345170815Sariff struct snddev_info *d; 34682180Scg int i, j; 34778362Scg 348170815Sariff sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n", 349193640Sariff (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); 35082180Scg if (SLIST_EMPTY(&sndstat_devlist)) { 35178362Scg sbuf_printf(s, "No devices installed.\n"); 35278362Scg sbuf_finish(s); 35378362Scg return sbuf_len(s); 35482180Scg } 35578362Scg 35682180Scg sbuf_printf(s, "Installed devices:\n"); 35782180Scg 35882180Scg for (i = 0; i <= sndstat_maxunit; i++) { 35982180Scg for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { 36082180Scg ent = sndstat_find(j, i); 36182180Scg if (!ent) 36282180Scg continue; 363170815Sariff d = device_get_softc(ent->dev); 364170815Sariff if (!PCM_REGISTERED(d)) 365170815Sariff continue; 366170815Sariff /* XXX Need Giant magic entry ??? */ 367170815Sariff PCM_ACQUIRE_QUICK(d); 36882180Scg sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); 36982180Scg sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); 370202267Smav if (snd_verbose > 0) 371202267Smav sbuf_printf(s, " %s", ent->str); 37282180Scg if (ent->handler) 373164614Sariff ent->handler(s, ent->dev, snd_verbose); 37482180Scg sbuf_printf(s, "\n"); 375170815Sariff PCM_RELEASE_QUICK(d); 37682180Scg } 37778362Scg } 37882180Scg 379164614Sariff if (snd_verbose >= 3 && sndstat_files > 0) { 38082180Scg sbuf_printf(s, "\nFile Versions:\n"); 38182180Scg 38282180Scg SLIST_FOREACH(ent, &sndstat_devlist, link) { 38382180Scg if (ent->dev == NULL && ent->str != NULL) 38482180Scg sbuf_printf(s, "%s\n", ent->str); 38582180Scg } 38682180Scg } 38782180Scg 38878362Scg sbuf_finish(s); 38978362Scg return sbuf_len(s); 39078362Scg} 39178362Scg 39278362Scgstatic int 39378362Scgsndstat_init(void) 39478362Scg{ 395170161Sariff if (sndstat_dev != NULL) 396170161Sariff return EINVAL; 397235157Spho sx_init(&sndstat_lock, "sndstat lock"); 398170161Sariff sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, 399170161Sariff UID_ROOT, GID_WHEEL, 0444, "sndstat"); 400170161Sariff return 0; 40178362Scg} 40278362Scg 40378362Scgstatic int 40478362Scgsndstat_uninit(void) 40578362Scg{ 406170161Sariff if (sndstat_dev == NULL) 407170161Sariff return EINVAL; 408170161Sariff 409235157Spho sx_xlock(&sndstat_lock); 410170161Sariff if (SNDSTAT_PID(sndstat_dev) != curthread->td_proc->p_pid) { 411235157Spho sx_unlock(&sndstat_lock); 41278362Scg return EBUSY; 41378670Scg } 41478362Scg 415235157Spho /* XXXPHO: use destroy_dev_sched() */ 416170161Sariff destroy_dev(sndstat_dev); 417170161Sariff sndstat_dev = NULL; 41878362Scg 419235157Spho SNDSTAT_FLUSH(); 420235157Spho 421235157Spho sx_unlock(&sndstat_lock); 422235157Spho sx_destroy(&sndstat_lock); 42378362Scg return 0; 42478362Scg} 42578362Scg 42678362Scgstatic void 42778362Scgsndstat_sysinit(void *p) 42878362Scg{ 42978362Scg sndstat_init(); 43078362Scg} 43178362Scg 43278362Scgstatic void 43378362Scgsndstat_sysuninit(void *p) 43478362Scg{ 435150115Syongari int error; 436150115Syongari 437150115Syongari error = sndstat_uninit(); 438150115Syongari KASSERT(error == 0, ("%s: error = %d", __func__, error)); 43978362Scg} 44078362Scg 44182180ScgSYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); 44282180ScgSYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); 443