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