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