14b31995imp/*-
21537078pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
31537078pfg *
4e3faadaariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
5e3faadaariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
6e3faadaariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
773a7a67cg * All rights reserved.
873a7a67cg *
973a7a67cg * Redistribution and use in source and binary forms, with or without
1073a7a67cg * modification, are permitted provided that the following conditions
1173a7a67cg * are met:
1273a7a67cg * 1. Redistributions of source code must retain the above copyright
1373a7a67cg *    notice, this list of conditions and the following disclaimer.
1473a7a67cg * 2. Redistributions in binary form must reproduce the above copyright
1573a7a67cg *    notice, this list of conditions and the following disclaimer in the
1673a7a67cg *    documentation and/or other materials provided with the distribution.
1773a7a67cg *
1873a7a67cg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1973a7a67cg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2073a7a67cg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2173a7a67cg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2273a7a67cg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2373a7a67cg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2473a7a67cg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2573a7a67cg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2673a7a67cg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2773a7a67cg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2873a7a67cg * SUCH DAMAGE.
2973a7a67cg */
3073a7a67cg
31e3faadaariff#ifdef HAVE_KERNEL_OPTION_HEADERS
32e3faadaariff#include "opt_snd.h"
33e3faadaariff#endif
34e3faadaariff
35b2eb38fcg#include <dev/sound/pcm/sound.h>
36e12a0ceariff#include <sys/ctype.h>
37658534eattilio#include <sys/lock.h>
38658534eattilio#include <sys/rwlock.h>
39e3faadaariff#include <sys/sysent.h>
4073a7a67cg
414e7ece6avg#include <vm/vm.h>
424e7ece6avg#include <vm/vm_object.h>
434e7ece6avg#include <vm/vm_page.h>
444e7ece6avg#include <vm/vm_pager.h>
454e7ece6avg
462cfb90ccgSND_DECLARE_FILE("$FreeBSD$");
472cfb90ccg
4898cd3abariffstatic int dsp_mmap_allow_prot_exec = 0;
49dddd332hselaskySYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RWTUN,
50e3faadaariff    &dsp_mmap_allow_prot_exec, 0,
51e3faadaariff    "linux mmap compatibility (-1=force disable 0=auto 1=force enable)");
5298cd3abariff
53159f45dhselaskystatic int dsp_basename_clone = 1;
54159f45dhselaskySYSCTL_INT(_hw_snd, OID_AUTO, basename_clone, CTLFLAG_RWTUN,
55159f45dhselasky    &dsp_basename_clone, 0,
56159f45dhselasky    "DSP basename cloning (0: Disable; 1: Enabled)");
57159f45dhselasky
58e12a0ceariffstruct dsp_cdevinfo {
59e12a0ceariff	struct pcm_channel *rdch, *wrch;
60e3faadaariff	struct pcm_channel *volch;
6198cd3abariff	int busy, simplex;
6298cd3abariff	TAILQ_ENTRY(dsp_cdevinfo) link;
63e12a0ceariff};
64e12a0ceariff
6598cd3abariff#define PCM_RDCH(x)		(((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
6698cd3abariff#define PCM_WRCH(x)		(((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
67e3faadaariff#define PCM_VOLCH(x)		(((struct dsp_cdevinfo *)(x)->si_drv1)->volch)
6898cd3abariff#define PCM_SIMPLEX(x)		(((struct dsp_cdevinfo *)(x)->si_drv1)->simplex)
69e12a0ceariff
7098cd3abariff#define DSP_CDEVINFO_CACHESIZE	8
71e12a0ceariff
7298cd3abariff#define DSP_REGISTERED(x, y)	(PCM_REGISTERED(x) &&			\
7398cd3abariff				 (y) != NULL && (y)->si_drv1 != NULL)
74e12a0ceariff
752d78819cg#define OLDPCM_IOCTL
762d78819cg
774a0664ecgstatic d_open_t dsp_open;
784a0664ecgstatic d_close_t dsp_close;
794a0664ecgstatic d_read_t dsp_read;
804a0664ecgstatic d_write_t dsp_write;
814a0664ecgstatic d_ioctl_t dsp_ioctl;
824a0664ecgstatic d_poll_t dsp_poll;
834a0664ecgstatic d_mmap_t dsp_mmap;
844e7ece6avgstatic d_mmap_single_t dsp_mmap_single;
854a0664ecg
86ebfd4famatkstruct cdevsw dsp_cdevsw = {
87ad92543phk	.d_version =	D_VERSION,
880ae911ephk	.d_open =	dsp_open,
890ae911ephk	.d_close =	dsp_close,
900ae911ephk	.d_read =	dsp_read,
910ae911ephk	.d_write =	dsp_write,
920ae911ephk	.d_ioctl =	dsp_ioctl,
930ae911ephk	.d_poll =	dsp_poll,
940ae911ephk	.d_mmap =	dsp_mmap,
954e7ece6avg	.d_mmap_single = dsp_mmap_single,
960ae911ephk	.d_name =	"dsp",
974a0664ecg};
984a0664ecg
99e12a0ceariffstatic eventhandler_tag dsp_ehtag = NULL;
100e12a0ceariffstatic int dsp_umax = -1;
101e12a0ceariffstatic int dsp_cmax = -1;
1024a0664ecg
1038140b07netchildstatic int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
1048140b07netchildstatic int dsp_oss_syncstart(int sg_id);
1058140b07netchildstatic int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
1068140b07netchildstatic int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled);
1078140b07netchildstatic int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
1088140b07netchildstatic int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
109e3faadaariffstatic int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask);
110e3faadaariff#ifdef OSSV4_EXPERIMENT
1118140b07netchildstatic int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
1128140b07netchildstatic int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
1138140b07netchildstatic int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
1148140b07netchildstatic int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
1158140b07netchildstatic int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
1168140b07netchild#endif
1178140b07netchild
1184a0664ecgstatic struct snddev_info *
119dfd1f7fphkdsp_get_info(struct cdev *dev)
1204a0664ecg{
121e12a0ceariff	return (devclass_get_softc(pcm_devclass, PCMUNIT(dev)));
1224a0664ecg}
1234a0664ecg
12498cd3abariffstatic uint32_t
125dfd1f7fphkdsp_get_flags(struct cdev *dev)
1262cfb90ccg{
1272cfb90ccg	device_t bdev;
1282cfb90ccg
129e12a0ceariff	bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
1302cfb90ccg
131e12a0ceariff	return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff);
1322cfb90ccg}
1332cfb90ccg
1342cfb90ccgstatic void
13598cd3abariffdsp_set_flags(struct cdev *dev, uint32_t flags)
1362cfb90ccg{
1372cfb90ccg	device_t bdev;
1382cfb90ccg
139e12a0ceariff	bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev));
1402cfb90ccg
141e12a0ceariff	if (bdev != NULL)
142e12a0ceariff		pcm_setflags(bdev, flags);
1432cfb90ccg}
1442cfb90ccg
1454a0664ecg/*
1466619102joel * return the channels associated with an open device instance.
1474a0664ecg * lock channels specified.
1484a0664ecg */
14973a7a67cgstatic int
15098cd3abariffgetchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch,
15198cd3abariff    uint32_t prio)
15273a7a67cg{
1534a0664ecg	struct snddev_info *d;
15498cd3abariff	struct pcm_channel *ch;
15598cd3abariff	uint32_t flags;
15606117a9cg
15798cd3abariff	if (PCM_SIMPLEX(dev) != 0) {
15898cd3abariff		d = dsp_get_info(dev);
15998cd3abariff		if (!PCM_REGISTERED(d))
16098cd3abariff			return (ENXIO);
161e3faadaariff		PCM_LOCK(d);
16298cd3abariff		PCM_WAIT(d);
16398cd3abariff		PCM_ACQUIRE(d);
16498cd3abariff		/*
16598cd3abariff		 * Note: order is important -
16698cd3abariff		 *       pcm flags -> prio query flags -> wild guess
16798cd3abariff		 */
16898cd3abariff		ch = NULL;
16998cd3abariff		flags = dsp_get_flags(dev);
17098cd3abariff		if (flags & SD_F_PRIO_WR) {
17198cd3abariff			ch = PCM_RDCH(dev);
17298cd3abariff			PCM_RDCH(dev) = NULL;
17398cd3abariff		} else if (flags & SD_F_PRIO_RD) {
17498cd3abariff			ch = PCM_WRCH(dev);
17598cd3abariff			PCM_WRCH(dev) = NULL;
17698cd3abariff		} else if (prio & SD_F_PRIO_WR) {
17798cd3abariff			ch = PCM_RDCH(dev);
17898cd3abariff			PCM_RDCH(dev) = NULL;
17998cd3abariff			flags |= SD_F_PRIO_WR;
18098cd3abariff		} else if (prio & SD_F_PRIO_RD) {
18198cd3abariff			ch = PCM_WRCH(dev);
18298cd3abariff			PCM_WRCH(dev) = NULL;
18398cd3abariff			flags |= SD_F_PRIO_RD;
18498cd3abariff		} else if (PCM_WRCH(dev) != NULL) {
18598cd3abariff			ch = PCM_RDCH(dev);
18698cd3abariff			PCM_RDCH(dev) = NULL;
18798cd3abariff			flags |= SD_F_PRIO_WR;
18898cd3abariff		} else if (PCM_RDCH(dev) != NULL) {
18998cd3abariff			ch = PCM_WRCH(dev);
19098cd3abariff			PCM_WRCH(dev) = NULL;
19198cd3abariff			flags |= SD_F_PRIO_RD;
19298cd3abariff		}
19398cd3abariff		PCM_SIMPLEX(dev) = 0;
1942cfb90ccg		dsp_set_flags(dev, flags);
19598cd3abariff		if (ch != NULL) {
19698cd3abariff			CHN_LOCK(ch);
19798cd3abariff			pcm_chnref(ch, -1);
19898cd3abariff			pcm_chnrelease(ch);
19998cd3abariff		}
20098cd3abariff		PCM_RELEASE(d);
201e3faadaariff		PCM_UNLOCK(d);
2022cfb90ccg	}
2034a0664ecg
204e12a0ceariff	*rdch = PCM_RDCH(dev);
205e12a0ceariff	*wrch = PCM_WRCH(dev);
2064a0664ecg
20798cd3abariff	if (*rdch != NULL && (prio & SD_F_PRIO_RD))
2084a0664ecg		CHN_LOCK(*rdch);
20998cd3abariff	if (*wrch != NULL && (prio & SD_F_PRIO_WR))
2104a0664ecg		CHN_LOCK(*wrch);
2114a0664ecg
21298cd3abariff	return (0);
21373a7a67cg}
21473a7a67cg
2154a0664ecg/* unlock specified channels */
21673a7a67cgstatic void
21798cd3abariffrelchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch,
21898cd3abariff    uint32_t prio)
21973a7a67cg{
22098cd3abariff	if (wrch != NULL && (prio & SD_F_PRIO_WR))
2214a0664ecg		CHN_UNLOCK(wrch);
22298cd3abariff	if (rdch != NULL && (prio & SD_F_PRIO_RD))
2234a0664ecg		CHN_UNLOCK(rdch);
22473a7a67cg}
22573a7a67cg
226e12a0ceariffstatic void
227e12a0ceariffdsp_cdevinfo_alloc(struct cdev *dev,
228e3faadaariff    struct pcm_channel *rdch, struct pcm_channel *wrch,
229e3faadaariff    struct pcm_channel *volch)
230e12a0ceariff{
23198cd3abariff	struct snddev_info *d;
232