xref: /openbsd-src/usr.bin/sndiod/dev_sioctl.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
1*7b639200Sratchov /*	$OpenBSD: dev_sioctl.c,v 1.11 2024/12/20 07:35:56 ratchov Exp $	*/
2d07fece6Sratchov /*
3d07fece6Sratchov  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
4d07fece6Sratchov  *
5d07fece6Sratchov  * Permission to use, copy, modify, and distribute this software for any
6d07fece6Sratchov  * purpose with or without fee is hereby granted, provided that the above
7d07fece6Sratchov  * copyright notice and this permission notice appear in all copies.
8d07fece6Sratchov  *
9d07fece6Sratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d07fece6Sratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d07fece6Sratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d07fece6Sratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d07fece6Sratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d07fece6Sratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d07fece6Sratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d07fece6Sratchov  */
17d07fece6Sratchov #include <sys/time.h>
18d07fece6Sratchov #include <sys/types.h>
19d07fece6Sratchov 
20d07fece6Sratchov #include <poll.h>
21d07fece6Sratchov #include <sndio.h>
22d07fece6Sratchov #include <stdio.h>
23d07fece6Sratchov #include <stdlib.h>
24d07fece6Sratchov #include <string.h>
25d07fece6Sratchov #include "abuf.h"
26d07fece6Sratchov #include "defs.h"
27d07fece6Sratchov #include "dev.h"
28d07fece6Sratchov #include "dsp.h"
29d07fece6Sratchov #include "file.h"
30d07fece6Sratchov #include "dev_sioctl.h"
31d07fece6Sratchov #include "utils.h"
32d07fece6Sratchov 
33d07fece6Sratchov void dev_sioctl_ondesc(void *, struct sioctl_desc *, int);
34d07fece6Sratchov void dev_sioctl_onval(void *, unsigned int, unsigned int);
35d07fece6Sratchov int dev_sioctl_pollfd(void *, struct pollfd *);
36d07fece6Sratchov int dev_sioctl_revents(void *, struct pollfd *);
37d07fece6Sratchov void dev_sioctl_in(void *);
38d07fece6Sratchov void dev_sioctl_out(void *);
39d07fece6Sratchov void dev_sioctl_hup(void *);
40d07fece6Sratchov 
41d07fece6Sratchov struct fileops dev_sioctl_ops = {
42d07fece6Sratchov 	"sioctl",
43d07fece6Sratchov 	dev_sioctl_pollfd,
44d07fece6Sratchov 	dev_sioctl_revents,
45d07fece6Sratchov 	dev_sioctl_in,
46d07fece6Sratchov 	dev_sioctl_out,
47d07fece6Sratchov 	dev_sioctl_hup
48d07fece6Sratchov };
49d07fece6Sratchov 
50d07fece6Sratchov void
51d07fece6Sratchov dev_sioctl_ondesc(void *arg, struct sioctl_desc *desc, int val)
52d07fece6Sratchov {
53d07fece6Sratchov 	struct dev *d = arg;
5499580020Sratchov 	char *group, group_buf[CTL_NAMEMAX];
55d07fece6Sratchov 
56e575fbdeSratchov 	if (desc == NULL) {
57e575fbdeSratchov 		dev_ctlsync(d);
58d07fece6Sratchov 		return;
59e575fbdeSratchov 	}
60e575fbdeSratchov 
6199580020Sratchov 	ctl_del(CTL_HW, d, &desc->addr);
62d07fece6Sratchov 
6399580020Sratchov 	if (desc->group[0] == 0)
6499580020Sratchov 		group = d->name;
6599580020Sratchov 	else {
6699580020Sratchov 		if (snprintf(group_buf, CTL_NAMEMAX, "%s/%s",
6799580020Sratchov 			d->name, desc->group) >= CTL_NAMEMAX)
68d07fece6Sratchov 			return;
6999580020Sratchov 		group = group_buf;
7099580020Sratchov 	}
71d07fece6Sratchov 
7299580020Sratchov 	ctl_new(CTL_HW, d, &desc->addr,
734d8b188fSratchov 	    desc->type, desc->display, group,
74d07fece6Sratchov 	    desc->node0.name, desc->node0.unit, desc->func,
75d07fece6Sratchov 	    desc->node1.name, desc->node1.unit, desc->maxval, val);
76d07fece6Sratchov }
77d07fece6Sratchov 
78d07fece6Sratchov void
79d07fece6Sratchov dev_sioctl_onval(void *arg, unsigned int addr, unsigned int val)
80d07fece6Sratchov {
81*7b639200Sratchov 	char str[64];
82d07fece6Sratchov 	struct dev *d = arg;
83d07fece6Sratchov 	struct ctl *c;
84d07fece6Sratchov 
85*7b639200Sratchov 	logx(2, "%s: onctl: addr = %u, val = %u", d->path, addr, val);
86d07fece6Sratchov 
8799580020Sratchov 	for (c = ctl_list; c != NULL; c = c->next) {
8853c84758Sratchov 		if (c->scope != CTL_HW || c->u.hw.dev != d ||
8953c84758Sratchov 		    c->u.hw.addr != addr)
90d07fece6Sratchov 			continue;
916551a2ffSratchov 
92*7b639200Sratchov 		logx(2, "ctl%u: %s -> %u", c->addr,
93*7b639200Sratchov 		    (ctl_fmt(str, sizeof(str), c), str), val);
946551a2ffSratchov 
95d07fece6Sratchov 		c->val_mask = ~0U;
96d07fece6Sratchov 		c->curval = val;
97d07fece6Sratchov 	}
98d07fece6Sratchov }
99d07fece6Sratchov 
100d07fece6Sratchov /*
101d07fece6Sratchov  * open the control device.
102d07fece6Sratchov  */
103d07fece6Sratchov void
104d07fece6Sratchov dev_sioctl_open(struct dev *d)
105d07fece6Sratchov {
106b3d2daecSratchov 	if (d->sioctl.hdl == NULL) {
107b3d2daecSratchov 		/*
108b3d2daecSratchov 		 * At this point there are clients, for instance if we're
109b3d2daecSratchov 		 * called by dev_reopen() but the control device couldn't
110b3d2daecSratchov 		 * be opened. In this case controls have changed (thoseof
111b3d2daecSratchov 		 * old device are just removed) so we need to notify clients.
112b3d2daecSratchov 		 */
113b3d2daecSratchov 		dev_ctlsync(d);
114d07fece6Sratchov 		return;
115b3d2daecSratchov 	}
116d07fece6Sratchov 	sioctl_ondesc(d->sioctl.hdl, dev_sioctl_ondesc, d);
117d07fece6Sratchov 	sioctl_onval(d->sioctl.hdl, dev_sioctl_onval, d);
118d07fece6Sratchov }
119d07fece6Sratchov 
120d07fece6Sratchov /*
121d07fece6Sratchov  * close the control device.
122d07fece6Sratchov  */
123d07fece6Sratchov void
124d07fece6Sratchov dev_sioctl_close(struct dev *d)
125d07fece6Sratchov {
126bbbe65dfSratchov 	struct ctl *c, **pc;
127bbbe65dfSratchov 
128bbbe65dfSratchov 	/* remove controls */
12999580020Sratchov 	pc = &ctl_list;
130bbbe65dfSratchov 	while ((c = *pc) != NULL) {
13199580020Sratchov 		if (c->scope == CTL_HW && c->u.hw.dev == d) {
132bbbe65dfSratchov 			c->refs_mask &= ~CTL_DEVMASK;
133bbbe65dfSratchov 			if (c->refs_mask == 0) {
134bbbe65dfSratchov 				*pc = c->next;
135bbbe65dfSratchov 				xfree(c);
136bbbe65dfSratchov 				continue;
137bbbe65dfSratchov 			}
138bbbe65dfSratchov 			c->type = CTL_NONE;
139bbbe65dfSratchov 			c->desc_mask = ~0;
140bbbe65dfSratchov 		}
141bbbe65dfSratchov 		pc = &c->next;
142bbbe65dfSratchov 	}
143bbbe65dfSratchov 	dev_ctlsync(d);
144d07fece6Sratchov }
145d07fece6Sratchov 
146d07fece6Sratchov int
147d07fece6Sratchov dev_sioctl_pollfd(void *arg, struct pollfd *pfd)
148d07fece6Sratchov {
149d07fece6Sratchov 	struct dev *d = arg;
150d07fece6Sratchov 	struct ctl *c;
151d07fece6Sratchov 	int events = 0;
152d07fece6Sratchov 
15399580020Sratchov 	for (c = ctl_list; c != NULL; c = c->next) {
15499580020Sratchov 		if (c->scope == CTL_HW && c->u.hw.dev == d && c->dirty)
155d07fece6Sratchov 			events |= POLLOUT;
156d07fece6Sratchov 	}
157d07fece6Sratchov 	return sioctl_pollfd(d->sioctl.hdl, pfd, events);
158d07fece6Sratchov }
159d07fece6Sratchov 
160d07fece6Sratchov int
161d07fece6Sratchov dev_sioctl_revents(void *arg, struct pollfd *pfd)
162d07fece6Sratchov {
163d07fece6Sratchov 	struct dev *d = arg;
164d07fece6Sratchov 
165d07fece6Sratchov 	return sioctl_revents(d->sioctl.hdl, pfd);
166d07fece6Sratchov }
167d07fece6Sratchov 
168d07fece6Sratchov void
169d07fece6Sratchov dev_sioctl_in(void *arg)
170d07fece6Sratchov {
171d07fece6Sratchov }
172d07fece6Sratchov 
173d07fece6Sratchov void
174d07fece6Sratchov dev_sioctl_out(void *arg)
175d07fece6Sratchov {
176d07fece6Sratchov 	struct dev *d = arg;
177d07fece6Sratchov 	struct ctl *c;
178d07fece6Sratchov 	int cnt;
179d07fece6Sratchov 
180d07fece6Sratchov 	/*
181d07fece6Sratchov 	 * for each dirty ctl, call sioctl_setval() and dev_unref(). As
182d07fece6Sratchov 	 * dev_unref() may destroy the ctl_list, we must call it after
183d07fece6Sratchov 	 * we've finished iterating on it.
184d07fece6Sratchov 	 */
185d07fece6Sratchov 	cnt = 0;
18699580020Sratchov 	for (c = ctl_list; c != NULL; c = c->next) {
18799580020Sratchov 		if (c->scope != CTL_HW || c->u.hw.dev != d || !c->dirty)
188d07fece6Sratchov 			continue;
18999580020Sratchov 		if (!sioctl_setval(d->sioctl.hdl, c->u.hw.addr, c->curval)) {
190*7b639200Sratchov 			logx(1, "ctl%u: set failed", c->addr);
191d07fece6Sratchov 			break;
192d07fece6Sratchov 		}
193d07fece6Sratchov 		c->dirty = 0;
194d07fece6Sratchov 		cnt++;
195d07fece6Sratchov 	}
196d07fece6Sratchov 	while (cnt-- > 0)
197d07fece6Sratchov 		dev_unref(d);
198d07fece6Sratchov }
199d07fece6Sratchov 
200d07fece6Sratchov void
201d07fece6Sratchov dev_sioctl_hup(void *arg)
202d07fece6Sratchov {
203d07fece6Sratchov 	struct dev *d = arg;
204d07fece6Sratchov 
205d07fece6Sratchov 	dev_sioctl_close(d);
206bbbe65dfSratchov 	file_del(d->sioctl.file);
207bbbe65dfSratchov 	sioctl_close(d->sioctl.hdl);
208bbbe65dfSratchov 	d->sioctl.hdl = NULL;
209d07fece6Sratchov }
210