xref: /openbsd/lib/libsndio/sioctl_sun.c (revision 4aaef610)
1d07fece6Sratchov /*
2d07fece6Sratchov  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
3d07fece6Sratchov  *
4d07fece6Sratchov  * Permission to use, copy, modify, and distribute this software for any
5d07fece6Sratchov  * purpose with or without fee is hereby granted, provided that the above
6d07fece6Sratchov  * copyright notice and this permission notice appear in all copies.
7d07fece6Sratchov  *
8d07fece6Sratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9d07fece6Sratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10d07fece6Sratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11d07fece6Sratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12d07fece6Sratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13d07fece6Sratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14d07fece6Sratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15d07fece6Sratchov  */
16d07fece6Sratchov /*
17d07fece6Sratchov  * the way the sun mixer is designed doesn't let us representing
18d07fece6Sratchov  * it easily with the sioctl api. For now expose only few
19d07fece6Sratchov  * white-listed controls the same way as we do in kernel
20d07fece6Sratchov  * for the wskbd volume keys.
21d07fece6Sratchov  */
22d07fece6Sratchov #include <sys/types.h>
23d07fece6Sratchov #include <sys/ioctl.h>
24d07fece6Sratchov #include <sys/audioio.h>
25d07fece6Sratchov #include <errno.h>
26d07fece6Sratchov #include <fcntl.h>
27d07fece6Sratchov #include <limits.h>
28d07fece6Sratchov #include <poll.h>
29d07fece6Sratchov #include <sndio.h>
30d07fece6Sratchov #include <stdio.h>
31d07fece6Sratchov #include <stdlib.h>
32d07fece6Sratchov #include <string.h>
33d07fece6Sratchov #include <unistd.h>
34d07fece6Sratchov 
35d07fece6Sratchov #include "debug.h"
36d07fece6Sratchov #include "sioctl_priv.h"
37d07fece6Sratchov 
38d07fece6Sratchov #define DEVPATH_PREFIX	"/dev/audioctl"
39d07fece6Sratchov #define DEVPATH_MAX 	(1 +		\
40d07fece6Sratchov 	sizeof(DEVPATH_PREFIX) - 1 +	\
41d07fece6Sratchov 	sizeof(int) * 3)
42d07fece6Sratchov 
43d07fece6Sratchov struct volume
44d07fece6Sratchov {
45d07fece6Sratchov 	int nch;			/* channels in the level control */
46d07fece6Sratchov 	int level_idx;			/* index of the level control */
47d07fece6Sratchov 	int level_val[8];		/* current value */
48d07fece6Sratchov 	int mute_idx;			/* index of the mute control */
49d07fece6Sratchov 	int mute_val;			/* per channel state of mute control */
50d07fece6Sratchov 	int base_addr;
51d07fece6Sratchov 	char *name;
52d07fece6Sratchov };
53d07fece6Sratchov 
54d07fece6Sratchov struct sioctl_sun_hdl {
55d07fece6Sratchov 	struct sioctl_hdl sioctl;
56*4aaef610Sratchov 	char display[SIOCTL_DISPLAYMAX];
57*4aaef610Sratchov 	int display_addr;
58d07fece6Sratchov 	struct volume output, input;
59d07fece6Sratchov 	int fd, events;
60d07fece6Sratchov };
61d07fece6Sratchov 
62d07fece6Sratchov static void sioctl_sun_close(struct sioctl_hdl *);
63d07fece6Sratchov static int sioctl_sun_nfds(struct sioctl_hdl *);
64d07fece6Sratchov static int sioctl_sun_pollfd(struct sioctl_hdl *, struct pollfd *, int);
65d07fece6Sratchov static int sioctl_sun_revents(struct sioctl_hdl *, struct pollfd *);
66d07fece6Sratchov static int sioctl_sun_setctl(struct sioctl_hdl *, unsigned int, unsigned int);
67d07fece6Sratchov static int sioctl_sun_onval(struct sioctl_hdl *);
68d07fece6Sratchov static int sioctl_sun_ondesc(struct sioctl_hdl *);
69d07fece6Sratchov 
70d07fece6Sratchov /*
71d07fece6Sratchov  * operations every device should support
72d07fece6Sratchov  */
73d07fece6Sratchov struct sioctl_ops sioctl_sun_ops = {
74d07fece6Sratchov 	sioctl_sun_close,
75d07fece6Sratchov 	sioctl_sun_nfds,
76d07fece6Sratchov 	sioctl_sun_pollfd,
77d07fece6Sratchov 	sioctl_sun_revents,
78d07fece6Sratchov 	sioctl_sun_setctl,
79d07fece6Sratchov 	sioctl_sun_onval,
80d07fece6Sratchov 	sioctl_sun_ondesc
81d07fece6Sratchov };
82d07fece6Sratchov 
83d07fece6Sratchov static int
initmute(struct sioctl_sun_hdl * hdl,struct mixer_devinfo * info)84d07fece6Sratchov initmute(struct sioctl_sun_hdl *hdl, struct mixer_devinfo *info)
85d07fece6Sratchov {
86d07fece6Sratchov 	struct mixer_devinfo mi;
879b9c0fffSratchov 	char name[MAX_AUDIO_DEV_LEN];
88d07fece6Sratchov 
89d07fece6Sratchov 	for (mi.index = info->next; mi.index != -1; mi.index = mi.next) {
90d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
91d07fece6Sratchov 			break;
92d07fece6Sratchov 		if (strcmp(mi.label.name, AudioNmute) == 0)
93d07fece6Sratchov 			return mi.index;
94d07fece6Sratchov 	}
959b9c0fffSratchov 
969b9c0fffSratchov 	/* try "_mute" suffix */
979b9c0fffSratchov 	snprintf(name, sizeof(name), "%s_mute", info->label.name);
989b9c0fffSratchov 	for (mi.index = 0; ; mi.index++) {
999b9c0fffSratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
1009b9c0fffSratchov 			break;
1019b9c0fffSratchov 		if (info->mixer_class == mi.mixer_class &&
1029b9c0fffSratchov 		    strcmp(mi.label.name, name) == 0)
1039b9c0fffSratchov 			return mi.index;
1049b9c0fffSratchov 	}
105d07fece6Sratchov 	return -1;
106d07fece6Sratchov }
107d07fece6Sratchov 
108d07fece6Sratchov static int
initvol(struct sioctl_sun_hdl * hdl,struct volume * vol,char * cn,char * dn)109d07fece6Sratchov initvol(struct sioctl_sun_hdl *hdl, struct volume *vol, char *cn, char *dn)
110d07fece6Sratchov {
111d07fece6Sratchov 	struct mixer_devinfo dev, cls;
112d07fece6Sratchov 
113d07fece6Sratchov 	for (dev.index = 0; ; dev.index++) {
114d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &dev) < 0)
115d07fece6Sratchov 			break;
116d07fece6Sratchov 		if (dev.type != AUDIO_MIXER_VALUE)
117d07fece6Sratchov 			continue;
118d07fece6Sratchov 		cls.index = dev.mixer_class;
119d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &cls) < 0)
120d07fece6Sratchov 			break;
121d07fece6Sratchov 		if (strcmp(cls.label.name, cn) == 0 &&
122d07fece6Sratchov 		    strcmp(dev.label.name, dn) == 0) {
123d07fece6Sratchov 			vol->nch = dev.un.v.num_channels;
124d07fece6Sratchov 			vol->level_idx = dev.index;
125d07fece6Sratchov 			vol->mute_idx = initmute(hdl, &dev);
126d07fece6Sratchov 			DPRINTF("using %s.%s, %d channels, %s\n", cn, dn,
127d07fece6Sratchov 			    vol->nch, vol->mute_idx >= 0 ? "mute" : "no mute");
128d07fece6Sratchov 			return 1;
129d07fece6Sratchov 		}
130d07fece6Sratchov 	}
131d07fece6Sratchov 	vol->level_idx = vol->mute_idx = -1;
132d07fece6Sratchov 	return 0;
133d07fece6Sratchov }
134d07fece6Sratchov 
135d07fece6Sratchov static void
init(struct sioctl_sun_hdl * hdl)136d07fece6Sratchov init(struct sioctl_sun_hdl *hdl)
137d07fece6Sratchov {
138d07fece6Sratchov 	static struct {
139d07fece6Sratchov 		char *cn, *dn;
140d07fece6Sratchov 	} output_names[] = {
141d07fece6Sratchov 		{AudioCoutputs, AudioNmaster},
142d07fece6Sratchov 		{AudioCinputs,  AudioNdac},
143d07fece6Sratchov 		{AudioCoutputs, AudioNdac},
144d07fece6Sratchov 		{AudioCoutputs, AudioNoutput}
145d07fece6Sratchov 	}, input_names[] = {
146d07fece6Sratchov 		{AudioCrecord, AudioNrecord},
147d07fece6Sratchov 		{AudioCrecord, AudioNvolume},
148d07fece6Sratchov 		{AudioCinputs, AudioNrecord},
149d07fece6Sratchov 		{AudioCinputs, AudioNvolume},
150d07fece6Sratchov 		{AudioCinputs, AudioNinput}
151d07fece6Sratchov 	};
152*4aaef610Sratchov 	struct audio_device getdev;
153d07fece6Sratchov 	int i;
154d07fece6Sratchov 
155d07fece6Sratchov 	for (i = 0; i < sizeof(output_names) / sizeof(output_names[0]); i++) {
156d07fece6Sratchov 		if (initvol(hdl, &hdl->output,
157d07fece6Sratchov 			output_names[i].cn, output_names[i].dn)) {
158d07fece6Sratchov 			hdl->output.name = "output";
159d07fece6Sratchov 			hdl->output.base_addr = 0;
160d07fece6Sratchov 			break;
161d07fece6Sratchov 		}
162d07fece6Sratchov 	}
163d07fece6Sratchov 	for (i = 0; i < sizeof(input_names) / sizeof(input_names[0]); i++) {
164d07fece6Sratchov 		if (initvol(hdl, &hdl->input,
165d07fece6Sratchov 			input_names[i].cn, input_names[i].dn)) {
166d07fece6Sratchov 			hdl->input.name = "input";
167d07fece6Sratchov 			hdl->input.base_addr = 64;
168d07fece6Sratchov 			break;
169d07fece6Sratchov 		}
170d07fece6Sratchov 	}
171*4aaef610Sratchov 
172*4aaef610Sratchov 	hdl->display_addr = 128;
173*4aaef610Sratchov 	if (ioctl(hdl->fd, AUDIO_GETDEV, &getdev) == -1)
174*4aaef610Sratchov 		strlcpy(hdl->display, "unknown", SIOCTL_DISPLAYMAX);
175*4aaef610Sratchov 	else
176*4aaef610Sratchov 		strlcpy(hdl->display, getdev.name, SIOCTL_DISPLAYMAX);
177*4aaef610Sratchov 	DPRINTF("init: server.device: display = %s\n", hdl->display);
178d07fece6Sratchov }
179d07fece6Sratchov 
180d07fece6Sratchov static int
setvol(struct sioctl_sun_hdl * hdl,struct volume * vol,int addr,int val)181d07fece6Sratchov setvol(struct sioctl_sun_hdl *hdl, struct volume *vol, int addr, int val)
182d07fece6Sratchov {
183d07fece6Sratchov 	struct mixer_ctrl ctrl;
184d07fece6Sratchov 	int i;
185d07fece6Sratchov 
186d07fece6Sratchov 	addr -= vol->base_addr;
187d07fece6Sratchov 	if (vol->level_idx >= 0 && addr >= 0 && addr < vol->nch) {
188d07fece6Sratchov 		if (vol->level_val[addr] == val) {
189d07fece6Sratchov 			DPRINTF("level %d, no change\n", val);
190d07fece6Sratchov 			return 1;
191d07fece6Sratchov 		}
192d07fece6Sratchov 		vol->level_val[addr] = val;
193d07fece6Sratchov 		ctrl.dev = vol->level_idx;
194d07fece6Sratchov 		ctrl.type = AUDIO_MIXER_VALUE;
195d07fece6Sratchov 		ctrl.un.value.num_channels = vol->nch;
196d07fece6Sratchov 		for (i = 0; i < vol->nch; i++)
197d07fece6Sratchov 			ctrl.un.value.level[i] = vol->level_val[i];
198d07fece6Sratchov 		DPRINTF("vol %d setting to %d\n", addr, vol->level_val[addr]);
199d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_WRITE, &ctrl) < 0) {
200d07fece6Sratchov 			DPRINTF("level write failed\n");
201d07fece6Sratchov 			return 0;
202d07fece6Sratchov 		}
203d07fece6Sratchov 		_sioctl_onval_cb(&hdl->sioctl, vol->base_addr + addr, val);
204d07fece6Sratchov 		return 1;
205d07fece6Sratchov 	}
206d07fece6Sratchov 
207d07fece6Sratchov 	addr -= 32;
208d07fece6Sratchov 	if (vol->mute_idx >= 0 && addr >= 0 && addr < vol->nch) {
209d07fece6Sratchov 		val = val ? 1 : 0;
210d07fece6Sratchov 		if (vol->mute_val == val) {
211d07fece6Sratchov 			DPRINTF("mute %d, no change\n", val);
212d07fece6Sratchov 			return 1;
213d07fece6Sratchov 		}
214d07fece6Sratchov 		vol->mute_val = val;
215d07fece6Sratchov 		ctrl.dev = vol->mute_idx;
216d07fece6Sratchov 		ctrl.type = AUDIO_MIXER_ENUM;
217d07fece6Sratchov 		ctrl.un.ord = val;
218d07fece6Sratchov 		DPRINTF("mute setting to %d\n", val);
219d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_WRITE, &ctrl) < 0) {
220d07fece6Sratchov 			DPERROR("mute write\n");
221d07fece6Sratchov 			return 0;
222d07fece6Sratchov 		}
223d07fece6Sratchov 		for (i = 0; i < vol->nch; i++) {
224d07fece6Sratchov 			_sioctl_onval_cb(&hdl->sioctl,
225d07fece6Sratchov 			    vol->base_addr + 32 + i, val);
226d07fece6Sratchov 		}
227d07fece6Sratchov 		return 1;
228d07fece6Sratchov 	}
229d07fece6Sratchov 	return 1;
230d07fece6Sratchov }
231d07fece6Sratchov 
232d07fece6Sratchov static int
scanvol(struct sioctl_sun_hdl * hdl,struct volume * vol)233d07fece6Sratchov scanvol(struct sioctl_sun_hdl *hdl, struct volume *vol)
234d07fece6Sratchov {
235d07fece6Sratchov 	struct sioctl_desc desc;
236d07fece6Sratchov 	struct mixer_ctrl ctrl;
237d07fece6Sratchov 	int i, val;
238d07fece6Sratchov 
239d07fece6Sratchov 	memset(&desc, 0, sizeof(struct sioctl_desc));
240d07fece6Sratchov 	if (vol->level_idx >= 0) {
241d07fece6Sratchov 		ctrl.dev = vol->level_idx;
242d07fece6Sratchov 		ctrl.type = AUDIO_MIXER_VALUE;
243d07fece6Sratchov 		ctrl.un.value.num_channels = vol->nch;
244d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_READ, &ctrl) < 0) {
245d07fece6Sratchov 			DPRINTF("level read failed\n");
246d07fece6Sratchov 			return 0;
247d07fece6Sratchov 		}
248d07fece6Sratchov 		desc.type = SIOCTL_NUM;
249d07fece6Sratchov 		desc.maxval = AUDIO_MAX_GAIN;
250d07fece6Sratchov 		desc.node1.name[0] = 0;
251d07fece6Sratchov 		desc.node1.unit = -1;
252d07fece6Sratchov 		strlcpy(desc.func, "level", SIOCTL_NAMEMAX);
253d07fece6Sratchov 		strlcpy(desc.node0.name, vol->name, SIOCTL_NAMEMAX);
254d07fece6Sratchov 		for (i = 0; i < vol->nch; i++) {
255d07fece6Sratchov 			desc.node0.unit = i;
256d07fece6Sratchov 			desc.addr = vol->base_addr + i;
257d07fece6Sratchov 			val = ctrl.un.value.level[i];
258d07fece6Sratchov 			vol->level_val[i] = val;
259d07fece6Sratchov 			_sioctl_ondesc_cb(&hdl->sioctl, &desc, val);
260d07fece6Sratchov 		}
261d07fece6Sratchov 	}
262d07fece6Sratchov 	if (vol->mute_idx >= 0) {
263d07fece6Sratchov 		ctrl.dev = vol->mute_idx;
264d07fece6Sratchov 		ctrl.type = AUDIO_MIXER_ENUM;
265d07fece6Sratchov 		if (ioctl(hdl->fd, AUDIO_MIXER_READ, &ctrl) < 0) {
266d07fece6Sratchov 			DPRINTF("mute read failed\n");
267d07fece6Sratchov 			return 0;
268d07fece6Sratchov 		}
269d07fece6Sratchov 		desc.type = SIOCTL_SW;
270d07fece6Sratchov 		desc.maxval = 1;
271d07fece6Sratchov 		desc.node1.name[0] = 0;
272d07fece6Sratchov 		desc.node1.unit = -1;
273d07fece6Sratchov 		strlcpy(desc.func, "mute", SIOCTL_NAMEMAX);
274d07fece6Sratchov 		strlcpy(desc.node0.name, vol->name, SIOCTL_NAMEMAX);
275d07fece6Sratchov 		val = ctrl.un.ord ? 1 : 0;
276d07fece6Sratchov 		vol->mute_val = val;
277d07fece6Sratchov 		for (i = 0; i < vol->nch; i++) {
278d07fece6Sratchov 			desc.node0.unit = i;
279d07fece6Sratchov 			desc.addr = vol->base_addr + 32 + i;
280d07fece6Sratchov 			_sioctl_ondesc_cb(&hdl->sioctl, &desc, val);
281d07fece6Sratchov 		}
282d07fece6Sratchov 	}
283d07fece6Sratchov 	return 1;
284d07fece6Sratchov }
285d07fece6Sratchov 
286d07fece6Sratchov static int
updatevol(struct sioctl_sun_hdl * hdl,struct volume * vol,int idx)287d07fece6Sratchov updatevol(struct sioctl_sun_hdl *hdl, struct volume *vol, int idx)
288d07fece6Sratchov {
289d07fece6Sratchov 	struct mixer_ctrl ctrl;
290d07fece6Sratchov 	int val, i;
291d07fece6Sratchov 
292d07fece6Sratchov 	if (idx == vol->mute_idx)
293d07fece6Sratchov 		ctrl.type = AUDIO_MIXER_ENUM;
294d07fece6Sratchov 	else {
295d07fece6Sratchov 		ctrl.type = AUDIO_MIXER_VALUE;
296d07fece6Sratchov 		ctrl.un.value.num_channels = vol->nch;
297d07fece6Sratchov 	}
298d07fece6Sratchov 	ctrl.dev = idx;
299d07fece6Sratchov 	if (ioctl(hdl->fd, AUDIO_MIXER_READ, &ctrl) == -1) {
300d07fece6Sratchov 		DPERROR("sioctl_sun_revents: ioctl\n");
301d07fece6Sratchov 		hdl->sioctl.eof = 1;
302d07fece6Sratchov 		return 0;
303d07fece6Sratchov 	}
304d07fece6Sratchov 	if (idx == vol->mute_idx) {
305d07fece6Sratchov 		val = ctrl.un.ord ? 1 : 0;
306d07fece6Sratchov 		if (vol->mute_val == val)
307d07fece6Sratchov 			return 1;
308d07fece6Sratchov 		vol->mute_val = val;
309d07fece6Sratchov 		for (i = 0; i < vol->nch; i++) {
310d07fece6Sratchov 			_sioctl_onval_cb(&hdl->sioctl,
311d07fece6Sratchov 			    vol->base_addr + 32 + i, val);
312d07fece6Sratchov 		}
313d07fece6Sratchov 	} else {
314d07fece6Sratchov 		for (i = 0; i < vol->nch; i++) {
315d07fece6Sratchov 			val = ctrl.un.value.level[i];
316d07fece6Sratchov 			if (vol->level_val[i] == val)
317d07fece6Sratchov 				continue;
318d07fece6Sratchov 			vol->level_val[i] = val;
319d07fece6Sratchov 			_sioctl_onval_cb(&hdl->sioctl,
320d07fece6Sratchov 			    vol->base_addr + i, val);
321d07fece6Sratchov 		}
322d07fece6Sratchov 	}
323d07fece6Sratchov 	return 1;
324d07fece6Sratchov }
325d07fece6Sratchov 
326d07fece6Sratchov int
sioctl_sun_getfd(const char * str,unsigned int mode,int nbio)327d07fece6Sratchov sioctl_sun_getfd(const char *str, unsigned int mode, int nbio)
328d07fece6Sratchov {
329d07fece6Sratchov 	const char *p;
330d07fece6Sratchov 	char path[DEVPATH_MAX];
331d07fece6Sratchov 	unsigned int devnum;
332d07fece6Sratchov 	int fd, flags;
333d07fece6Sratchov 
334d07fece6Sratchov #ifdef DEBUG
335d07fece6Sratchov 	_sndio_debug_init();
336d07fece6Sratchov #endif
337d07fece6Sratchov 	p = _sndio_parsetype(str, "rsnd");
338d07fece6Sratchov 	if (p == NULL) {
339d07fece6Sratchov 		DPRINTF("sioctl_sun_getfd: %s: \"rsnd\" expected\n", str);
340d07fece6Sratchov 		return -1;
341d07fece6Sratchov 	}
342d07fece6Sratchov 	switch (*p) {
343d07fece6Sratchov 	case '/':
344d07fece6Sratchov 		p++;
345d07fece6Sratchov 		break;
346d07fece6Sratchov 	default:
347d07fece6Sratchov 		DPRINTF("sioctl_sun_getfd: %s: '/' expected\n", str);
348d07fece6Sratchov 		return -1;
349d07fece6Sratchov 	}
350d07fece6Sratchov 	if (strcmp(p, "default") == 0) {
351d07fece6Sratchov 		devnum = 0;
352d07fece6Sratchov 	} else {
353d07fece6Sratchov 		p = _sndio_parsenum(p, &devnum, 255);
354d07fece6Sratchov 		if (p == NULL || *p != '\0') {
355d07fece6Sratchov 			DPRINTF("sioctl_sun_getfd: %s: number expected after '/'\n", str);
356d07fece6Sratchov 			return -1;
357d07fece6Sratchov 		}
358d07fece6Sratchov 	}
359d07fece6Sratchov 	snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
360d07fece6Sratchov 	if (mode == (SIOCTL_READ | SIOCTL_WRITE))
361d07fece6Sratchov 		flags = O_RDWR;
362d07fece6Sratchov 	else
363d07fece6Sratchov 		flags = (mode & SIOCTL_WRITE) ? O_WRONLY : O_RDONLY;
364d07fece6Sratchov 	while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) < 0) {
365d07fece6Sratchov 		if (errno == EINTR)
366d07fece6Sratchov 			continue;
367d07fece6Sratchov 		DPERROR(path);
368d07fece6Sratchov 		return -1;
369d07fece6Sratchov 	}
370d07fece6Sratchov 	return fd;
371d07fece6Sratchov }
372d07fece6Sratchov 
373d07fece6Sratchov struct sioctl_hdl *
sioctl_sun_fdopen(int fd,unsigned int mode,int nbio)374d07fece6Sratchov sioctl_sun_fdopen(int fd, unsigned int mode, int nbio)
375d07fece6Sratchov {
376d07fece6Sratchov 	struct sioctl_sun_hdl *hdl;
377d07fece6Sratchov 
378d07fece6Sratchov #ifdef DEBUG
379d07fece6Sratchov 	_sndio_debug_init();
380d07fece6Sratchov #endif
381d07fece6Sratchov 	hdl = malloc(sizeof(struct sioctl_sun_hdl));
382d07fece6Sratchov 	if (hdl == NULL)
383d07fece6Sratchov 		return NULL;
384d07fece6Sratchov 	_sioctl_create(&hdl->sioctl, &sioctl_sun_ops, mode, nbio);
385d07fece6Sratchov 	hdl->fd = fd;
386d07fece6Sratchov 	init(hdl);
387d07fece6Sratchov 	return (struct sioctl_hdl *)hdl;
388d07fece6Sratchov }
389d07fece6Sratchov 
390d07fece6Sratchov struct sioctl_hdl *
_sioctl_sun_open(const char * str,unsigned int mode,int nbio)391d07fece6Sratchov _sioctl_sun_open(const char *str, unsigned int mode, int nbio)
392d07fece6Sratchov {
393d07fece6Sratchov 	struct sioctl_hdl *hdl;
394d07fece6Sratchov 	int fd;
395d07fece6Sratchov 
396d07fece6Sratchov 	fd = sioctl_sun_getfd(str, mode, nbio);
397d07fece6Sratchov 	if (fd < 0)
398d07fece6Sratchov 		return NULL;
399d07fece6Sratchov 	hdl = sioctl_sun_fdopen(fd, mode, nbio);
400d07fece6Sratchov 	if (hdl != NULL)
401d07fece6Sratchov 		return hdl;
402d07fece6Sratchov 	while (close(fd) < 0 && errno == EINTR)
403d07fece6Sratchov 		; /* retry */
404d07fece6Sratchov 	return NULL;
405d07fece6Sratchov }
406d07fece6Sratchov 
407d07fece6Sratchov static void
sioctl_sun_close(struct sioctl_hdl * addr)408d07fece6Sratchov sioctl_sun_close(struct sioctl_hdl *addr)
409d07fece6Sratchov {
410d07fece6Sratchov 	struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)addr;
411d07fece6Sratchov 
412d07fece6Sratchov 	close(hdl->fd);
413d07fece6Sratchov 	free(hdl);
414d07fece6Sratchov }
415d07fece6Sratchov 
416d07fece6Sratchov static int
sioctl_sun_ondesc(struct sioctl_hdl * addr)417d07fece6Sratchov sioctl_sun_ondesc(struct sioctl_hdl *addr)
418d07fece6Sratchov {
419d07fece6Sratchov 	struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)addr;
420*4aaef610Sratchov 	struct sioctl_desc desc;
421d07fece6Sratchov 
422d07fece6Sratchov 	if (!scanvol(hdl, &hdl->output) ||
423d07fece6Sratchov 	    !scanvol(hdl, &hdl->input)) {
424d07fece6Sratchov 		hdl->sioctl.eof = 1;
425d07fece6Sratchov 		return 0;
426d07fece6Sratchov 	}
427*4aaef610Sratchov 
428*4aaef610Sratchov 	/* report "server.device" control */
429*4aaef610Sratchov 	memset(&desc, 0, sizeof(struct sioctl_desc));
430*4aaef610Sratchov 	desc.type = SIOCTL_SEL;
431*4aaef610Sratchov 	desc.maxval = 1;
432*4aaef610Sratchov 	strlcpy(desc.func, "device", SIOCTL_NAMEMAX);
433*4aaef610Sratchov 	strlcpy(desc.node0.name, "server", SIOCTL_NAMEMAX);
434*4aaef610Sratchov 	desc.node0.unit = -1;
435*4aaef610Sratchov 	strlcpy(desc.node1.name, "0", SIOCTL_NAMEMAX);
436*4aaef610Sratchov 	desc.node1.unit = -1;
437*4aaef610Sratchov 	strlcpy(desc.display, hdl->display, SIOCTL_DISPLAYMAX);
438*4aaef610Sratchov 	desc.addr = hdl->display_addr;
439*4aaef610Sratchov 	_sioctl_ondesc_cb(&hdl->sioctl, &desc, 1);
440*4aaef610Sratchov 
441d07fece6Sratchov 	_sioctl_ondesc_cb(&hdl->sioctl, NULL, 0);
442d07fece6Sratchov 	return 1;
443d07fece6Sratchov }
444d07fece6Sratchov 
445d07fece6Sratchov static int
sioctl_sun_onval(struct sioctl_hdl * addr)446d07fece6Sratchov sioctl_sun_onval(struct sioctl_hdl *addr)
447d07fece6Sratchov {
448d07fece6Sratchov 	return 1;
449d07fece6Sratchov }
450d07fece6Sratchov 
451d07fece6Sratchov static int
sioctl_sun_setctl(struct sioctl_hdl * arg,unsigned int addr,unsigned int val)452d07fece6Sratchov sioctl_sun_setctl(struct sioctl_hdl *arg, unsigned int addr, unsigned int val)
453d07fece6Sratchov {
454d07fece6Sratchov 	struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)arg;
455d07fece6Sratchov 
456d07fece6Sratchov 	if (!setvol(hdl, &hdl->output, addr, val) ||
457d07fece6Sratchov 	    !setvol(hdl, &hdl->input, addr, val)) {
458d07fece6Sratchov 		hdl->sioctl.eof = 1;
459d07fece6Sratchov 		return 0;
460d07fece6Sratchov 	}
461d07fece6Sratchov 	return 1;
462d07fece6Sratchov }
463d07fece6Sratchov 
464d07fece6Sratchov static int
sioctl_sun_nfds(struct sioctl_hdl * addr)465d07fece6Sratchov sioctl_sun_nfds(struct sioctl_hdl *addr)
466d07fece6Sratchov {
467d07fece6Sratchov 	return 1;
468d07fece6Sratchov }
469d07fece6Sratchov 
470d07fece6Sratchov static int
sioctl_sun_pollfd(struct sioctl_hdl * addr,struct pollfd * pfd,int events)471d07fece6Sratchov sioctl_sun_pollfd(struct sioctl_hdl *addr, struct pollfd *pfd, int events)
472d07fece6Sratchov {
473d07fece6Sratchov 	struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)addr;
474d07fece6Sratchov 
475d07fece6Sratchov 	pfd->fd = hdl->fd;
476d07fece6Sratchov 	pfd->events = POLLIN;
477d07fece6Sratchov 	hdl->events = events;
478d07fece6Sratchov 	return 1;
479d07fece6Sratchov }
480d07fece6Sratchov 
481d07fece6Sratchov static int
sioctl_sun_revents(struct sioctl_hdl * arg,struct pollfd * pfd)482d07fece6Sratchov sioctl_sun_revents(struct sioctl_hdl *arg, struct pollfd *pfd)
483d07fece6Sratchov {
484d07fece6Sratchov 	struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)arg;
485d07fece6Sratchov 	struct volume *vol;
486d07fece6Sratchov 	int idx, n;
487d07fece6Sratchov 
488d07fece6Sratchov 	if (pfd->revents & POLLIN) {
489d07fece6Sratchov 		while (1) {
490d07fece6Sratchov 			n = read(hdl->fd, &idx, sizeof(int));
491d07fece6Sratchov 			if (n == -1) {
492d07fece6Sratchov 				if (errno == EINTR || errno == EAGAIN)
493d07fece6Sratchov 					break;
494d07fece6Sratchov 				DPERROR("read");
495d07fece6Sratchov 				hdl->sioctl.eof = 1;
496d07fece6Sratchov 				return POLLHUP;
497d07fece6Sratchov 			}
498d07fece6Sratchov 			if (n < sizeof(int)) {
499d07fece6Sratchov 				DPRINTF("sioctl_sun_revents: short read\n");
500d07fece6Sratchov 				hdl->sioctl.eof = 1;
501d07fece6Sratchov 				return POLLHUP;
502d07fece6Sratchov 			}
503d07fece6Sratchov 
504d07fece6Sratchov 			if (idx == hdl->output.level_idx ||
505d07fece6Sratchov 			    idx == hdl->output.mute_idx) {
506d07fece6Sratchov 				vol = &hdl->output;
507d07fece6Sratchov 			} else if (idx == hdl->input.level_idx ||
508d07fece6Sratchov 			    idx == hdl->input.mute_idx) {
509d07fece6Sratchov 				vol = &hdl->input;
510d07fece6Sratchov 			} else
511d07fece6Sratchov 				continue;
512d07fece6Sratchov 
513d07fece6Sratchov 			if (!updatevol(hdl, vol, idx))
514d07fece6Sratchov 				return POLLHUP;
515d07fece6Sratchov 		}
516d07fece6Sratchov 	}
517d07fece6Sratchov 	return hdl->events & POLLOUT;
518d07fece6Sratchov }
519