xref: /openbsd-src/usr.bin/sndiod/dev_sioctl.c (revision a0747c9f67a4ae71ccb71e62a28d1ea19e06a63c)
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