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