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