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 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 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 * 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 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 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 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 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 237 sioctl_aucat_nfds(struct sioctl_hdl *addr) 238 { 239 return 1; 240 } 241 242 static int 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 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