1 /* $OpenBSD: dev_sioctl.c,v 1.5 2020/04/24 11:33:28 ratchov Exp $ */ 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 <sys/time.h> 18 #include <sys/types.h> 19 20 #include <poll.h> 21 #include <sndio.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include "abuf.h" 26 #include "defs.h" 27 #include "dev.h" 28 #include "dsp.h" 29 #include "file.h" 30 #include "dev_sioctl.h" 31 #include "utils.h" 32 33 void dev_sioctl_ondesc(void *, struct sioctl_desc *, int); 34 void dev_sioctl_onval(void *, unsigned int, unsigned int); 35 int dev_sioctl_pollfd(void *, struct pollfd *); 36 int dev_sioctl_revents(void *, struct pollfd *); 37 void dev_sioctl_in(void *); 38 void dev_sioctl_out(void *); 39 void dev_sioctl_hup(void *); 40 41 struct fileops dev_sioctl_ops = { 42 "sioctl", 43 dev_sioctl_pollfd, 44 dev_sioctl_revents, 45 dev_sioctl_in, 46 dev_sioctl_out, 47 dev_sioctl_hup 48 }; 49 50 void 51 dev_sioctl_ondesc(void *arg, struct sioctl_desc *desc, int val) 52 { 53 #define GROUP_PREFIX "hw" 54 char group_buf[CTL_NAMEMAX], *group; 55 struct dev *d = arg; 56 int addr; 57 58 if (desc == NULL) { 59 dev_ctlsync(d); 60 return; 61 } 62 63 addr = CTLADDR_END + desc->addr; 64 dev_rmctl(d, addr); 65 66 /* 67 * prefix group names we use (currently "app") with "hw/" 68 * to ensure that all controls have unique names when multiple 69 * sndiod's are chained 70 */ 71 if (strcmp(desc->group, "app") == 0) { 72 group = group_buf; 73 if (snprintf(group_buf, CTL_NAMEMAX, GROUP_PREFIX "/%s", 74 desc->group) >= CTL_NAMEMAX) 75 return; 76 } else 77 group = desc->group; 78 79 dev_addctl(d, group, desc->type, addr, 80 desc->node0.name, desc->node0.unit, desc->func, 81 desc->node1.name, desc->node1.unit, desc->maxval, val); 82 } 83 84 void 85 dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val) 86 { 87 struct dev *d = arg; 88 struct ctl *c; 89 90 addr += CTLADDR_END; 91 92 dev_log(d); 93 log_puts(": onctl: addr = "); 94 log_putu(addr); 95 log_puts(", val = "); 96 log_putu(val); 97 log_puts("\n"); 98 99 for (c = d->ctl_list; c != NULL; c = c->next) { 100 if (c->addr != addr) 101 continue; 102 ctl_log(c); 103 log_puts(": new value -> "); 104 log_putu(val); 105 log_puts("\n"); 106 c->val_mask = ~0U; 107 c->curval = val; 108 } 109 } 110 111 /* 112 * open the control device. 113 */ 114 void 115 dev_sioctl_open(struct dev *d) 116 { 117 if (d->sioctl.hdl == NULL) { 118 /* 119 * At this point there are clients, for instance if we're 120 * called by dev_reopen() but the control device couldn't 121 * be opened. In this case controls have changed (thoseof 122 * old device are just removed) so we need to notify clients. 123 */ 124 dev_ctlsync(d); 125 return; 126 } 127 sioctl_ondesc(d->sioctl.hdl, dev_sioctl_ondesc, d); 128 sioctl_onval(d->sioctl.hdl, dev_sioctl_onval, d); 129 } 130 131 /* 132 * close the control device. 133 */ 134 void 135 dev_sioctl_close(struct dev *d) 136 { 137 struct ctl *c, **pc; 138 139 /* remove controls */ 140 pc = &d->ctl_list; 141 while ((c = *pc) != NULL) { 142 if (c->addr >= CTLADDR_END) { 143 c->refs_mask &= ~CTL_DEVMASK; 144 if (c->refs_mask == 0) { 145 *pc = c->next; 146 xfree(c); 147 continue; 148 } 149 c->type = CTL_NONE; 150 c->desc_mask = ~0; 151 } 152 pc = &c->next; 153 } 154 dev_ctlsync(d); 155 } 156 157 int 158 dev_sioctl_pollfd(void *arg, struct pollfd *pfd) 159 { 160 struct dev *d = arg; 161 struct ctl *c; 162 int events = 0; 163 164 for (c = d->ctl_list; c != NULL; c = c->next) { 165 if (c->dirty) 166 events |= POLLOUT; 167 } 168 return sioctl_pollfd(d->sioctl.hdl, pfd, events); 169 } 170 171 int 172 dev_sioctl_revents(void *arg, struct pollfd *pfd) 173 { 174 struct dev *d = arg; 175 176 return sioctl_revents(d->sioctl.hdl, pfd); 177 } 178 179 void 180 dev_sioctl_in(void *arg) 181 { 182 } 183 184 void 185 dev_sioctl_out(void *arg) 186 { 187 struct dev *d = arg; 188 struct ctl *c; 189 int cnt; 190 191 /* 192 * for each dirty ctl, call sioctl_setval() and dev_unref(). As 193 * dev_unref() may destroy the ctl_list, we must call it after 194 * we've finished iterating on it. 195 */ 196 cnt = 0; 197 for (c = d->ctl_list; c != NULL; c = c->next) { 198 if (!c->dirty) 199 continue; 200 if (!sioctl_setval(d->sioctl.hdl, 201 c->addr - CTLADDR_END, c->curval)) { 202 ctl_log(c); 203 log_puts(": set failed\n"); 204 break; 205 } 206 if (log_level >= 2) { 207 ctl_log(c); 208 log_puts(": changed\n"); 209 } 210 c->dirty = 0; 211 cnt++; 212 } 213 while (cnt-- > 0) 214 dev_unref(d); 215 } 216 217 void 218 dev_sioctl_hup(void *arg) 219 { 220 struct dev *d = arg; 221 222 dev_sioctl_close(d); 223 file_del(d->sioctl.file); 224 sioctl_close(d->sioctl.hdl); 225 d->sioctl.hdl = NULL; 226 } 227