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
dev_sioctl_ondesc(void * arg,struct sioctl_desc * desc,int val)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
dev_sioctl_onval(void * arg,unsigned int addr,unsigned int val)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
dev_sioctl_open(struct dev * d)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
dev_sioctl_close(struct dev * d)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
dev_sioctl_pollfd(void * arg,struct pollfd * pfd)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
dev_sioctl_revents(void * arg,struct pollfd * pfd)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
dev_sioctl_in(void * arg)169 dev_sioctl_in(void *arg)
170 {
171 }
172
173 void
dev_sioctl_out(void * arg)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
dev_sioctl_hup(void * arg)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