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 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 desc.type = c->type; 91 desc.addr = ntohs(c->addr); 92 desc.maxval = ntohs(c->maxval); 93 _sioctl_ondesc_cb(&hdl->sioctl, 94 &desc, ntohs(c->curval)); 95 rpos += sizeof(struct amsg_ctl_desc); 96 c++; 97 } 98 hdl->buf_wpos = 0; 99 } 100 return 1; 101 } 102 103 /* 104 * execute the next message, return 0 if blocked 105 */ 106 static int 107 sioctl_aucat_runmsg(struct sioctl_aucat_hdl *hdl) 108 { 109 if (!_aucat_rmsg(&hdl->aucat, &hdl->sioctl.eof)) 110 return 0; 111 switch (ntohl(hdl->aucat.rmsg.cmd)) { 112 case AMSG_DATA: 113 hdl->buf_wpos = 0; 114 if (!sioctl_aucat_rdata(hdl)) 115 return 0; 116 break; 117 case AMSG_CTLSET: 118 DPRINTF("sioctl_aucat_runmsg: got CTLSET\n"); 119 _sioctl_onval_cb(&hdl->sioctl, 120 ntohs(hdl->aucat.rmsg.u.ctlset.addr), 121 ntohs(hdl->aucat.rmsg.u.ctlset.val)); 122 break; 123 case AMSG_CTLSYNC: 124 DPRINTF("sioctl_aucat_runmsg: got CTLSYNC\n"); 125 hdl->dump_wait = 0; 126 _sioctl_ondesc_cb(&hdl->sioctl, NULL, 0); 127 break; 128 default: 129 DPRINTF("sio_aucat_runmsg: unhandled message %u\n", 130 hdl->aucat.rmsg.cmd); 131 hdl->sioctl.eof = 1; 132 return 0; 133 } 134 hdl->aucat.rstate = RSTATE_MSG; 135 hdl->aucat.rtodo = sizeof(struct amsg); 136 return 1; 137 } 138 139 struct sioctl_hdl * 140 _sioctl_aucat_open(const char *str, unsigned int mode, int nbio) 141 { 142 struct sioctl_aucat_hdl *hdl; 143 144 hdl = malloc(sizeof(struct sioctl_aucat_hdl)); 145 if (hdl == NULL) 146 return NULL; 147 if (!_aucat_open(&hdl->aucat, str, mode)) 148 goto bad; 149 _sioctl_create(&hdl->sioctl, &sioctl_aucat_ops, mode, nbio); 150 if (!_aucat_setfl(&hdl->aucat, 1, &hdl->sioctl.eof)) 151 goto bad; 152 hdl->dump_wait = 0; 153 return (struct sioctl_hdl *)hdl; 154 bad: 155 free(hdl); 156 return NULL; 157 } 158 159 static void 160 sioctl_aucat_close(struct sioctl_hdl *addr) 161 { 162 struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr; 163 164 if (!hdl->sioctl.eof) 165 _aucat_setfl(&hdl->aucat, 0, &hdl->sioctl.eof); 166 _aucat_close(&hdl->aucat, hdl->sioctl.eof); 167 free(hdl); 168 } 169 170 static int 171 sioctl_aucat_ondesc(struct sioctl_hdl *addr) 172 { 173 struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr; 174 175 while (hdl->aucat.wstate != WSTATE_IDLE) { 176 if (!_sioctl_psleep(&hdl->sioctl, POLLOUT)) 177 return 0; 178 } 179 AMSG_INIT(&hdl->aucat.wmsg); 180 hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSUB); 181 hdl->aucat.wmsg.u.ctlsub.desc = 1; 182 hdl->aucat.wmsg.u.ctlsub.val = 0; 183 hdl->aucat.wtodo = sizeof(struct amsg); 184 if (!_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof)) 185 return 0; 186 hdl->dump_wait = 1; 187 while (hdl->dump_wait) { 188 DPRINTF("psleeping...\n"); 189 if (!_sioctl_psleep(&hdl->sioctl, 0)) 190 return 0; 191 DPRINTF("psleeping done\n"); 192 } 193 DPRINTF("done\n"); 194 return 1; 195 } 196 197 static int 198 sioctl_aucat_onval(struct sioctl_hdl *addr) 199 { 200 struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr; 201 202 while (hdl->aucat.wstate != WSTATE_IDLE) { 203 if (!_sioctl_psleep(&hdl->sioctl, POLLOUT)) 204 return 0; 205 } 206 AMSG_INIT(&hdl->aucat.wmsg); 207 hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSUB); 208 hdl->aucat.wmsg.u.ctlsub.desc = 1; 209 hdl->aucat.wmsg.u.ctlsub.val = 1; 210 hdl->aucat.wtodo = sizeof(struct amsg); 211 if (!_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof)) 212 return 0; 213 return 1; 214 } 215 216 static int 217 sioctl_aucat_setctl(struct sioctl_hdl *addr, unsigned int a, unsigned int v) 218 { 219 struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr; 220 221 hdl->aucat.wstate = WSTATE_MSG; 222 hdl->aucat.wtodo = sizeof(struct amsg); 223 hdl->aucat.wmsg.cmd = htonl(AMSG_CTLSET); 224 hdl->aucat.wmsg.u.ctlset.addr = htons(a); 225 hdl->aucat.wmsg.u.ctlset.val = htons(v); 226 while (hdl->aucat.wstate != WSTATE_IDLE) { 227 if (_aucat_wmsg(&hdl->aucat, &hdl->sioctl.eof)) 228 break; 229 if (hdl->sioctl.nbio || !_sioctl_psleep(&hdl->sioctl, POLLOUT)) 230 return 0; 231 } 232 return 1; 233 } 234 235 static int 236 sioctl_aucat_nfds(struct sioctl_hdl *addr) 237 { 238 return 1; 239 } 240 241 static int 242 sioctl_aucat_pollfd(struct sioctl_hdl *addr, struct pollfd *pfd, int events) 243 { 244 struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr; 245 246 return _aucat_pollfd(&hdl->aucat, pfd, events | POLLIN); 247 } 248 249 static int 250 sioctl_aucat_revents(struct sioctl_hdl *addr, struct pollfd *pfd) 251 { 252 struct sioctl_aucat_hdl *hdl = (struct sioctl_aucat_hdl *)addr; 253 int revents; 254 255 revents = _aucat_revents(&hdl->aucat, pfd); 256 if (revents & POLLIN) { 257 while (1) { 258 if (hdl->aucat.rstate == RSTATE_MSG) { 259 if (!sioctl_aucat_runmsg(hdl)) 260 break; 261 } 262 if (hdl->aucat.rstate == RSTATE_DATA) { 263 if (!sioctl_aucat_rdata(hdl)) 264 break; 265 } 266 } 267 revents &= ~POLLIN; 268 } 269 if (hdl->sioctl.eof) 270 return POLLHUP; 271 DPRINTFN(3, "sioctl_aucat_revents: revents = 0x%x\n", revents); 272 return revents; 273 } 274