1/* 2 * Copyright (c) 2013 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 * Authors: 24 * Chris Wilson <chris@chris-wilson.co.uk> 25 * 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include <sys/types.h> 33#include <sys/socket.h> 34#include <sys/un.h> 35#include <unistd.h> 36#include <dirent.h> 37#include <fcntl.h> 38#include <errno.h> 39 40#include "sna.h" 41 42#define ACPI_SOCKET "/var/run/acpid.socket" 43 44int sna_acpi_open(void) 45{ 46 struct sockaddr_un addr; 47 int fd, ret; 48 49 DBG(("%s\n", __FUNCTION__)); 50 51 fd = socket(AF_UNIX, SOCK_STREAM, 0); 52 if (fd < 0) 53 return -1; 54 55 memset(&addr, 0, sizeof(addr)); 56 addr.sun_family = AF_UNIX; 57 strcpy(addr.sun_path, ACPI_SOCKET); 58 59 ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); 60 if (ret < 0) { 61 close(fd); 62 return -1; 63 } 64 65 DBG(("%s: opened socket to APCI daemon, fd=%d\n", __FUNCTION__, fd)); 66 67 return fd; 68} 69 70void _sna_acpi_wakeup(struct sna *sna) 71{ 72 char *eol; 73 int n; 74 75 n = read(sna->acpi.fd, 76 sna->acpi.event + sna->acpi.offset, 77 sna->acpi.remain); 78 DBG(("%s: read %d bytes from acpid\n", __FUNCTION__, n)); 79 if (n <= 0) { 80 /* We will get '0' if we run out of space whilst reading 81 * one event - that should never happen, so treat it as 82 * an error and give up. 83 */ 84 if (n < 0) 85 n = errno; 86 switch (n) { 87 case EAGAIN: 88 case EINTR: 89 return; 90 } 91 92 DBG(("%s: error [%d], detaching from acpid\n", __FUNCTION__, n)); 93 94 /* XXX reattach later? */ 95 RemoveNotifyFd(sna->acpi.fd); 96 sna_acpi_fini(sna); 97 return; 98 } 99 100 sna->acpi.event[sna->acpi.offset + n] = '\0'; 101 sna->acpi.offset += n; 102 sna->acpi.remain -= n; 103 104 DBG(("%s: event string [%d]: '%s'\n", __FUNCTION__, sna->acpi.offset, sna->acpi.event)); 105 106 do { 107 eol = strchr(sna->acpi.event, '\n'); 108 if (eol == NULL) 109 return; 110 111 if (strncmp(sna->acpi.event, "ac_adapter", 10) == 0) { 112 char *space = sna->acpi.event; 113 int state = -1; 114 115 /* ac_adapter ACAD 00000080 00000001 */ 116 117 space = strchr(space, ' '); 118 if (space) 119 space = strchr(space + 1, ' '); 120 if (space) 121 space = strchr(space + 1, ' '); 122 if (space) 123 state = atoi(space + 1); 124 125 DBG(("%s: ac_adapter event new state=%d\n", __FUNCTION__, state)); 126 if (state) 127 sna->flags &= ~SNA_POWERSAVE; 128 else 129 sna->flags |= SNA_POWERSAVE; 130 } 131 132 n = (sna->acpi.event + sna->acpi.offset) - ++eol; 133 memmove(sna->acpi.event, eol, n+1); 134 sna->acpi.offset = n; 135 sna->acpi.remain = sizeof(sna->acpi.event) - 1 - n; 136 } while (n); 137} 138 139#if HAVE_NOTIFY_FD 140static void sna_acpi_notify(int fd, int read, void *data) 141{ 142 _sna_acpi_wakeup(data); 143} 144#endif 145 146static int read_power_state(const char *path) 147{ 148 DIR *dir; 149 struct dirent *de; 150 int i = -1; 151 152 DBG(("%s: searching '%s'\n", __FUNCTION__, path)); 153 154 dir = opendir(path); 155 if (dir == NULL) 156 return -1; 157 158 while ((de = readdir(dir))) { 159 char buf[1024]; 160 int fd; 161 162 if (*de->d_name == '.') 163 continue; 164 165 DBG(("%s: checking '%s'\n", __FUNCTION__, de->d_name)); 166 167 snprintf(buf, sizeof(buf), "%s/%s/type", path, de->d_name); 168 fd = open(buf, 0); 169 if (fd < 0) 170 continue; 171 172 i = read(fd, buf, sizeof(buf)); 173 buf[i > 0 ? i - 1: 0] = '\0'; 174 close(fd); 175 176 DBG(("%s: %s is of type '%s'\n", __FUNCTION__, de->d_name, buf)); 177 178 if (strcmp(buf, "Mains")) 179 continue; 180 181 snprintf(buf, sizeof(buf), "%s/%s/online", path, de->d_name); 182 fd = open(buf, 0); 183 if (fd < 0) 184 continue; 185 186 i = read(fd, buf, sizeof(buf)); 187 buf[i > 0 ? i - 1: 0] = '\0'; 188 if (i > 0) 189 i = atoi(buf); 190 DBG(("%s: %s is online? '%s'\n", __FUNCTION__, de->d_name, buf)); 191 close(fd); 192 193 break; 194 } 195 closedir(dir); 196 197 return i; 198} 199 200void sna_acpi_init(struct sna *sna) 201{ 202 if (sna->acpi.fd < 0) 203 return; 204 205 if (sna->flags & SNA_PERFORMANCE) 206 return; 207 208 DBG(("%s: attaching to acpid\n", __FUNCTION__)); 209 210 SetNotifyFd(sna->acpi.fd, sna_acpi_notify, X_NOTIFY_READ, sna); 211 sna->acpi.remain = sizeof(sna->acpi.event) - 1; 212 sna->acpi.offset = 0; 213 214 /* Read initial states */ 215 if (read_power_state("/sys/class/power_supply") == 0) { 216 DBG(("%s: AC adapter is currently offline\n", __FUNCTION__)); 217 sna->flags |= SNA_POWERSAVE; 218 } 219} 220 221void sna_acpi_fini(struct sna *sna) 222{ 223 if (sna->acpi.fd < 0) 224 return; 225 226 close(sna->acpi.fd); 227 sna->acpi.fd = -1; 228 229 sna->flags &= ~SNA_POWERSAVE; 230} 231