1 /* $OpenBSD: dev_sioctl.c,v 1.11 2024/12/20 07:35:56 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, desc->display, 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 char str[64]; 82 struct dev *d = arg; 83 struct ctl *c; 84 85 logx(2, "%s: onctl: addr = %u, val = %u", d->path, addr, val); 86 87 for (c = ctl_list; c != NULL; c = c->next) { 88 if (c->scope != CTL_HW || c->u.hw.dev != d || 89 c->u.hw.addr != addr) 90 continue; 91 92 logx(2, "ctl%u: %s -> %u", c->addr, 93 (ctl_fmt(str, sizeof(str), c), str), val); 94 95 c->val_mask = ~0U; 96 c->curval = val; 97 } 98 } 99 100 /* 101 * open the control device. 102 */ 103 void 104 dev_sioctl_open(struct dev *d) 105 { 106 if (d->sioctl.hdl == NULL) { 107 /* 108 * At this point there are clients, for instance if we're 109 * called by dev_reopen() but the control device couldn't 110 * be opened. In this case controls have changed (thoseof 111 * old device are just removed) so we need to notify clients. 112 */ 113 dev_ctlsync(d); 114 return; 115 } 116 sioctl_ondesc(d->sioctl.hdl, dev_sioctl_ondesc, d); 117 sioctl_onval(d->sioctl.hdl, dev_sioctl_onval, d); 118 } 119 120 /* 121 * close the control device. 122 */ 123 void 124 dev_sioctl_close(struct dev *d) 125 { 126 struct ctl *c, **pc; 127 128 /* remove controls */ 129 pc = &ctl_list; 130 while ((c = *pc) != NULL) { 131 if (c->scope == CTL_HW && c->u.hw.dev == d) { 132 c->refs_mask &= ~CTL_DEVMASK; 133 if (c->refs_mask == 0) { 134 *pc = c->next; 135 xfree(c); 136 continue; 137 } 138 c->type = CTL_NONE; 139 c->desc_mask = ~0; 140 } 141 pc = &c->next; 142 } 143 dev_ctlsync(d); 144 } 145 146 int 147 dev_sioctl_pollfd(void *arg, struct pollfd *pfd) 148 { 149 struct dev *d = arg; 150 struct ctl *c; 151 int events = 0; 152 153 for (c = ctl_list; c != NULL; c = c->next) { 154 if (c->scope == CTL_HW && c->u.hw.dev == d && c->dirty) 155 events |= POLLOUT; 156 } 157 return sioctl_pollfd(d->sioctl.hdl, pfd, events); 158 } 159 160 int 161 dev_sioctl_revents(void *arg, struct pollfd *pfd) 162 { 163 struct dev *d = arg; 164 165 return sioctl_revents(d->sioctl.hdl, pfd); 166 } 167 168 void 169 dev_sioctl_in(void *arg) 170 { 171 } 172 173 void 174 dev_sioctl_out(void *arg) 175 { 176 struct dev *d = arg; 177 struct ctl *c; 178 int cnt; 179 180 /* 181 * for each dirty ctl, call sioctl_setval() and dev_unref(). As 182 * dev_unref() may destroy the ctl_list, we must call it after 183 * we've finished iterating on it. 184 */ 185 cnt = 0; 186 for (c = ctl_list; c != NULL; c = c->next) { 187 if (c->scope != CTL_HW || c->u.hw.dev != d || !c->dirty) 188 continue; 189 if (!sioctl_setval(d->sioctl.hdl, c->u.hw.addr, c->curval)) { 190 logx(1, "ctl%u: set failed", c->addr); 191 break; 192 } 193 c->dirty = 0; 194 cnt++; 195 } 196 while (cnt-- > 0) 197 dev_unref(d); 198 } 199 200 void 201 dev_sioctl_hup(void *arg) 202 { 203 struct dev *d = arg; 204 205 dev_sioctl_close(d); 206 file_del(d->sioctl.file); 207 sioctl_close(d->sioctl.hdl); 208 d->sioctl.hdl = NULL; 209 } 210