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