1 /* $OpenBSD: dev_sioctl.c,v 1.1 2020/02/26 13:53:58 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 return; 60 addr = CTLADDR_END + desc->addr; 61 dev_rmctl(d, addr); 62 63 /* 64 * prefix group names we use (top-level and "app") with "hw." 65 * to ensure that all controls have unique names when multiple 66 * sndiod's are chained 67 */ 68 if (desc->group[0] == 0) 69 group = GROUP_PREFIX; 70 else { 71 group = group_buf; 72 if (snprintf(group_buf, CTL_NAMEMAX, GROUP_PREFIX "/%s", 73 desc->group) >= CTL_NAMEMAX) 74 return; 75 } 76 77 dev_addctl(d, group, desc->type, addr, 78 desc->node0.name, desc->node0.unit, desc->func, 79 desc->node1.name, desc->node1.unit, desc->maxval, val); 80 } 81 82 void 83 dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val) 84 { 85 struct dev *d = arg; 86 struct ctl *c; 87 88 addr += CTLADDR_END; 89 90 dev_log(d); 91 log_puts(": onctl: addr = "); 92 log_putu(addr); 93 log_puts(", val = "); 94 log_putu(val); 95 log_puts("\n"); 96 97 for (c = d->ctl_list; c != NULL; c = c->next) { 98 if (c->addr != addr) 99 continue; 100 ctl_log(c); 101 log_puts(": new value -> "); 102 log_putu(val); 103 log_puts("\n"); 104 c->val_mask = ~0U; 105 c->curval = val; 106 } 107 } 108 109 /* 110 * open the control device. 111 */ 112 void 113 dev_sioctl_open(struct dev *d) 114 { 115 if (d->sioctl.hdl == NULL) 116 return; 117 sioctl_ondesc(d->sioctl.hdl, dev_sioctl_ondesc, d); 118 sioctl_onval(d->sioctl.hdl, dev_sioctl_onval, d); 119 d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix", 120 sioctl_nfds(d->sioctl.hdl)); 121 } 122 123 /* 124 * close the control device. 125 */ 126 void 127 dev_sioctl_close(struct dev *d) 128 { 129 if (d->sioctl.hdl == NULL) 130 return; 131 file_del(d->sioctl.file); 132 } 133 134 int 135 dev_sioctl_pollfd(void *arg, struct pollfd *pfd) 136 { 137 struct dev *d = arg; 138 struct ctl *c; 139 int events = 0; 140 141 for (c = d->ctl_list; c != NULL; c = c->next) { 142 if (c->dirty) 143 events |= POLLOUT; 144 } 145 return sioctl_pollfd(d->sioctl.hdl, pfd, events); 146 } 147 148 int 149 dev_sioctl_revents(void *arg, struct pollfd *pfd) 150 { 151 struct dev *d = arg; 152 153 return sioctl_revents(d->sioctl.hdl, pfd); 154 } 155 156 void 157 dev_sioctl_in(void *arg) 158 { 159 } 160 161 void 162 dev_sioctl_out(void *arg) 163 { 164 struct dev *d = arg; 165 struct ctl *c; 166 int cnt; 167 168 /* 169 * for each dirty ctl, call sioctl_setval() and dev_unref(). As 170 * dev_unref() may destroy the ctl_list, we must call it after 171 * we've finished iterating on it. 172 */ 173 cnt = 0; 174 for (c = d->ctl_list; c != NULL; c = c->next) { 175 if (!c->dirty) 176 continue; 177 if (!sioctl_setval(d->sioctl.hdl, 178 c->addr - CTLADDR_END, c->curval)) { 179 ctl_log(c); 180 log_puts(": set failed\n"); 181 break; 182 } 183 if (log_level >= 2) { 184 ctl_log(c); 185 log_puts(": changed\n"); 186 } 187 c->dirty = 0; 188 cnt++; 189 } 190 while (cnt-- > 0) 191 dev_unref(d); 192 } 193 194 void 195 dev_sioctl_hup(void *arg) 196 { 197 struct dev *d = arg; 198 199 dev_sioctl_close(d); 200 } 201