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