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