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; 564aaef610Sratchov char display[SIOCTL_DISPLAYMAX]; 574aaef610Sratchov 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 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 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 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 }; 1524aaef610Sratchov 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 } 1714aaef610Sratchov 1724aaef610Sratchov hdl->display_addr = 128; 1734aaef610Sratchov if (ioctl(hdl->fd, AUDIO_GETDEV, &getdev) == -1) 1744aaef610Sratchov strlcpy(hdl->display, "unknown", SIOCTL_DISPLAYMAX); 1754aaef610Sratchov else 1764aaef610Sratchov strlcpy(hdl->display, getdev.name, SIOCTL_DISPLAYMAX); 1774aaef610Sratchov DPRINTF("init: server.device: display = %s\n", hdl->display); 178d07fece6Sratchov } 179d07fece6Sratchov 180d07fece6Sratchov static int 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 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 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 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 * 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 * 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 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 417d07fece6Sratchov sioctl_sun_ondesc(struct sioctl_hdl *addr) 418d07fece6Sratchov { 419d07fece6Sratchov struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)addr; 4204aaef610Sratchov 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 } 4274aaef610Sratchov 4284aaef610Sratchov /* report "server.device" control */ 4294aaef610Sratchov memset(&desc, 0, sizeof(struct sioctl_desc)); 4304aaef610Sratchov desc.type = SIOCTL_SEL; 4314aaef610Sratchov desc.maxval = 1; 4324aaef610Sratchov strlcpy(desc.func, "device", SIOCTL_NAMEMAX); 4334aaef610Sratchov strlcpy(desc.node0.name, "server", SIOCTL_NAMEMAX); 4344aaef610Sratchov desc.node0.unit = -1; 4354aaef610Sratchov strlcpy(desc.node1.name, "0", SIOCTL_NAMEMAX); 4364aaef610Sratchov desc.node1.unit = -1; 4374aaef610Sratchov strlcpy(desc.display, hdl->display, SIOCTL_DISPLAYMAX); 4384aaef610Sratchov desc.addr = hdl->display_addr; 4394aaef610Sratchov _sioctl_ondesc_cb(&hdl->sioctl, &desc, 1); 4404aaef610Sratchov 441d07fece6Sratchov _sioctl_ondesc_cb(&hdl->sioctl, NULL, 0); 442d07fece6Sratchov return 1; 443d07fece6Sratchov } 444d07fece6Sratchov 445d07fece6Sratchov static int 446d07fece6Sratchov sioctl_sun_onval(struct sioctl_hdl *addr) 447d07fece6Sratchov { 448d07fece6Sratchov return 1; 449d07fece6Sratchov } 450d07fece6Sratchov 451d07fece6Sratchov static int 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 465d07fece6Sratchov sioctl_sun_nfds(struct sioctl_hdl *addr) 466d07fece6Sratchov { 467d07fece6Sratchov return 1; 468d07fece6Sratchov } 469d07fece6Sratchov 470d07fece6Sratchov static int 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 475*544c3c00Sratchov hdl->events = events; 476*544c3c00Sratchov 477*544c3c00Sratchov /* 478*544c3c00Sratchov * The audio(4) driver doesn't support POLLOUT, so if it is 479*544c3c00Sratchov * requested, don't set the struct pollfd. The AUDIO_MIXER_WRITE 480*544c3c00Sratchov * ioctl never blocks, so just return POLLOUT in sioctl_sun_revents(). 481*544c3c00Sratchov */ 482*544c3c00Sratchov if (events & POLLOUT) 483*544c3c00Sratchov return 0; 484*544c3c00Sratchov 485d07fece6Sratchov pfd->fd = hdl->fd; 486d07fece6Sratchov pfd->events = POLLIN; 487d07fece6Sratchov return 1; 488d07fece6Sratchov } 489d07fece6Sratchov 490d07fece6Sratchov static int 491d07fece6Sratchov sioctl_sun_revents(struct sioctl_hdl *arg, struct pollfd *pfd) 492d07fece6Sratchov { 493d07fece6Sratchov struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)arg; 494d07fece6Sratchov struct volume *vol; 495d07fece6Sratchov int idx, n; 496d07fece6Sratchov 497*544c3c00Sratchov if (hdl->events & POLLOUT) 498*544c3c00Sratchov return POLLOUT; 499*544c3c00Sratchov 500d07fece6Sratchov if (pfd->revents & POLLIN) { 501d07fece6Sratchov while (1) { 502d07fece6Sratchov n = read(hdl->fd, &idx, sizeof(int)); 503d07fece6Sratchov if (n == -1) { 504d07fece6Sratchov if (errno == EINTR || errno == EAGAIN) 505d07fece6Sratchov break; 506d07fece6Sratchov DPERROR("read"); 507d07fece6Sratchov hdl->sioctl.eof = 1; 508d07fece6Sratchov return POLLHUP; 509d07fece6Sratchov } 510d07fece6Sratchov if (n < sizeof(int)) { 511d07fece6Sratchov DPRINTF("sioctl_sun_revents: short read\n"); 512d07fece6Sratchov hdl->sioctl.eof = 1; 513d07fece6Sratchov return POLLHUP; 514d07fece6Sratchov } 515d07fece6Sratchov 516d07fece6Sratchov if (idx == hdl->output.level_idx || 517d07fece6Sratchov idx == hdl->output.mute_idx) { 518d07fece6Sratchov vol = &hdl->output; 519d07fece6Sratchov } else if (idx == hdl->input.level_idx || 520d07fece6Sratchov idx == hdl->input.mute_idx) { 521d07fece6Sratchov vol = &hdl->input; 522d07fece6Sratchov } else 523d07fece6Sratchov continue; 524d07fece6Sratchov 525d07fece6Sratchov if (!updatevol(hdl, vol, idx)) 526d07fece6Sratchov return POLLHUP; 527d07fece6Sratchov } 528d07fece6Sratchov } 529*544c3c00Sratchov return 0; 530d07fece6Sratchov } 531