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