142542f5fSchristos/* 242542f5fSchristos * Copyright (c) 2013 Intel Corporation 342542f5fSchristos * 442542f5fSchristos * Permission is hereby granted, free of charge, to any person obtaining a 542542f5fSchristos * copy of this software and associated documentation files (the "Software"), 642542f5fSchristos * to deal in the Software without restriction, including without limitation 742542f5fSchristos * the rights to use, copy, modify, merge, publish, distribute, sublicense, 842542f5fSchristos * and/or sell copies of the Software, and to permit persons to whom the 942542f5fSchristos * Software is furnished to do so, subject to the following conditions: 1042542f5fSchristos * 1142542f5fSchristos * The above copyright notice and this permission notice (including the next 1242542f5fSchristos * paragraph) shall be included in all copies or substantial portions of the 1342542f5fSchristos * Software. 1442542f5fSchristos * 1542542f5fSchristos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1642542f5fSchristos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1742542f5fSchristos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1842542f5fSchristos * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1942542f5fSchristos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2042542f5fSchristos * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 2142542f5fSchristos * SOFTWARE. 2242542f5fSchristos * 2342542f5fSchristos * Authors: 2442542f5fSchristos * Chris Wilson <chris@chris-wilson.co.uk> 2542542f5fSchristos * 2642542f5fSchristos */ 2742542f5fSchristos 2842542f5fSchristos#ifdef HAVE_CONFIG_H 2942542f5fSchristos#include "config.h" 3042542f5fSchristos#endif 3142542f5fSchristos 3242542f5fSchristos#include <sys/types.h> 3342542f5fSchristos#include <sys/socket.h> 3442542f5fSchristos#include <sys/un.h> 3542542f5fSchristos#include <unistd.h> 3642542f5fSchristos#include <dirent.h> 3742542f5fSchristos#include <fcntl.h> 3842542f5fSchristos#include <errno.h> 3942542f5fSchristos 4042542f5fSchristos#include "sna.h" 4142542f5fSchristos 4242542f5fSchristos#define ACPI_SOCKET "/var/run/acpid.socket" 4342542f5fSchristos 4442542f5fSchristosint sna_acpi_open(void) 4542542f5fSchristos{ 4642542f5fSchristos struct sockaddr_un addr; 4742542f5fSchristos int fd, ret; 4842542f5fSchristos 4942542f5fSchristos DBG(("%s\n", __FUNCTION__)); 5042542f5fSchristos 5142542f5fSchristos fd = socket(AF_UNIX, SOCK_STREAM, 0); 5242542f5fSchristos if (fd < 0) 5342542f5fSchristos return -1; 5442542f5fSchristos 5542542f5fSchristos memset(&addr, 0, sizeof(addr)); 5642542f5fSchristos addr.sun_family = AF_UNIX; 5742542f5fSchristos strcpy(addr.sun_path, ACPI_SOCKET); 5842542f5fSchristos 5942542f5fSchristos ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); 6042542f5fSchristos if (ret < 0) { 6142542f5fSchristos close(fd); 6242542f5fSchristos return -1; 6342542f5fSchristos } 6442542f5fSchristos 6542542f5fSchristos DBG(("%s: opened socket to APCI daemon, fd=%d\n", __FUNCTION__, fd)); 6642542f5fSchristos 6742542f5fSchristos return fd; 6842542f5fSchristos} 6942542f5fSchristos 7042542f5fSchristosvoid _sna_acpi_wakeup(struct sna *sna) 7142542f5fSchristos{ 7242542f5fSchristos char *eol; 7342542f5fSchristos int n; 7442542f5fSchristos 7542542f5fSchristos n = read(sna->acpi.fd, 7642542f5fSchristos sna->acpi.event + sna->acpi.offset, 7742542f5fSchristos sna->acpi.remain); 7842542f5fSchristos DBG(("%s: read %d bytes from acpid\n", __FUNCTION__, n)); 7942542f5fSchristos if (n <= 0) { 8042542f5fSchristos /* We will get '0' if we run out of space whilst reading 8142542f5fSchristos * one event - that should never happen, so treat it as 8242542f5fSchristos * an error and give up. 8342542f5fSchristos */ 8442542f5fSchristos if (n < 0) 8542542f5fSchristos n = errno; 8642542f5fSchristos switch (n) { 8742542f5fSchristos case EAGAIN: 8842542f5fSchristos case EINTR: 8942542f5fSchristos return; 9042542f5fSchristos } 9142542f5fSchristos 9242542f5fSchristos DBG(("%s: error [%d], detaching from acpid\n", __FUNCTION__, n)); 9342542f5fSchristos 9442542f5fSchristos /* XXX reattach later? */ 9546edf8f1Smrg RemoveNotifyFd(sna->acpi.fd); 9642542f5fSchristos sna_acpi_fini(sna); 9742542f5fSchristos return; 9842542f5fSchristos } 9942542f5fSchristos 10042542f5fSchristos sna->acpi.event[sna->acpi.offset + n] = '\0'; 10142542f5fSchristos sna->acpi.offset += n; 10242542f5fSchristos sna->acpi.remain -= n; 10342542f5fSchristos 10442542f5fSchristos DBG(("%s: event string [%d]: '%s'\n", __FUNCTION__, sna->acpi.offset, sna->acpi.event)); 10542542f5fSchristos 10642542f5fSchristos do { 10742542f5fSchristos eol = strchr(sna->acpi.event, '\n'); 10842542f5fSchristos if (eol == NULL) 10942542f5fSchristos return; 11042542f5fSchristos 11142542f5fSchristos if (strncmp(sna->acpi.event, "ac_adapter", 10) == 0) { 11242542f5fSchristos char *space = sna->acpi.event; 11342542f5fSchristos int state = -1; 11442542f5fSchristos 11542542f5fSchristos /* ac_adapter ACAD 00000080 00000001 */ 11642542f5fSchristos 11742542f5fSchristos space = strchr(space, ' '); 11842542f5fSchristos if (space) 11942542f5fSchristos space = strchr(space + 1, ' '); 12042542f5fSchristos if (space) 12142542f5fSchristos space = strchr(space + 1, ' '); 12242542f5fSchristos if (space) 12342542f5fSchristos state = atoi(space + 1); 12442542f5fSchristos 12542542f5fSchristos DBG(("%s: ac_adapter event new state=%d\n", __FUNCTION__, state)); 12642542f5fSchristos if (state) 12742542f5fSchristos sna->flags &= ~SNA_POWERSAVE; 12842542f5fSchristos else 12942542f5fSchristos sna->flags |= SNA_POWERSAVE; 13042542f5fSchristos } 13142542f5fSchristos 13242542f5fSchristos n = (sna->acpi.event + sna->acpi.offset) - ++eol; 13342542f5fSchristos memmove(sna->acpi.event, eol, n+1); 13442542f5fSchristos sna->acpi.offset = n; 13542542f5fSchristos sna->acpi.remain = sizeof(sna->acpi.event) - 1 - n; 13642542f5fSchristos } while (n); 13742542f5fSchristos} 13842542f5fSchristos 13963ef14f0Smrg#if HAVE_NOTIFY_FD 14063ef14f0Smrgstatic void sna_acpi_notify(int fd, int read, void *data) 14163ef14f0Smrg{ 14263ef14f0Smrg _sna_acpi_wakeup(data); 14363ef14f0Smrg} 14463ef14f0Smrg#endif 14563ef14f0Smrg 14642542f5fSchristosstatic int read_power_state(const char *path) 14742542f5fSchristos{ 14842542f5fSchristos DIR *dir; 14942542f5fSchristos struct dirent *de; 15042542f5fSchristos int i = -1; 15142542f5fSchristos 15242542f5fSchristos DBG(("%s: searching '%s'\n", __FUNCTION__, path)); 15342542f5fSchristos 15442542f5fSchristos dir = opendir(path); 15542542f5fSchristos if (dir == NULL) 15642542f5fSchristos return -1; 15742542f5fSchristos 15842542f5fSchristos while ((de = readdir(dir))) { 15942542f5fSchristos char buf[1024]; 16042542f5fSchristos int fd; 16142542f5fSchristos 16242542f5fSchristos if (*de->d_name == '.') 16342542f5fSchristos continue; 16442542f5fSchristos 16542542f5fSchristos DBG(("%s: checking '%s'\n", __FUNCTION__, de->d_name)); 16642542f5fSchristos 16742542f5fSchristos snprintf(buf, sizeof(buf), "%s/%s/type", path, de->d_name); 16842542f5fSchristos fd = open(buf, 0); 16942542f5fSchristos if (fd < 0) 17042542f5fSchristos continue; 17142542f5fSchristos 17242542f5fSchristos i = read(fd, buf, sizeof(buf)); 17342542f5fSchristos buf[i > 0 ? i - 1: 0] = '\0'; 17442542f5fSchristos close(fd); 17542542f5fSchristos 17642542f5fSchristos DBG(("%s: %s is of type '%s'\n", __FUNCTION__, de->d_name, buf)); 17742542f5fSchristos 17842542f5fSchristos if (strcmp(buf, "Mains")) 17942542f5fSchristos continue; 18042542f5fSchristos 18142542f5fSchristos snprintf(buf, sizeof(buf), "%s/%s/online", path, de->d_name); 18242542f5fSchristos fd = open(buf, 0); 18342542f5fSchristos if (fd < 0) 18442542f5fSchristos continue; 18542542f5fSchristos 18642542f5fSchristos i = read(fd, buf, sizeof(buf)); 18742542f5fSchristos buf[i > 0 ? i - 1: 0] = '\0'; 18842542f5fSchristos if (i > 0) 18942542f5fSchristos i = atoi(buf); 19042542f5fSchristos DBG(("%s: %s is online? '%s'\n", __FUNCTION__, de->d_name, buf)); 19142542f5fSchristos close(fd); 19242542f5fSchristos 19342542f5fSchristos break; 19442542f5fSchristos } 19542542f5fSchristos closedir(dir); 19642542f5fSchristos 19742542f5fSchristos return i; 19842542f5fSchristos} 19942542f5fSchristos 20042542f5fSchristosvoid sna_acpi_init(struct sna *sna) 20142542f5fSchristos{ 20242542f5fSchristos if (sna->acpi.fd < 0) 20342542f5fSchristos return; 20442542f5fSchristos 20542542f5fSchristos if (sna->flags & SNA_PERFORMANCE) 20642542f5fSchristos return; 20742542f5fSchristos 20842542f5fSchristos DBG(("%s: attaching to acpid\n", __FUNCTION__)); 20942542f5fSchristos 21063ef14f0Smrg SetNotifyFd(sna->acpi.fd, sna_acpi_notify, X_NOTIFY_READ, sna); 21142542f5fSchristos sna->acpi.remain = sizeof(sna->acpi.event) - 1; 21242542f5fSchristos sna->acpi.offset = 0; 21342542f5fSchristos 21442542f5fSchristos /* Read initial states */ 21542542f5fSchristos if (read_power_state("/sys/class/power_supply") == 0) { 21642542f5fSchristos DBG(("%s: AC adapter is currently offline\n", __FUNCTION__)); 21742542f5fSchristos sna->flags |= SNA_POWERSAVE; 21842542f5fSchristos } 21942542f5fSchristos} 22042542f5fSchristos 22142542f5fSchristosvoid sna_acpi_fini(struct sna *sna) 22242542f5fSchristos{ 22342542f5fSchristos if (sna->acpi.fd < 0) 22442542f5fSchristos return; 22542542f5fSchristos 22642542f5fSchristos close(sna->acpi.fd); 22742542f5fSchristos sna->acpi.fd = -1; 22842542f5fSchristos 22942542f5fSchristos sna->flags &= ~SNA_POWERSAVE; 23042542f5fSchristos} 231