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#if HAVE_NOTIFY_FD 96 RemoveNotifyFd(sna->acpi.fd); 97#else 98 RemoveGeneralSocket(sna->acpi.fd); 99#endif 100 sna_acpi_fini(sna); 101 return; 102 } 103 104 sna->acpi.event[sna->acpi.offset + n] = '\0'; 105 sna->acpi.offset += n; 106 sna->acpi.remain -= n; 107 108 DBG(("%s: event string [%d]: '%s'\n", __FUNCTION__, sna->acpi.offset, sna->acpi.event)); 109 110 do { 111 eol = strchr(sna->acpi.event, '\n'); 112 if (eol == NULL) 113 return; 114 115 if (strncmp(sna->acpi.event, "ac_adapter", 10) == 0) { 116 char *space = sna->acpi.event; 117 int state = -1; 118 119 /* ac_adapter ACAD 00000080 00000001 */ 120 121 space = strchr(space, ' '); 122 if (space) 123 space = strchr(space + 1, ' '); 124 if (space) 125 space = strchr(space + 1, ' '); 126 if (space) 127 state = atoi(space + 1); 128 129 DBG(("%s: ac_adapter event new state=%d\n", __FUNCTION__, state)); 130 if (state) 131 sna->flags &= ~SNA_POWERSAVE; 132 else 133 sna->flags |= SNA_POWERSAVE; 134 } 135 136 n = (sna->acpi.event + sna->acpi.offset) - ++eol; 137 memmove(sna->acpi.event, eol, n+1); 138 sna->acpi.offset = n; 139 sna->acpi.remain = sizeof(sna->acpi.event) - 1 - n; 140 } while (n); 141} 142 143static int read_power_state(const char *path) 144{ 145 DIR *dir; 146 struct dirent *de; 147 int i = -1; 148 149 DBG(("%s: searching '%s'\n", __FUNCTION__, path)); 150 151 dir = opendir(path); 152 if (dir == NULL) 153 return -1; 154 155 while ((de = readdir(dir))) { 156 char buf[1024]; 157 int fd; 158 159 if (*de->d_name == '.') 160 continue; 161 162 DBG(("%s: checking '%s'\n", __FUNCTION__, de->d_name)); 163 164 snprintf(buf, sizeof(buf), "%s/%s/type", path, de->d_name); 165 fd = open(buf, 0); 166 if (fd < 0) 167 continue; 168 169 i = read(fd, buf, sizeof(buf)); 170 buf[i > 0 ? i - 1: 0] = '\0'; 171 close(fd); 172 173 DBG(("%s: %s is of type '%s'\n", __FUNCTION__, de->d_name, buf)); 174 175 if (strcmp(buf, "Mains")) 176 continue; 177 178 snprintf(buf, sizeof(buf), "%s/%s/online", path, de->d_name); 179 fd = open(buf, 0); 180 if (fd < 0) 181 continue; 182 183 i = read(fd, buf, sizeof(buf)); 184 buf[i > 0 ? i - 1: 0] = '\0'; 185 if (i > 0) 186 i = atoi(buf); 187 DBG(("%s: %s is online? '%s'\n", __FUNCTION__, de->d_name, buf)); 188 close(fd); 189 190 break; 191 } 192 closedir(dir); 193 194 return i; 195} 196 197void sna_acpi_init(struct sna *sna) 198{ 199 if (sna->acpi.fd < 0) 200 return; 201 202 if (sna->flags & SNA_PERFORMANCE) 203 return; 204 205 DBG(("%s: attaching to acpid\n", __FUNCTION__)); 206 207#if HAVE_NOTIFY_FD 208 SetNotifyFd(sna->acpi.fd, NULL, X_NOTIFY_NONE, NULL); 209#else 210 AddGeneralSocket(sna->acpi.fd); 211#endif 212 sna->acpi.remain = sizeof(sna->acpi.event) - 1; 213 sna->acpi.offset = 0; 214 215 /* Read initial states */ 216 if (read_power_state("/sys/class/power_supply") == 0) { 217 DBG(("%s: AC adapter is currently offline\n", __FUNCTION__)); 218 sna->flags |= SNA_POWERSAVE; 219 } 220} 221 222void sna_acpi_fini(struct sna *sna) 223{ 224 if (sna->acpi.fd < 0) 225 return; 226 227 close(sna->acpi.fd); 228 sna->acpi.fd = -1; 229 230 sna->flags &= ~SNA_POWERSAVE; 231} 232