volume.c (4249B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <fcntl.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <sys/ioctl.h> 6 #include <unistd.h> 7 8 #include "../util.h" 9 10 #if defined(__OpenBSD__) 11 #include <sys/queue.h> 12 #include <poll.h> 13 #include <sndio.h> 14 #include <stdlib.h> 15 16 struct control { 17 LIST_ENTRY(control) next; 18 unsigned int addr; 19 #define CTRL_NONE 0 20 #define CTRL_LEVEL 1 21 #define CTRL_MUTE 2 22 unsigned int type; 23 unsigned int maxval; 24 unsigned int val; 25 }; 26 27 static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls); 28 static struct pollfd *pfds; 29 static struct sioctl_hdl *hdl; 30 static int initialized; 31 32 /* 33 * Call-back to obtain the description of all audio controls. 34 */ 35 static void 36 ondesc(void *unused, struct sioctl_desc *desc, int val) 37 { 38 struct control *c, *ctmp; 39 unsigned int type = CTRL_NONE; 40 41 if (desc == NULL) 42 return; 43 44 /* Delete existing audio control with the same address. */ 45 LIST_FOREACH_SAFE(c, &controls, next, ctmp) { 46 if (desc->addr == c->addr) { 47 LIST_REMOVE(c, next); 48 free(c); 49 break; 50 } 51 } 52 53 /* Only match output.level and output.mute audio controls. */ 54 if (desc->group[0] != 0 || 55 strcmp(desc->node0.name, "output") != 0) 56 return; 57 if (desc->type == SIOCTL_NUM && 58 strcmp(desc->func, "level") == 0) 59 type = CTRL_LEVEL; 60 else if (desc->type == SIOCTL_SW && 61 strcmp(desc->func, "mute") == 0) 62 type = CTRL_MUTE; 63 else 64 return; 65 66 c = malloc(sizeof(struct control)); 67 if (c == NULL) { 68 warn("sndio: failed to allocate audio control\n"); 69 return; 70 } 71 72 c->addr = desc->addr; 73 c->type = type; 74 c->maxval = desc->maxval; 75 c->val = val; 76 LIST_INSERT_HEAD(&controls, c, next); 77 } 78 79 /* 80 * Call-back invoked whenever an audio control changes. 81 */ 82 static void 83 onval(void *unused, unsigned int addr, unsigned int val) 84 { 85 struct control *c; 86 87 LIST_FOREACH(c, &controls, next) { 88 if (c->addr == addr) 89 break; 90 } 91 c->val = val; 92 } 93 94 static void 95 cleanup(void) 96 { 97 struct control *c; 98 99 if (hdl) { 100 sioctl_close(hdl); 101 hdl = NULL; 102 } 103 104 free(pfds); 105 pfds = NULL; 106 107 while (!LIST_EMPTY(&controls)) { 108 c = LIST_FIRST(&controls); 109 LIST_REMOVE(c, next); 110 free(c); 111 } 112 } 113 114 static int 115 init(void) 116 { 117 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0); 118 if (hdl == NULL) { 119 warn("sndio: cannot open device"); 120 goto failed; 121 } 122 123 if (!sioctl_ondesc(hdl, ondesc, NULL)) { 124 warn("sndio: cannot set control description call-back"); 125 goto failed; 126 } 127 128 if (!sioctl_onval(hdl, onval, NULL)) { 129 warn("sndio: cannot set control values call-back"); 130 goto failed; 131 } 132 133 pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd)); 134 if (pfds == NULL) { 135 warn("sndio: cannot allocate pollfd structures"); 136 goto failed; 137 } 138 139 return 1; 140 failed: 141 cleanup(); 142 return 0; 143 } 144 145 const char * 146 vol_perc(const char *unused) 147 { 148 struct control *c; 149 int n, v, value; 150 151 if (!initialized) 152 initialized = init(); 153 154 if (hdl == NULL) 155 return NULL; 156 157 n = sioctl_pollfd(hdl, pfds, POLLIN); 158 if (n > 0) { 159 n = poll(pfds, n, 0); 160 if (n > 0) { 161 if (sioctl_revents(hdl, pfds) & POLLHUP) { 162 warn("sndio: disconnected"); 163 cleanup(); 164 return NULL; 165 } 166 } 167 } 168 169 value = 100; 170 LIST_FOREACH(c, &controls, next) { 171 if (c->type == CTRL_MUTE && c->val == 1) 172 value = 0; 173 else if (c->type == CTRL_LEVEL) { 174 v = (c->val * 100 + c->maxval / 2) / c->maxval; 175 /* For multiple channels return the minimum. */ 176 if (v < value) 177 value = v; 178 } 179 } 180 181 return bprintf("%d", value); 182 } 183 #else 184 #include <sys/soundcard.h> 185 186 const char * 187 vol_perc(const char *card) 188 { 189 size_t i; 190 int v, afd, devmask; 191 char *vnames[] = SOUND_DEVICE_NAMES; 192 193 if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) { 194 warn("open '%s':", card); 195 return NULL; 196 } 197 198 if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { 199 warn("ioctl 'SOUND_MIXER_READ_DEVMASK':"); 200 close(afd); 201 return NULL; 202 } 203 for (i = 0; i < LEN(vnames); i++) { 204 if (devmask & (1 << i) && !strcmp("vol", vnames[i])) { 205 if (ioctl(afd, MIXER_READ(i), &v) < 0) { 206 warn("ioctl 'MIXER_READ(%ld)':", i); 207 close(afd); 208 return NULL; 209 } 210 } 211 } 212 213 close(afd); 214 215 return bprintf("%d", v & 0xff); 216 } 217 #endif