xref: /openbsd-src/lib/libsndio/sioctl_aucat.c (revision 4d8b188f3074948b01f60b8d4fd246ea279f07b8)
1 /*
2  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include <errno.h>
17 #include <poll.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sndio.h>
22 #include <unistd.h>
23 #include <arpa/inet.h>
24 #include "debug.h"
25 #include "aucat.h"
26 #include "sioctl_priv.h"
27 
28 struct sioctl_aucat_hdl {
29 	struct sioctl_hdl sioctl;
30 	struct aucat aucat;
31 	struct sioctl_desc desc;
32 	struct amsg_ctl_desc buf[16];
33 	size_t buf_wpos;
34 	int dump_wait;
35 };
36 
37 static void sioctl_aucat_close(struct sioctl_hdl *);
38 static int sioctl_aucat_nfds(struct sioctl_hdl *);
39 static int sioctl_aucat_pollfd(struct sioctl_hdl *, struct pollfd *, int);
40 static int sioctl_aucat_revents(struct sioctl_hdl *, struct pollfd *);
41 static int sioctl_aucat_setctl(struct sioctl_hdl *, unsigned int, unsigned int);
42 static int sioctl_aucat_onval(struct sioctl_hdl *);
43 static int sioctl_aucat_ondesc(struct sioctl_hdl *);
44 
45 /*
46  * operations every device should support
47  */
48 struct sioctl_ops sioctl_aucat_ops = {
49 	sioctl_aucat_close,
50 	sioctl_aucat_nfds,
51 	sioctl_aucat_pollfd,
52 	sioctl_aucat_revents,
53 	sioctl_aucat_setctl,
54 	sioctl_aucat_onval,
55 	sioctl_aucat_ondesc
56 };
57 
58 static int
sioctl_aucat_rdata(struct sioctl_aucat_hdl * hdl)59 sioctl_aucat_rdata(struct sioctl_aucat_hdl *hdl)
60 {
61 	struct sioctl_desc desc;
62 	struct amsg_ctl_desc *c;
63 	size_t rpos;
64 	int n;
65 
66 	while (hdl->aucat.rstate == RSTATE_DATA) {
67 
68 		/* read entries */
69 		while (hdl->buf_wpos < sizeof(hdl->buf) &&
70 		    hdl->aucat.rstate == RSTATE_DATA) {
71 			n = _aucat_rdata(&hdl->aucat,
72 			    (unsigned char *)hdl->buf + hdl->buf_wpos,
73 			    sizeof(hdl->buf) - hdl->buf_wpos,
74 			    &hdl->sioctl.eof);
75 			if (n == 0 || hdl->sioctl.eof)
76 				return 0;
77 			hdl->buf_wpos += n;
78 		}
79 
80 		/* parse entries */
81 		c = hdl->buf;
82 		rpos = 0;
83 		while (rpos < hdl->buf_wpos) {
84 			strlcpy(desc.group, c->group, SIOCTL_NAMEMAX);
85 			strlcpy(desc.node0.name, c->node0.name, SIOCTL_NAMEMAX);
86 			desc.node0.unit = (int16_t)ntohs(c->node0.unit);
87 			strlcpy(desc.node1.name, c->node1.name, SIOCTL_NAMEMAX);
88 			desc.node1.unit = (int16_t)ntohs(c->node1.unit);
89 			strlcpy(desc.func, c->func, SIOCTL_NAMEMAX);
90 			strlcpy(desc.display, c->display, SIOCTL_DISPLAYMAX);
91 			desc.type = c->type;
92 			desc.addr = ntohs(c->addr);
93 			desc.maxval = ntohs(c->maxval);
94 			_sioctl_ondesc_cb(&hdl->sioctl,
95 			    &desc, ntohs(c->curval));
96 			rpos += sizeof(struct amsg_ctl_desc);
97 			c++;
98 		}
99 		hdl->buf_wpos = 0;
100 	}
101 	return 1;
102 }
103 
104 /*
105  * execute the next message, return 0 if blocked
106  */
107 static int
sioctl_aucat_runmsg(struct sioctl_aucat_hdl * hdl)108 sioctl_aucat_runmsg(struct sioctl_aucat_hdl *hdl)
109 {
110 	if (!_aucat_rmsg(&hdl->aucat, &hdl->sioctl.eof))
111 		return 0;
112 	switch (ntohl(hdl->aucat.rmsg.cmd)) {
113 	case AMSG_DATA:
114 		hdl->buf_wpos = 0;
115 		if (!sioctl_aucat_rdata(hdl))
116 			return 0;
117 		break;
118 	case AMSG_CTLSET:
119 		DPRINTF("sioctl_aucat_runmsg: got CTLSET\n");
120 		_sioctl_onval_cb(&hdl->sioctl,
121 		    ntohs(hdl->aucat.rmsg.u.ctlset.addr),
122 		    ntohs(hdl->aucat.rmsg.u.ctlset.val));
123 		break;
124 	case AMSG_CTLSYNC:
125 		DPRINTF("sioctl_aucat_runmsg: got CTLSYNC\n");
126 		hdl->dump_wait = 0;
127 		_sioctl_ondesc_cb(&hdl->sioctl, NULL, 0);
128 		break;
129 	default:
130 		DPRINTF("sio_aucat_runmsg: unhandled message %u\n",
131 		    hdl->aucat.rmsg.cmd);
132 		hdl->sioctl.eof = 1;
133 		return 0;
134 	}
135 	hdl->aucat.rstate = RSTATE_MSG;
136 	hdl->aucat.rtodo = sizeof(struct amsg);
137 	return 1;
138 }
139 
140 struct sioctl_hdl *
_sioctl_aucat_open(const char * str,unsigned int mode,int nbio)141 _sioctl_aucat_open(const char *str, unsigned int mode, int nbio)
142 {
143 	struct sioctl_aucat_hdl *hdl;
144 
145 	hdl = malloc(sizeof(struct sioctl_aucat_hdl));
146 	if (hdl == NULL)
147 		return NULL;
148 	if (!_aucat_open(&hdl->aucat, str, mode))
149 		goto bad;
150 	_sioctl_create(&hdl->sioctl, &sioctl_aucat_ops, mode, nbio);
151 	if (!_aucat_setfl(&hdl->aucat, 1, &hdl->sioctl.eof))
152 		goto bad;
153 	hdl->dump_wait = 0;
154 	return (struct sioctl_hdl *)hdl;
155 bad:
156 	free(hdl);
157 	return NULL;
158 }
159 
160 static void
sioctl_aucat_close(struct sioctl_hdl * addr)161 sioctl_aucat_close(struct sioctl_hdl *addr)
162 {
163 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
164 
165 	if (!hdl->sioctl.eof)
166 		_aucat_setfl(&hdl->aucat, 0, &hdl->sioctl.eof);
167 	_aucat_close(&hdl->aucat, hdl->sioctl.eof);
168 	free(hdl);
169 }
170 
171 static int
sioctl_aucat_ondesc(struct sioctl_hdl * addr)172 sioctl_aucat_ondesc(struct sioctl_hdl *addr)
173 {
174 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
175 
176 	while (hdl->aucat.wstate != WSTATE_IDLE) {
177 		if (!_sioctl_psleep(&hdl->sioctl, POLLOUT))
178 			return 0;
179 	}
180 	AMSG_INIT(&hdl->aucat.wmsg);
181 	hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSUB);
182 	hdl->aucat.wmsg.u.ctlsub.desc = 1;
183 	hdl->aucat.wmsg.u.ctlsub.val = 0;
184 	hdl->aucat.wtodo = sizeof(struct amsg);
185 	if (!_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof))
186 		return 0;
187 	hdl->dump_wait = 1;
188 	while (hdl->dump_wait) {
189 		DPRINTF("psleeping...\n");
190 		if (!_sioctl_psleep(&hdl->sioctl, 0))
191 			return 0;
192 		DPRINTF("psleeping done\n");
193 	}
194 	DPRINTF("done\n");
195 	return 1;
196 }
197 
198 static int
sioctl_aucat_onval(struct sioctl_hdl * addr)199 sioctl_aucat_onval(struct sioctl_hdl *addr)
200 {
201 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
202 
203 	while (hdl->aucat.wstate != WSTATE_IDLE) {
204 		if (!_sioctl_psleep(&hdl->sioctl, POLLOUT))
205 			return 0;
206 	}
207 	AMSG_INIT(&hdl->aucat.wmsg);
208 	hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSUB);
209 	hdl->aucat.wmsg.u.ctlsub.desc = 1;
210 	hdl->aucat.wmsg.u.ctlsub.val = 1;
211 	hdl->aucat.wtodo = sizeof(struct amsg);
212 	if (!_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof))
213 		return 0;
214 	return 1;
215 }
216 
217 static int
sioctl_aucat_setctl(struct sioctl_hdl * addr,unsigned int a,unsigned int v)218 sioctl_aucat_setctl(struct sioctl_hdl *addr, unsigned int a, unsigned int v)
219 {
220 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
221 
222 	hdl->aucat.wstate = WSTATE_MSG;
223 	hdl->aucat.wtodo = sizeof(struct amsg);
224 	hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSET);
225 	hdl->aucat.wmsg.u.ctlset.addr = htons(a);
226 	hdl->aucat.wmsg.u.ctlset.val = htons(v);
227 	while (hdl->aucat.wstate != WSTATE_IDLE) {
228 		if (_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof))
229 			break;
230 		if (hdl->sioctl.nbio || !_sioctl_psleep(&hdl->sioctl, POLLOUT))
231 			return 0;
232 	}
233 	return 1;
234 }
235 
236 static int
sioctl_aucat_nfds(struct sioctl_hdl * addr)237 sioctl_aucat_nfds(struct sioctl_hdl *addr)
238 {
239 	return 1;
240 }
241 
242 static int
sioctl_aucat_pollfd(struct sioctl_hdl * addr,struct pollfd * pfd,int events)243 sioctl_aucat_pollfd(struct sioctl_hdl *addr, struct pollfd *pfd, int events)
244 {
245 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
246 
247 	return _aucat_pollfd(&hdl->aucat, pfd, events | POLLIN);
248 }
249 
250 static int
sioctl_aucat_revents(struct sioctl_hdl * addr,struct pollfd * pfd)251 sioctl_aucat_revents(struct sioctl_hdl *addr, struct pollfd *pfd)
252 {
253 	struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr;
254 	int revents;
255 
256 	revents = _aucat_revents(&hdl->aucat, pfd);
257 	if (revents & POLLIN) {
258 		while (1) {
259 			if (hdl->aucat.rstate == RSTATE_MSG) {
260 				if (!sioctl_aucat_runmsg(hdl))
261 					break;
262 			}
263 			if (hdl->aucat.rstate == RSTATE_DATA) {
264 				if (!sioctl_aucat_rdata(hdl))
265 					break;
266 			}
267 		}
268 		revents &= ~POLLIN;
269 	}
270 	if (hdl->sioctl.eof)
271 		return POLLHUP;
272 	DPRINTFN(3, "sioctl_aucat_revents: revents = 0x%x\n", revents);
273 	return revents;
274 }
275