blob: d1825bde482feaadd2a6a389638bcd76fc927ce7 [file] [log] [blame]
Gilad Arnold1ebd8132012-03-05 10:19:29 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "gpio_handler.h"
6
7#include <fcntl.h>
8#include <string.h>
9#include <sys/stat.h>
10#include <sys/types.h>
11
12#include <base/eintr_wrapper.h>
13#include <base/string_util.h>
14#include <glib.h>
15
16#include "update_engine/utils.h"
17
18using std::string;
19
20namespace chromeos_update_engine {
21
22const char* GpioHandler::dutflaga_dev_name_ = NULL;
23const char* GpioHandler::dutflagb_dev_name_ = NULL;
24
25namespace {
26// Names of udev properties that are linked to the GPIO chip device and identify
27// the two dutflag GPIOs on different boards.
28const char kIdGpioDutflaga[] = "ID_GPIO_DUTFLAGA";
29const char kIdGpioDutflagb[] = "ID_GPIO_DUTFLAGB";
30
31// Scoped closer for udev and udev_enumerate objects.
32// TODO(garnold) chromium-os:26934: it would be nice to generalize the different
33// ScopedFooCloser implementations in update engine using a single template.
34class ScopedUdevCloser {
35 public:
36 explicit ScopedUdevCloser(udev** udev_p) : udev_p_(udev_p) {}
37 ~ScopedUdevCloser() {
38 if (udev_p_ && *udev_p_) {
39 udev_unref(*udev_p_);
40 *udev_p_ = NULL;
41 }
42 }
43 private:
44 struct udev **udev_p_;
45
46 DISALLOW_COPY_AND_ASSIGN(ScopedUdevCloser);
47};
48
49class ScopedUdevEnumerateCloser {
50 public:
51 explicit ScopedUdevEnumerateCloser(udev_enumerate **udev_enum_p) :
52 udev_enum_p_(udev_enum_p) {}
53 ~ScopedUdevEnumerateCloser() {
54 if (udev_enum_p_ && *udev_enum_p_) {
55 udev_enumerate_unref(*udev_enum_p_);
56 *udev_enum_p_ = NULL;
57 }
58 }
59 private:
60 struct udev_enumerate** udev_enum_p_;
61
62 DISALLOW_COPY_AND_ASSIGN(ScopedUdevEnumerateCloser);
63};
64} // namespace {}
65
66bool GpioHandler::IsGpioSignalingTest() {
67 // Peek dut_flaga GPIO state.
68 bool dutflaga_gpio_state;
69 if (!GetDutflagGpioStatus(kDutflagaGpio, &dutflaga_gpio_state)) {
70 LOG(WARNING) << "dutflaga GPIO reading failed, defaulting to non-test mode";
71 return false;
72 }
73
74 LOG(INFO) << "dutflaga GPIO reading: "
75 << (dutflaga_gpio_state ? "on (non-test mode)" : "off (test mode)");
76 return !dutflaga_gpio_state;
77}
78
79bool GpioHandler::GetDutflagGpioDevName(struct udev* udev,
80 const string& gpio_dutflag_str,
81 const char** dutflag_dev_name_p) {
82 CHECK(udev && dutflag_dev_name_p);
83
84 struct udev_enumerate* udev_enum = NULL;
85 int num_gpio_dutflags = 0;
86 const string gpio_dutflag_pattern = "*" + gpio_dutflag_str;
87 int ret;
88
89 // Initialize udev enumerate context and closer.
90 if (!(udev_enum = udev_enumerate_new(udev))) {
91 LOG(ERROR) << "failed to obtain udev enumerate context";
92 return false;
93 }
94 ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
95
96 // Populate filters for find an initialized GPIO chip.
97 if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
98 (ret = udev_enumerate_add_match_sysname(udev_enum,
99 gpio_dutflag_pattern.c_str()))) {
100 LOG(ERROR) << "failed to initialize udev enumerate context (" << ret << ")";
101 return false;
102 }
103
104 // Obtain list of matching devices.
105 if ((ret = udev_enumerate_scan_devices(udev_enum))) {
106 LOG(ERROR) << "udev enumerate context scan failed (error code "
107 << ret << ")";
108 return false;
109 }
110
111 // Iterate over matching devices, obtain GPIO dut_flaga identifier.
112 struct udev_list_entry* list_entry;
113 udev_list_entry_foreach(list_entry,
114 udev_enumerate_get_list_entry(udev_enum)) {
115 // Make sure we're not enumerating more than one device.
116 num_gpio_dutflags++;
117 if (num_gpio_dutflags > 1) {
118 LOG(WARNING) <<
119 "enumerated multiple dutflag GPIOs, ignoring this one";
120 continue;
121 }
122
123 // Obtain device name.
124 const char* dev_name = udev_list_entry_get_name(list_entry);
125 if (!dev_name) {
126 LOG(WARNING) << "enumerated device has a null name string, skipping";
127 continue;
128 }
129
130 // Obtain device object.
131 struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
132 if (!dev) {
133 LOG(WARNING) <<
134 "obtained a null device object for enumerated device, skipping";
135 continue;
136 }
137
138 // Obtain device syspath.
139 const char* dev_syspath = udev_device_get_syspath(dev);
140 if (dev_syspath) {
141 LOG(INFO) << "obtained device syspath: " << dev_syspath;
142 *dutflag_dev_name_p = strdup(dev_syspath);
143 } else {
144 LOG(WARNING) << "could not obtain device syspath";
145 }
146
147 udev_device_unref(dev);
148 }
149
150 return true;
151}
152
153bool GpioHandler::GetDutflagGpioDevNames(string* dutflaga_dev_name_p,
154 string* dutflagb_dev_name_p) {
155 if (!(dutflaga_dev_name_p || dutflagb_dev_name_p))
156 return true; // No output pointers, nothing to do.
157
158 string gpio_dutflaga_str, gpio_dutflagb_str;
159
160 if (!(dutflaga_dev_name_ && dutflagb_dev_name_)) {
161 struct udev* udev = NULL;
162 struct udev_enumerate* udev_enum = NULL;
163 int num_gpio_chips = 0;
164 const char* id_gpio_dutflaga = NULL;
165 const char* id_gpio_dutflagb = NULL;
166 int ret;
167
168 LOG(INFO) << "begin discovery of dut_flaga/b devices";
169
170 // Obtain libudev instance and closer.
171 if (!(udev = udev_new())) {
172 LOG(ERROR) << "failed to obtain libudev instance";
173 return false;
174 }
175 ScopedUdevCloser udev_closer(&udev);
176
177 // Initialize a udev enumerate object and closer with a bounded lifespan.
178 {
179 if (!(udev_enum = udev_enumerate_new(udev))) {
180 LOG(ERROR) << "failed to obtain udev enumerate context";
181 return false;
182 }
183 ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
184
185 // Populate filters for find an initialized GPIO chip.
186 if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
187 (ret = udev_enumerate_add_match_sysname(udev_enum, "gpiochip*")) ||
188 (ret = udev_enumerate_add_match_property(udev_enum,
189 kIdGpioDutflaga, "*")) ||
190 (ret = udev_enumerate_add_match_property(udev_enum,
191 kIdGpioDutflagb, "*"))) {
192 LOG(ERROR) << "failed to initialize udev enumerate context ("
193 << ret << ")";
194 return false;
195 }
196
197 // Obtain list of matching devices.
198 if ((ret = udev_enumerate_scan_devices(udev_enum))) {
199 LOG(ERROR) << "udev enumerate context scan failed (" << ret << ")";
200 return false;
201 }
202
203 // Iterate over matching devices, obtain GPIO dut_flaga identifier.
204 struct udev_list_entry* list_entry;
205 udev_list_entry_foreach(list_entry,
206 udev_enumerate_get_list_entry(udev_enum)) {
207 // Make sure we're not enumerating more than one device.
208 num_gpio_chips++;
209 if (num_gpio_chips > 1) {
210 LOG(WARNING) << "enumerated multiple GPIO chips, ignoring this one";
211 continue;
212 }
213
214 // Obtain device name.
215 const char* dev_name = udev_list_entry_get_name(list_entry);
216 if (!dev_name) {
217 LOG(WARNING) << "enumerated device has a null name string, skipping";
218 continue;
219 }
220
221 // Obtain device object.
222 struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
223 if (!dev) {
224 LOG(WARNING) <<
225 "obtained a null device object for enumerated device, skipping";
226 continue;
227 }
228
229 // Obtain dut_flaga/b identifiers.
230 id_gpio_dutflaga =
231 udev_device_get_property_value(dev, kIdGpioDutflaga);
232 id_gpio_dutflagb =
233 udev_device_get_property_value(dev, kIdGpioDutflagb);
234 if (id_gpio_dutflaga && id_gpio_dutflagb) {
235 LOG(INFO) << "found dut_flaga/b identifiers: a=" << id_gpio_dutflaga
236 << " b=" << id_gpio_dutflagb;
237
238 gpio_dutflaga_str = id_gpio_dutflaga;
239 gpio_dutflagb_str = id_gpio_dutflagb;
240 } else {
241 LOG(ERROR) << "GPIO chip missing dut_flaga/b properties";
242 }
243
244 udev_device_unref(dev);
245 }
246 }
247
248 // Obtain dut_flaga, reusing the same udev instance.
249 if (!dutflaga_dev_name_ && !gpio_dutflaga_str.empty()) {
250 LOG(INFO) << "discovering device for GPIO dut_flaga ";
251 if (!GetDutflagGpioDevName(udev, gpio_dutflaga_str,
252 &dutflaga_dev_name_)) {
253 LOG(ERROR) << "discovery of dut_flaga GPIO device failed";
254 return false;
255 }
256 }
257
258 // Now obtain dut_flagb.
259 if (!dutflagb_dev_name_ && !gpio_dutflagb_str.empty()) {
260 LOG(INFO) << "discovering device for GPIO dut_flagb";
261 if (!GetDutflagGpioDevName(udev, gpio_dutflagb_str,
262 &dutflagb_dev_name_)) {
263 LOG(ERROR) << "discovery of dut_flagb GPIO device failed";
264 return false;
265 }
266 }
267
268 LOG(INFO) << "end discovery of dut_flaga/b devices";
269 }
270
271 // Write cached GPIO dutflag(s) to output strings.
272 if (dutflaga_dev_name_p && dutflaga_dev_name_)
273 *dutflaga_dev_name_p = dutflaga_dev_name_;
274 if (dutflagb_dev_name_p && dutflagb_dev_name_)
275 *dutflagb_dev_name_p = dutflagb_dev_name_;
276
277 return true;
278}
279
280bool GpioHandler::GetDutflagGpioStatus(GpioHandler::DutflagGpioId id,
281 bool* status_p) {
282 CHECK(status_p);
283
284 // Obtain GPIO device file name.
285 string dutflag_dev_name;
286 switch (id) {
287 case kDutflagaGpio:
288 GetDutflagGpioDevNames(&dutflag_dev_name, NULL);
289 break;
290 case kDutflagbGpio:
291 GetDutflagGpioDevNames(NULL, &dutflag_dev_name);
292 break;
293 default:
294 LOG(FATAL) << "invalid dutflag GPIO id: " << id;
295 }
296
297 if (dutflag_dev_name.empty()) {
298 LOG(WARNING) << "could not find dutflag GPIO device";
299 return false;
300 }
301
302 // Open device for reading.
303 string dutflaga_value_dev_name = dutflag_dev_name + "/value";
304 int dutflaga_fd = HANDLE_EINTR(open(dutflaga_value_dev_name.c_str(), 0));
305 if (dutflaga_fd < 0) {
306 PLOG(ERROR) << "opening dutflaga GPIO device file failed";
307 return false;
308 }
309 ScopedEintrSafeFdCloser dutflaga_fd_closer(&dutflaga_fd);
310
311 // Read the dut_flaga GPIO signal. We attempt to read more than---but expect
312 // to receive exactly---two characters: a '0' or '1', and a newline. This is
313 // to ensure that the GPIO device returns a legible result.
314 char buf[3];
315 int ret = HANDLE_EINTR(read(dutflaga_fd, buf, 3));
316 if (ret != 2) {
317 if (ret < 0)
318 PLOG(ERROR) << "reading dutflaga GPIO status failed";
319 else
320 LOG(ERROR) << "read more than one byte (" << ret << ")";
321 return false;
322 }
323
324 // Identify and write GPIO status.
325 char c = buf[0];
326 if ((c == '0' || c == '1') && buf[1] == '\n') {
327 *status_p = (c == '1');
328 } else {
329 buf[2] = '\0';
330 LOG(ERROR) << "read unexpected value from dutflaga GPIO: " << buf;
331 return false;
332 }
333
334 return true;
335}
336
337} // namespace chromeos_update_engine