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