1428d7b3dSmrg/*
2428d7b3dSmrg * Copyright (c) 2013 Intel Corporation
3428d7b3dSmrg *
4428d7b3dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5428d7b3dSmrg * copy of this software and associated documentation files (the "Software"),
6428d7b3dSmrg * to deal in the Software without restriction, including without limitation
7428d7b3dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8428d7b3dSmrg * and/or sell copies of the Software, and to permit persons to whom the
9428d7b3dSmrg * Software is furnished to do so, subject to the following conditions:
10428d7b3dSmrg *
11428d7b3dSmrg * The above copyright notice and this permission notice (including the next
12428d7b3dSmrg * paragraph) shall be included in all copies or substantial portions of the
13428d7b3dSmrg * Software.
14428d7b3dSmrg *
15428d7b3dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16428d7b3dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17428d7b3dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18428d7b3dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19428d7b3dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20428d7b3dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21428d7b3dSmrg * SOFTWARE.
22428d7b3dSmrg *
23428d7b3dSmrg * Authors:
24428d7b3dSmrg *    Chris Wilson <chris@chris-wilson.co.uk>
25428d7b3dSmrg *
26428d7b3dSmrg */
27428d7b3dSmrg
28428d7b3dSmrg#ifdef HAVE_CONFIG_H
29428d7b3dSmrg#include "config.h"
30428d7b3dSmrg#endif
31428d7b3dSmrg
32428d7b3dSmrg#include <sys/types.h>
33428d7b3dSmrg#include <sys/socket.h>
34428d7b3dSmrg#include <sys/un.h>
35428d7b3dSmrg#include <unistd.h>
36428d7b3dSmrg#include <dirent.h>
37428d7b3dSmrg#include <fcntl.h>
38428d7b3dSmrg#include <errno.h>
39428d7b3dSmrg
40428d7b3dSmrg#include "sna.h"
41428d7b3dSmrg
42428d7b3dSmrg#define ACPI_SOCKET  "/var/run/acpid.socket"
43428d7b3dSmrg
44428d7b3dSmrgint sna_acpi_open(void)
45428d7b3dSmrg{
46428d7b3dSmrg	struct sockaddr_un addr;
47428d7b3dSmrg	int fd, ret;
48428d7b3dSmrg
49428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
50428d7b3dSmrg
51428d7b3dSmrg	fd = socket(AF_UNIX, SOCK_STREAM, 0);
52428d7b3dSmrg	if (fd < 0)
53428d7b3dSmrg		return -1;
54428d7b3dSmrg
55428d7b3dSmrg	memset(&addr, 0, sizeof(addr));
56428d7b3dSmrg	addr.sun_family = AF_UNIX;
57428d7b3dSmrg	strcpy(addr.sun_path, ACPI_SOCKET);
58428d7b3dSmrg
59428d7b3dSmrg	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
60428d7b3dSmrg	if (ret < 0) {
61428d7b3dSmrg		close(fd);
62428d7b3dSmrg		return -1;
63428d7b3dSmrg	}
64428d7b3dSmrg
65428d7b3dSmrg	DBG(("%s: opened socket to APCI daemon, fd=%d\n", __FUNCTION__, fd));
66428d7b3dSmrg
67428d7b3dSmrg	return fd;
68428d7b3dSmrg}
69428d7b3dSmrg
70428d7b3dSmrgvoid _sna_acpi_wakeup(struct sna *sna)
71428d7b3dSmrg{
72428d7b3dSmrg	char *eol;
73428d7b3dSmrg	int n;
74428d7b3dSmrg
75428d7b3dSmrg	n = read(sna->acpi.fd,
76428d7b3dSmrg		 sna->acpi.event + sna->acpi.offset,
77428d7b3dSmrg		 sna->acpi.remain);
78428d7b3dSmrg	DBG(("%s: read %d bytes from acpid\n", __FUNCTION__, n));
79428d7b3dSmrg	if (n <= 0) {
80428d7b3dSmrg		/* We will get '0' if we run out of space whilst reading
81428d7b3dSmrg		 * one event - that should never happen, so treat it as
82428d7b3dSmrg		 * an error and give up.
83428d7b3dSmrg		 */
84428d7b3dSmrg		if (n < 0)
85428d7b3dSmrg			n = errno;
86428d7b3dSmrg		switch (n) {
87428d7b3dSmrg		case EAGAIN:
88428d7b3dSmrg		case EINTR:
89428d7b3dSmrg			return;
90428d7b3dSmrg		}
91428d7b3dSmrg
92428d7b3dSmrg		DBG(("%s: error [%d], detaching from acpid\n", __FUNCTION__, n));
93428d7b3dSmrg
94428d7b3dSmrg		/* XXX reattach later? */
95428d7b3dSmrg#if HAVE_NOTIFY_FD
96428d7b3dSmrg		RemoveNotifyFd(sna->acpi.fd);
97428d7b3dSmrg#else
98428d7b3dSmrg		RemoveGeneralSocket(sna->acpi.fd);
99428d7b3dSmrg#endif
100428d7b3dSmrg		sna_acpi_fini(sna);
101428d7b3dSmrg		return;
102428d7b3dSmrg	}
103428d7b3dSmrg
104428d7b3dSmrg	sna->acpi.event[sna->acpi.offset + n] = '\0';
105428d7b3dSmrg	sna->acpi.offset += n;
106428d7b3dSmrg	sna->acpi.remain -= n;
107428d7b3dSmrg
108428d7b3dSmrg	DBG(("%s: event string [%d]: '%s'\n", __FUNCTION__, sna->acpi.offset, sna->acpi.event));
109428d7b3dSmrg
110428d7b3dSmrg	do {
111428d7b3dSmrg		eol = strchr(sna->acpi.event, '\n');
112428d7b3dSmrg		if (eol == NULL)
113428d7b3dSmrg			return;
114428d7b3dSmrg
115428d7b3dSmrg		if (strncmp(sna->acpi.event, "ac_adapter", 10) == 0) {
116428d7b3dSmrg			char *space = sna->acpi.event;
117428d7b3dSmrg			int state = -1;
118428d7b3dSmrg
119428d7b3dSmrg			/* ac_adapter ACAD 00000080 00000001 */
120428d7b3dSmrg
121428d7b3dSmrg			space = strchr(space, ' ');
122428d7b3dSmrg			if (space)
123428d7b3dSmrg				space = strchr(space + 1, ' ');
124428d7b3dSmrg			if (space)
125428d7b3dSmrg				space = strchr(space + 1, ' ');
126428d7b3dSmrg			if (space)
127428d7b3dSmrg				state = atoi(space + 1);
128428d7b3dSmrg
129428d7b3dSmrg			DBG(("%s: ac_adapter event new state=%d\n", __FUNCTION__, state));
130428d7b3dSmrg			if (state)
131428d7b3dSmrg				sna->flags &= ~SNA_POWERSAVE;
132428d7b3dSmrg			else
133428d7b3dSmrg				sna->flags |= SNA_POWERSAVE;
134428d7b3dSmrg		}
135428d7b3dSmrg
136428d7b3dSmrg		n = (sna->acpi.event + sna->acpi.offset) - ++eol;
137428d7b3dSmrg		memmove(sna->acpi.event, eol, n+1);
138428d7b3dSmrg		sna->acpi.offset = n;
139428d7b3dSmrg		sna->acpi.remain = sizeof(sna->acpi.event) - 1 - n;
140428d7b3dSmrg	} while (n);
141428d7b3dSmrg}
142428d7b3dSmrg
143428d7b3dSmrgstatic int read_power_state(const char *path)
144428d7b3dSmrg{
145428d7b3dSmrg	DIR *dir;
146428d7b3dSmrg	struct dirent *de;
147428d7b3dSmrg	int i = -1;
148428d7b3dSmrg
149428d7b3dSmrg	DBG(("%s: searching '%s'\n", __FUNCTION__, path));
150428d7b3dSmrg
151428d7b3dSmrg	dir = opendir(path);
152428d7b3dSmrg	if (dir == NULL)
153428d7b3dSmrg		return -1;
154428d7b3dSmrg
155428d7b3dSmrg	while ((de = readdir(dir))) {
156428d7b3dSmrg		char buf[1024];
157428d7b3dSmrg		int fd;
158428d7b3dSmrg
159428d7b3dSmrg		if (*de->d_name == '.')
160428d7b3dSmrg			continue;
161428d7b3dSmrg
162428d7b3dSmrg		DBG(("%s: checking '%s'\n", __FUNCTION__, de->d_name));
163428d7b3dSmrg
164428d7b3dSmrg		snprintf(buf, sizeof(buf), "%s/%s/type", path, de->d_name);
165428d7b3dSmrg		fd = open(buf, 0);
166428d7b3dSmrg		if (fd < 0)
167428d7b3dSmrg			continue;
168428d7b3dSmrg
169428d7b3dSmrg		i = read(fd, buf, sizeof(buf));
170428d7b3dSmrg		buf[i > 0 ? i - 1: 0] = '\0';
171428d7b3dSmrg		close(fd);
172428d7b3dSmrg
173428d7b3dSmrg		DBG(("%s: %s is of type '%s'\n", __FUNCTION__, de->d_name, buf));
174428d7b3dSmrg
175428d7b3dSmrg		if (strcmp(buf, "Mains"))
176428d7b3dSmrg			continue;
177428d7b3dSmrg
178428d7b3dSmrg		snprintf(buf, sizeof(buf), "%s/%s/online", path, de->d_name);
179428d7b3dSmrg		fd = open(buf, 0);
180428d7b3dSmrg		if (fd < 0)
181428d7b3dSmrg			continue;
182428d7b3dSmrg
183428d7b3dSmrg		i = read(fd, buf, sizeof(buf));
184428d7b3dSmrg		buf[i > 0 ? i - 1: 0] = '\0';
185428d7b3dSmrg		if (i > 0)
186428d7b3dSmrg			i = atoi(buf);
187428d7b3dSmrg		DBG(("%s: %s is online? '%s'\n", __FUNCTION__, de->d_name, buf));
188428d7b3dSmrg		close(fd);
189428d7b3dSmrg
190428d7b3dSmrg		break;
191428d7b3dSmrg	}
192428d7b3dSmrg	closedir(dir);
193428d7b3dSmrg
194428d7b3dSmrg	return i;
195428d7b3dSmrg}
196428d7b3dSmrg
197428d7b3dSmrgvoid sna_acpi_init(struct sna *sna)
198428d7b3dSmrg{
199428d7b3dSmrg	if (sna->acpi.fd < 0)
200428d7b3dSmrg		return;
201428d7b3dSmrg
202428d7b3dSmrg	if (sna->flags & SNA_PERFORMANCE)
203428d7b3dSmrg		return;
204428d7b3dSmrg
205428d7b3dSmrg	DBG(("%s: attaching to acpid\n", __FUNCTION__));
206428d7b3dSmrg
207428d7b3dSmrg#if HAVE_NOTIFY_FD
208428d7b3dSmrg	SetNotifyFd(sna->acpi.fd, NULL, X_NOTIFY_NONE, NULL);
209428d7b3dSmrg#else
210428d7b3dSmrg	AddGeneralSocket(sna->acpi.fd);
211428d7b3dSmrg#endif
212428d7b3dSmrg	sna->acpi.remain = sizeof(sna->acpi.event) - 1;
213428d7b3dSmrg	sna->acpi.offset = 0;
214428d7b3dSmrg
215428d7b3dSmrg	/* Read initial states */
216428d7b3dSmrg	if (read_power_state("/sys/class/power_supply") == 0) {
217428d7b3dSmrg		DBG(("%s: AC adapter is currently offline\n", __FUNCTION__));
218428d7b3dSmrg		sna->flags |= SNA_POWERSAVE;
219428d7b3dSmrg	}
220428d7b3dSmrg}
221428d7b3dSmrg
222428d7b3dSmrgvoid sna_acpi_fini(struct sna *sna)
223428d7b3dSmrg{
224428d7b3dSmrg	if (sna->acpi.fd < 0)
225428d7b3dSmrg		return;
226428d7b3dSmrg
227428d7b3dSmrg	close(sna->acpi.fd);
228428d7b3dSmrg	sna->acpi.fd = -1;
229428d7b3dSmrg
230428d7b3dSmrg	sna->flags &= ~SNA_POWERSAVE;
231428d7b3dSmrg}
232