blob: 176d121e9cf1fda723b0ae69ebc8a1c5854adfc4 [file] [log] [blame]
Ram Chandrasekardac8f7d2020-06-10 12:23:28 -07001/*
2 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <unistd.h>
32#include <poll.h>
33#include <sys/socket.h>
34#include <linux/types.h>
35#include <linux/netlink.h>
36#include <android-base/logging.h>
37#include <android-base/properties.h>
38#include <android-base/stringprintf.h>
39
40#include "thermalMonitor.h"
41
42#define UEVENT_BUF 1024
43
44#define HYST_FMT "change@/devices/virtual/thermal/thermal_zone%d\n\
45 ACTION=change\n\
46 DEVPATH=/devices/virtual/thermal/thermal_zone%d\n\
47 SUBSYSTEM=thermal\n\
48 NAME=%s\n\
49 TEMP=%d\n\
50 HYST=%d\n\
51 EVENT=%d\n"\
52
53#define TRIP_FMT "change@/devices/virtual/thermal/thermal_zone%d\n\
54 ACTION=change\n\
55 DEVPATH=/devices/virtual/thermal/thermal_zone%d\n\
56 SUBSYSTEM=thermal\n\
57 NAME=%s\n\
58 TEMP=%d\n\
59 TRIP=%d\n\
60 EVENT=%d\n"\
61
62namespace android {
63namespace hardware {
64namespace thermal {
65namespace V2_0 {
66namespace implementation {
67
68using parseCB = std::function<void(char *inp_buf, ssize_t len)>;
69using pollCB = std::function<bool()>;
70
71void thermal_monitor_uevent(const parseCB &parse_cb, const pollCB &stopPollCB)
72{
73 struct pollfd pfd;
74 char buf[UEVENT_BUF] = {0};
75 int sz = 64*1024;
76 struct sockaddr_nl nls;
77
78 memset(&nls, 0, sizeof(nls));
79 nls.nl_family = AF_NETLINK;
80 nls.nl_pid = getpid();
81 nls.nl_groups = 0xffffffff;
82
83 pfd.events = POLLIN;
84 pfd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
85 NETLINK_KOBJECT_UEVENT);
86 if (pfd.fd < 0) {
87 LOG(ERROR) << "socket creation error:" << errno << std::endl;
88 return;
89 }
90 LOG(DEBUG) << "socket creation success" << std::endl;
91
92 setsockopt(pfd.fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz));
93 if (bind(pfd.fd, (struct sockaddr *)&nls, sizeof(nls)) < 0) {
94 close(pfd.fd);
95 LOG(ERROR) << "socket bind failed:" << errno << std::endl;
96 return;
97 }
98 LOG(DEBUG) << "Listening for uevent" << std::endl;
99
100 while (!stopPollCB()) {
101 ssize_t len;
102 int err;
103
104 err = poll(&pfd, 1, -1);
105 if (err == -1) {
106 LOG(ERROR) << "Error in uevent poll.";
107 break;
108 }
109 if (stopPollCB()) {
110 LOG(INFO) << "Exiting uevent monitor" << std::endl;
111 return;
112 }
113 len = recv(pfd.fd, buf, sizeof(buf) - 1, MSG_DONTWAIT);
114 if (len == -1) {
115 LOG(ERROR) << "uevent read failed:" << errno << std::endl;
116 continue;
117 }
118 buf[len] = '\0';
119
120 parse_cb(buf, len);
121 }
122
123 return;
124}
125
126ThermalMonitor::ThermalMonitor(const ueventMonitorCB &inp_cb):
127 cb(inp_cb)
128{
129 monitor_shutdown = false;
130}
131
132ThermalMonitor::~ThermalMonitor()
133{
134 monitor_shutdown = true;
135 th.join();
136}
137
138void ThermalMonitor::start()
139{
140 th = std::thread(thermal_monitor_uevent,
141 std::bind(&ThermalMonitor::parse_and_notify, this,
142 std::placeholders::_1, std::placeholders::_2),
143 std::bind(&ThermalMonitor::stopPolling, this));
144}
145
146void ThermalMonitor::parse_and_notify(char *inp_buf, ssize_t len)
147{
148 int zone_num, temp, trip, ret = 0, event;
149 ssize_t i = 0;
150 char sensor_name[30] = "", buf[UEVENT_BUF] = {0};
151
152 LOG(DEBUG) << "monitor received thermal uevent: " << inp_buf
153 << std::endl;
154
155 while (i < len) {
156 if (i >= UEVENT_BUF)
157 return;
158 ret = snprintf(buf + i, UEVENT_BUF - i, "%s ", inp_buf + i);
159 if (ret == (strlen(inp_buf + i) + 1))
160 i += ret;
161 else
162 return;
163 }
164
165 if (!strstr(buf, "SUBSYSTEM=thermal"))
166 return;
167
168 if (strstr(buf, "TRIP=")) {
169 ret = sscanf(buf, TRIP_FMT, &zone_num, &zone_num, sensor_name,
170 &temp, &trip, &event);
171 LOG(DEBUG) << "zone:" << zone_num << " sensor:" << sensor_name
172 <<" temp:" << temp << " trip:" << trip << " event:" <<
173 event << std::endl;
174 } else {
175 ret = sscanf(buf, HYST_FMT, &zone_num, &zone_num, sensor_name,
176 &temp, &trip, &event);
177 LOG(DEBUG) << "zone:" << zone_num << " sensor:" << sensor_name
178 <<" temp:" << temp << " trip:" << trip << " event:" <<
179 event << std::endl;
180 }
181 if (ret <= 0 || ret == EOF) {
182 LOG(ERROR) << "read error:" << ret <<". buf:" << buf << std::endl;
183 return;
184 }
185 cb(sensor_name, temp);
186}
187
188} // namespace implementation
189} // namespace V2_0
190} // namespace thermal
191} // namespace hardware
192} // namespace android