1 /*	$OpenBSD$	*/
2 /*
3  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <errno.h>
18 #include <poll.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include "debug.h"
24 #include "sioctl_priv.h"
25 #include "bsd-compat.h"
26 
27 struct sioctl_hdl *
28 sioctl_open(const char *str, unsigned int mode, int nbio)
29 {
30 	static char devany[] = SIO_DEVANY;
31 	struct sioctl_hdl *hdl;
32 
33 #ifdef DEBUG
34 	_sndio_debug_init();
35 #endif
36 	if (str == NULL) /* backward compat */
37 		str = devany;
38 	if (strcmp(str, devany) == 0 && !issetugid()) {
39 		str = getenv("AUDIODEVICE");
40 		if (str == NULL)
41 			str = devany;
42 	}
43 	if (strcmp(str, devany) == 0) {
44 		hdl = _sioctl_aucat_open("snd/0", mode, nbio);
45 		if (hdl != NULL)
46 			return hdl;
47 #if defined(USE_SUN_MIXER)
48 		return _sioctl_sun_open("rsnd/0", mode, nbio);
49 #else
50 		return NULL;
51 #endif
52 	}
53 	if (_sndio_parsetype(str, "snd"))
54 		return _sioctl_aucat_open(str, mode, nbio);
55 	if (_sndio_parsetype(str, "rsnd"))
56 #if defined(USE_SUN_MIXER)
57 		return _sioctl_sun_open(str, mode, nbio);
58 #else
59 		return NULL;
60 #endif
61 	DPRINTF("sioctl_open: %s: unknown device type\n", str);
62 	return NULL;
63 }
64 
65 void
66 _sioctl_create(struct sioctl_hdl *hdl, struct sioctl_ops *ops,
67     unsigned int mode, int nbio)
68 {
69 	hdl->ops = ops;
70 	hdl->mode = mode;
71 	hdl->nbio = nbio;
72 	hdl->eof = 0;
73 	hdl->ctl_cb = NULL;
74 }
75 
76 int
77 _sioctl_psleep(struct sioctl_hdl *hdl, int event)
78 {
79 	struct pollfd pfds[SIOCTL_MAXNFDS];
80 	int revents, nfds;
81 
82 	for (;;) {
83 		nfds = sioctl_pollfd(hdl, pfds, event);
84 		if (nfds == 0)
85 			return 0;
86 		while (poll(pfds, nfds, -1) < 0) {
87 			if (errno == EINTR)
88 				continue;
89 			DPERROR("sioctl_psleep: poll");
90 			hdl->eof = 1;
91 			return 0;
92 		}
93 		revents = sioctl_revents(hdl, pfds);
94 		if (revents & POLLHUP) {
95 			DPRINTF("sioctl_psleep: hang-up\n");
96 			return 0;
97 		}
98 		if (event == 0 || (revents & event))
99 			break;
100 	}
101 	return 1;
102 }
103 
104 void
105 sioctl_close(struct sioctl_hdl *hdl)
106 {
107 	hdl->ops->close(hdl);
108 }
109 
110 int
111 sioctl_nfds(struct sioctl_hdl *hdl)
112 {
113 	return hdl->ops->nfds(hdl);
114 }
115 
116 int
117 sioctl_pollfd(struct sioctl_hdl *hdl, struct pollfd *pfd, int events)
118 {
119 	if (hdl->eof)
120 		return 0;
121 	return hdl->ops->pollfd(hdl, pfd, events);
122 }
123 
124 int
125 sioctl_revents(struct sioctl_hdl *hdl, struct pollfd *pfd)
126 {
127 	if (hdl->eof)
128 		return POLLHUP;
129 	return hdl->ops->revents(hdl, pfd);
130 }
131 
132 int
133 sioctl_eof(struct sioctl_hdl *hdl)
134 {
135 	return hdl->eof;
136 }
137 
138 int
139 sioctl_ondesc(struct sioctl_hdl *hdl,
140     void (*cb)(void *, struct sioctl_desc *, int), void *arg)
141 {
142 	hdl->desc_cb = cb;
143 	hdl->desc_arg = arg;
144 	return hdl->ops->ondesc(hdl);
145 }
146 
147 int
148 sioctl_onval(struct sioctl_hdl *hdl,
149     void (*cb)(void *, unsigned int, unsigned int), void *arg)
150 {
151 	hdl->ctl_cb = cb;
152 	hdl->ctl_arg = arg;
153 	return hdl->ops->onctl(hdl);
154 }
155 
156 void
157 _sioctl_ondesc_cb(struct sioctl_hdl *hdl,
158     struct sioctl_desc *desc, unsigned int val)
159 {
160 	if (desc) {
161 		DPRINTF("_sioctl_ondesc_cb: %u -> %s[%d].%s=%s[%d]:%d\n",
162 		    desc->addr,
163 		    desc->node0.name, desc->node0.unit,
164 		    desc->func,
165 		    desc->node1.name, desc->node1.unit,
166 		    val);
167 	}
168 	if (hdl->desc_cb)
169 		hdl->desc_cb(hdl->desc_arg, desc, val);
170 }
171 
172 void
173 _sioctl_onval_cb(struct sioctl_hdl *hdl, unsigned int addr, unsigned int val)
174 {
175 	DPRINTF("_sioctl_onval_cb: %u -> %u\n", addr, val);
176 	if (hdl->ctl_cb)
177 		hdl->ctl_cb(hdl->ctl_arg, addr, val);
178 }
179 
180 int
181 sioctl_setval(struct sioctl_hdl *hdl, unsigned int addr, unsigned int val)
182 {
183 	if (!(hdl->mode & SIOCTL_WRITE))
184 		return 0;
185 	return hdl->ops->setctl(hdl, addr, val);
186 }
187