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		RemoveNotifyFd(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
139#if HAVE_NOTIFY_FD
140static void sna_acpi_notify(int fd, int read, void *data)
141{
142	_sna_acpi_wakeup(data);
143}
144#endif
145
146static int read_power_state(const char *path)
147{
148	DIR *dir;
149	struct dirent *de;
150	int i = -1;
151
152	DBG(("%s: searching '%s'\n", __FUNCTION__, path));
153
154	dir = opendir(path);
155	if (dir == NULL)
156		return -1;
157
158	while ((de = readdir(dir))) {
159		char buf[1024];
160		int fd;
161
162		if (*de->d_name == '.')
163			continue;
164
165		DBG(("%s: checking '%s'\n", __FUNCTION__, de->d_name));
166
167		snprintf(buf, sizeof(buf), "%s/%s/type", path, de->d_name);
168		fd = open(buf, 0);
169		if (fd < 0)
170			continue;
171
172		i = read(fd, buf, sizeof(buf));
173		buf[i > 0 ? i - 1: 0] = '\0';
174		close(fd);
175
176		DBG(("%s: %s is of type '%s'\n", __FUNCTION__, de->d_name, buf));
177
178		if (strcmp(buf, "Mains"))
179			continue;
180
181		snprintf(buf, sizeof(buf), "%s/%s/online", path, de->d_name);
182		fd = open(buf, 0);
183		if (fd < 0)
184			continue;
185
186		i = read(fd, buf, sizeof(buf));
187		buf[i > 0 ? i - 1: 0] = '\0';
188		if (i > 0)
189			i = atoi(buf);
190		DBG(("%s: %s is online? '%s'\n", __FUNCTION__, de->d_name, buf));
191		close(fd);
192
193		break;
194	}
195	closedir(dir);
196
197	return i;
198}
199
200void sna_acpi_init(struct sna *sna)
201{
202	if (sna->acpi.fd < 0)
203		return;
204
205	if (sna->flags & SNA_PERFORMANCE)
206		return;
207
208	DBG(("%s: attaching to acpid\n", __FUNCTION__));
209
210	SetNotifyFd(sna->acpi.fd, sna_acpi_notify, X_NOTIFY_READ, sna);
211	sna->acpi.remain = sizeof(sna->acpi.event) - 1;
212	sna->acpi.offset = 0;
213
214	/* Read initial states */
215	if (read_power_state("/sys/class/power_supply") == 0) {
216		DBG(("%s: AC adapter is currently offline\n", __FUNCTION__));
217		sna->flags |= SNA_POWERSAVE;
218	}
219}
220
221void sna_acpi_fini(struct sna *sna)
222{
223	if (sna->acpi.fd < 0)
224		return;
225
226	close(sna->acpi.fd);
227	sna->acpi.fd = -1;
228
229	sna->flags &= ~SNA_POWERSAVE;
230}
231