blob: 8627ba280761c1abbdf6f72cc6dd088f022cfdad [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
Gilad Arnold4d740eb2012-05-15 08:48:13 -07007#include <base/memory/scoped_ptr.h>
Gilad Arnold1ebd8132012-03-05 10:19:29 -08008#include <base/string_util.h>
Gilad Arnold4d740eb2012-05-15 08:48:13 -07009#include <base/stringprintf.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080010#include <base/time.h>
11#include <glib.h>
Gilad Arnold1ebd8132012-03-05 10:19:29 -080012
Gilad Arnold4d740eb2012-05-15 08:48:13 -070013#include "update_engine/file_descriptor.h"
Gilad Arnold1ebd8132012-03-05 10:19:29 -080014
Gilad Arnold4d740eb2012-05-15 08:48:13 -070015using base::Time;
16using base::TimeDelta;
Gilad Arnold1ebd8132012-03-05 10:19:29 -080017using std::string;
18
Gilad Arnold4d740eb2012-05-15 08:48:13 -070019using namespace chromeos_update_engine;
20
Gilad Arnold1ebd8132012-03-05 10:19:29 -080021namespace chromeos_update_engine {
22
Gilad Arnold4d740eb2012-05-15 08:48:13 -070023const char* StandardGpioHandler::gpio_dirs_[kGpioDirMax] = {
24 "in", // kGpioDirIn
25 "out", // kGpioDirOut
Gilad Arnold1ebd8132012-03-05 10:19:29 -080026};
27
Gilad Arnold4d740eb2012-05-15 08:48:13 -070028const char* StandardGpioHandler::gpio_vals_[kGpioValMax] = {
29 "1", // kGpioValUp
30 "0", // kGpioValDown
Gilad Arnold1ebd8132012-03-05 10:19:29 -080031};
Gilad Arnold1ebd8132012-03-05 10:19:29 -080032
Gilad Arnold4d740eb2012-05-15 08:48:13 -070033const StandardGpioHandler::GpioDef
34StandardGpioHandler::gpio_defs_[kGpioIdMax] = {
Gilad Arnold6eccc532012-05-17 15:44:22 -070035 { "dutflaga", "ID_GPIO_DUTFLAGA" }, // kGpioIdDutflaga
36 { "dutflagb", "ID_GPIO_DUTFLAGB" }, // kGpioIdDutflagb
Gilad Arnold4d740eb2012-05-15 08:48:13 -070037};
38
39unsigned StandardGpioHandler::num_instances_ = 0;
40
41
42StandardGpioHandler::StandardGpioHandler(UdevInterface* udev_iface,
Gilad Arnold6eccc532012-05-17 15:44:22 -070043 FileDescriptor* fd,
44 bool is_defer_discovery,
45 bool is_cache_test_mode)
Gilad Arnold4d740eb2012-05-15 08:48:13 -070046 : udev_iface_(udev_iface),
Gilad Arnold6eccc532012-05-17 15:44:22 -070047 fd_(fd),
48 is_cache_test_mode_(is_cache_test_mode),
Gilad Arnold95931b82013-01-09 10:37:17 -080049 is_discovery_attempted_(false),
50 is_discovery_successful_(false) {
Gilad Arnold6eccc532012-05-17 15:44:22 -070051 CHECK(udev_iface && fd);
Gilad Arnold4d740eb2012-05-15 08:48:13 -070052
53 // Ensure there's only one instance of this class.
54 CHECK_EQ(num_instances_, static_cast<unsigned>(0));
55 num_instances_++;
56
Gilad Arnold6eccc532012-05-17 15:44:22 -070057 // Reset test signal flags.
58 ResetTestModeSignalingFlags();
59
Gilad Arnold4d740eb2012-05-15 08:48:13 -070060 // If GPIO discovery not deferred, do it.
Gilad Arnold95931b82013-01-09 10:37:17 -080061 if (!is_defer_discovery)
62 DiscoverGpios();
Gilad Arnold4d740eb2012-05-15 08:48:13 -070063}
64
65StandardGpioHandler::~StandardGpioHandler() {
66 num_instances_--;
67}
68
Gilad Arnold4d740eb2012-05-15 08:48:13 -070069bool StandardGpioHandler::IsTestModeSignaled() {
Gilad Arnold95931b82013-01-09 10:37:17 -080070 bool is_returning_cached = false; // for logging purposes
Gilad Arnold4d740eb2012-05-15 08:48:13 -070071
Gilad Arnold95931b82013-01-09 10:37:17 -080072 // Attempt GPIO discovery first.
73 if (DiscoverGpios()) {
74 // Force a check if so requested.
75 if (!is_cache_test_mode_)
76 ResetTestModeSignalingFlags();
Gilad Arnold6eccc532012-05-17 15:44:22 -070077
Gilad Arnold95931b82013-01-09 10:37:17 -080078 is_returning_cached = !is_first_check_; // for logging purposes
79 if (is_first_check_) {
80 is_first_check_ = false;
81 DoTestModeSignalingProtocol();
82 }
Gilad Arnold6eccc532012-05-17 15:44:22 -070083 }
84
85 LOG(INFO) << "result: " << (is_test_mode_ ? "test" : "normal") << " mode"
86 << (is_returning_cached ? " (cached)" : "")
87 << (is_handshake_completed_ ? "" : " (default)");
88 return is_test_mode_;
Gilad Arnold4d740eb2012-05-15 08:48:13 -070089}
90
Gilad Arnold6eccc532012-05-17 15:44:22 -070091
Gilad Arnold4d740eb2012-05-15 08:48:13 -070092bool StandardGpioHandler::GpioChipUdevEnumHelper::SetupEnumFilters(
93 udev_enumerate* udev_enum) {
94 CHECK(udev_enum);
95
96 return !(gpio_handler_->udev_iface_->EnumerateAddMatchSubsystem(
97 udev_enum, "gpio") ||
98 gpio_handler_->udev_iface_->EnumerateAddMatchSysname(
99 udev_enum, "gpiochip*"));
100}
101
102bool StandardGpioHandler::GpioChipUdevEnumHelper::ProcessDev(udev_device* dev) {
103 CHECK(dev);
104
105 // Ensure we did not encounter more than one chip.
106 if (num_gpio_chips_++) {
107 LOG(ERROR) << "enumerated multiple GPIO chips";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800108 return false;
109 }
110
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700111 // Obtain GPIO descriptors.
112 for (int id = 0; id < kGpioIdMax; id++) {
113 const GpioDef* gpio_def = &gpio_defs_[id];
114 const char* descriptor =
115 gpio_handler_->udev_iface_->DeviceGetPropertyValue(
116 dev, gpio_def->udev_property);
117 if (!descriptor) {
118 LOG(ERROR) << "could not obtain " << gpio_def->name
119 << " descriptor using property " << gpio_def->udev_property;
120 return false;
121 }
122 gpio_handler_->gpios_[id].descriptor = descriptor;
123 }
124
125 return true;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800126}
127
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700128bool StandardGpioHandler::GpioChipUdevEnumHelper::Finalize() {
129 if (num_gpio_chips_ != 1) {
130 LOG(ERROR) << "could not enumerate a GPIO chip";
131 return false;
132 }
133 return true;
134}
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800135
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700136bool StandardGpioHandler::GpioUdevEnumHelper::SetupEnumFilters(
137 udev_enumerate* udev_enum) {
138 CHECK(udev_enum);
139 const string gpio_pattern =
140 string("*").append(gpio_handler_->gpios_[id_].descriptor);
141 return !(
142 gpio_handler_->udev_iface_->EnumerateAddMatchSubsystem(
143 udev_enum, "gpio") ||
144 gpio_handler_->udev_iface_->EnumerateAddMatchSysname(
145 udev_enum, gpio_pattern.c_str()));
146}
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800147
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700148bool StandardGpioHandler::GpioUdevEnumHelper::ProcessDev(udev_device* dev) {
149 CHECK(dev);
150
151 // Ensure we did not encounter more than one GPIO device.
152 if (num_gpios_++) {
153 LOG(ERROR) << "enumerated multiple GPIO devices for a given descriptor";
154 return false;
155 }
156
157 // Obtain GPIO device sysfs path.
158 const char* dev_path = gpio_handler_->udev_iface_->DeviceGetSyspath(dev);
159 if (!dev_path) {
160 LOG(ERROR) << "failed to obtain device syspath for GPIO "
161 << gpio_defs_[id_].name;
162 return false;
163 }
164 gpio_handler_->gpios_[id_].dev_path = dev_path;
165
166 LOG(INFO) << "obtained device syspath: " << gpio_defs_[id_].name << " -> "
167 << gpio_handler_->gpios_[id_].dev_path;
168 return true;
169}
170
171bool StandardGpioHandler::GpioUdevEnumHelper::Finalize() {
172 if (num_gpios_ != 1) {
173 LOG(ERROR) << "could not enumerate GPIO device " << gpio_defs_[id_].name;
174 return false;
175 }
176 return true;
177}
178
Gilad Arnold6eccc532012-05-17 15:44:22 -0700179StandardGpioHandler::GpioDirResetter::GpioDirResetter(
180 StandardGpioHandler* handler, GpioId id, GpioDir dir) :
181 do_reset_(false), handler_(handler), id_(id), dir_(dir) {
182 CHECK(handler);
183 CHECK_GE(id, 0);
184 CHECK_LT(id, kGpioIdMax);
185 CHECK_GE(dir, 0);
186 CHECK_LT(dir, kGpioDirMax);
187}
188
189StandardGpioHandler::GpioDirResetter::~GpioDirResetter() {
190 if (do_reset_ && !handler_->SetGpioDirection(id_, dir_)) {
191 LOG(WARNING) << "failed to reset direction of " << gpio_defs_[id_].name
192 << " to " << gpio_dirs_[dir_];
193 }
194}
195
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700196
197bool StandardGpioHandler::InitUdevEnum(struct udev* udev,
198 UdevEnumHelper* enum_helper) {
199 // Obtain a udev enumerate object.
200 struct udev_enumerate* udev_enum;
201 if (!(udev_enum = udev_iface_->EnumerateNew(udev))) {
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800202 LOG(ERROR) << "failed to obtain udev enumerate context";
203 return false;
204 }
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800205
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700206 // Assign enumerate object to closer.
207 scoped_ptr<UdevInterface::UdevEnumerateCloser>
208 udev_enum_closer(udev_iface_->NewUdevEnumerateCloser(&udev_enum));
209
210 // Setup enumeration filters.
211 if (!enum_helper->SetupEnumFilters(udev_enum)) {
212 LOG(ERROR) << "failed to setup udev enumerate filters";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800213 return false;
214 }
215
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700216 // Scan for matching devices.
217 if (udev_iface_->EnumerateScanDevices(udev_enum)) {
218 LOG(ERROR) << "udev enumerate scan failed";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800219 return false;
220 }
221
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700222 // Iterate over matching devices.
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800223 struct udev_list_entry* list_entry;
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700224 for (list_entry = udev_iface_->EnumerateGetListEntry(udev_enum);
225 list_entry; list_entry = udev_iface_->ListEntryGetNext(list_entry)) {
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800226 // Obtain device name.
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700227 const char* dev_path = udev_iface_->ListEntryGetName(list_entry);
228 if (!dev_path) {
229 LOG(ERROR) << "enumerated device has a null name string";
230 return false;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800231 }
232
233 // Obtain device object.
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700234 struct udev_device* dev = udev_iface_->DeviceNewFromSyspath(udev, dev_path);
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800235 if (!dev) {
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700236 LOG(ERROR) << "obtained a null device object for enumerated device";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800237 return false;
238 }
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700239 scoped_ptr<UdevInterface::UdevDeviceCloser>
240 dev_closer(udev_iface_->NewUdevDeviceCloser(&dev));
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800241
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700242 if (!enum_helper->ProcessDev(dev))
243 return false;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800244 }
245
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700246 // Make sure postconditions were met.
247 return enum_helper->Finalize();
248}
249
Gilad Arnold6eccc532012-05-17 15:44:22 -0700250void StandardGpioHandler::ResetTestModeSignalingFlags() {
251 is_first_check_ = true;
252 is_handshake_completed_ = false;
253 is_test_mode_ = false;
254}
255
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700256bool StandardGpioHandler::DiscoverGpios() {
257 if (is_discovery_attempted_)
Gilad Arnold95931b82013-01-09 10:37:17 -0800258 return is_discovery_successful_;
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700259
260 is_discovery_attempted_ = true;
261
262 // Obtain libudev instance and attach to a dedicated closer.
263 struct udev* udev;
264 if (!(udev = udev_iface_->New())) {
Gilad Arnold95931b82013-01-09 10:37:17 -0800265 LOG(ERROR) << "failed to obtain libudev instance, aborting GPIO discovery";
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700266 return false;
267 }
268 scoped_ptr<UdevInterface::UdevCloser>
269 udev_closer(udev_iface_->NewUdevCloser(&udev));
270
271 // Enumerate GPIO chips, scanning for GPIO descriptors.
272 GpioChipUdevEnumHelper chip_enum_helper(this);
273 if (!InitUdevEnum(udev, &chip_enum_helper)) {
274 LOG(ERROR) << "enumeration error, aborting GPIO discovery";
275 return false;
276 }
277
278 // Obtain device names for all discovered GPIOs, reusing the udev instance.
279 for (int id = 0; id < kGpioIdMax; id++) {
280 GpioUdevEnumHelper gpio_enum_helper(this, static_cast<GpioId>(id));
281 if (!InitUdevEnum(udev, &gpio_enum_helper)) {
282 LOG(ERROR) << "enumeration error, aborting GPIO discovery";
283 return false;
284 }
285 }
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800286
Gilad Arnold95931b82013-01-09 10:37:17 -0800287 is_discovery_successful_ = true;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800288 return true;
289}
290
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700291bool StandardGpioHandler::GetGpioDevName(StandardGpioHandler::GpioId id,
292 string* dev_path_p) {
293 CHECK(id >= 0 && id < kGpioIdMax && dev_path_p);
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800294
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700295 *dev_path_p = gpios_[id].dev_path;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800296 return true;
297}
298
Gilad Arnold6eccc532012-05-17 15:44:22 -0700299bool StandardGpioHandler::OpenGpioFd(StandardGpioHandler::GpioId id,
300 const char* dev_name,
301 bool is_write) {
302 CHECK(id >= 0 && id < kGpioIdMax && dev_name);
303 string file_name = StringPrintf("%s/%s", gpios_[id].dev_path.c_str(),
304 dev_name);
305 if (!fd_->Open(file_name.c_str(), (is_write ? O_WRONLY : O_RDONLY))) {
Gilad Arnold6eccc532012-05-17 15:44:22 -0700306 if (fd_->IsSettingErrno()) {
Gilad Arnold95931b82013-01-09 10:37:17 -0800307 PLOG(ERROR) << "failed to open " << file_name
308 << " (" << gpio_defs_[id].name << ") for "
309 << (is_write ? "writing" : "reading");
Gilad Arnold6eccc532012-05-17 15:44:22 -0700310 } else {
Gilad Arnold95931b82013-01-09 10:37:17 -0800311 LOG(ERROR) << "failed to open " << file_name
312 << " (" << gpio_defs_[id].name << ") for "
313 << (is_write ? "writing" : "reading");
Gilad Arnold6eccc532012-05-17 15:44:22 -0700314 }
315 return false;
316 }
317 return true;
318}
319
320bool StandardGpioHandler::SetGpio(StandardGpioHandler::GpioId id,
321 const char* dev_name, const char* entries[],
322 const int num_entries, int index) {
323 CHECK_GE(id, 0);
324 CHECK_LT(id, kGpioIdMax);
325 CHECK(dev_name);
326 CHECK(entries);
327 CHECK_GT(num_entries, 0);
328 CHECK_GE(index, 0);
329 CHECK_LT(index, num_entries);
330
331 // Open device for writing.
332 if (!OpenGpioFd(id, dev_name, true))
333 return false;
334 ScopedFileDescriptorCloser dev_fd_closer(fd_);
335
336 // Write a string corresponding to the requested output index to the GPIO
337 // device, appending a newline.
338 string output_str = entries[index];
339 output_str += '\n';
340 ssize_t write_len = fd_->Write(output_str.c_str(), output_str.length());
341 if (write_len != static_cast<ssize_t>(output_str.length())) {
342 if (write_len < 0) {
343 const string err_str = "failed to write to GPIO";
344 if (fd_->IsSettingErrno()) {
345 PLOG(ERROR) << err_str;
346 } else {
347 LOG(ERROR) << err_str;
348 }
349 } else {
350 LOG(ERROR) << "wrong number of bytes written (" << write_len
351 << " instead of " << output_str.length() << ")";
352 }
353 return false;
354 }
355
356 // Close the device explicitly, returning the close result.
357 return fd_->Close();
358}
359
360bool StandardGpioHandler::GetGpio(StandardGpioHandler::GpioId id,
361 const char* dev_name, const char* entries[],
362 const int num_entries, int* index_p) {
363 CHECK_GE(id, 0);
364 CHECK_LT(id, kGpioIdMax);
365 CHECK(dev_name);
366 CHECK(entries);
367 CHECK_GT(num_entries, 0);
368 CHECK(index_p);
369
370 // Open device for reading.
371 if (!OpenGpioFd(id, dev_name, false))
372 return false;
373 ScopedFileDescriptorCloser dev_fd_closer(fd_);
374
375 // Read the GPIO device. We attempt to read more than the max number of
376 // characters expected followed by a newline, to ensure that we've indeed read
377 // all the data available on the device.
378 size_t max_entry_len = 0;
379 for (int i = 0; i < num_entries; i++) {
380 size_t entry_len = strlen(entries[i]);
381 if (entry_len > max_entry_len)
382 max_entry_len = entry_len;
383 }
384 max_entry_len++; // account for trailing newline
385 size_t buf_len = max_entry_len + 1; // room for excess char / null terminator
386 char buf[buf_len];
387 memset(buf, 0, buf_len);
388 ssize_t read_len = fd_->Read(buf, buf_len);
389 if (read_len < 0 || read_len > static_cast<ssize_t>(max_entry_len)) {
390 if (read_len < 0) {
391 const string err_str = "failed to read GPIO";
392 if (fd_->IsSettingErrno()) {
393 PLOG(ERROR) << err_str;
394 } else {
395 LOG(ERROR) << err_str;
396 }
397 } else {
398 LOG(ERROR) << "read too many bytes (" << read_len << ")";
399 }
400 return false;
401 }
402
403 // Remove trailing newline.
404 read_len--;
405 if (buf[read_len] != '\n') {
406 LOG(ERROR) << "read value missing trailing newline";
407 return false;
408 }
409 buf[read_len] = '\0';
410
411 // Identify and write GPIO status.
412 for (int i = 0; i < num_entries; i++)
413 if (!strcmp(entries[i], buf)) {
414 *index_p = i;
415 // Close the device explicitly, returning the close result.
416 return fd_->Close();
417 }
418
419 // Oops, unidentified reading...
420 LOG(ERROR) << "read unexpected value from GPIO (`" << buf << "')";
421 return false;
422}
423
424bool StandardGpioHandler::SetGpioDirection(StandardGpioHandler::GpioId id,
425 StandardGpioHandler::GpioDir dir) {
426 return SetGpio(id, "direction", gpio_dirs_, kGpioDirMax, dir);
427}
428
429bool StandardGpioHandler::GetGpioDirection(
430 StandardGpioHandler::GpioId id,
431 StandardGpioHandler::GpioDir* direction_p) {
432 return GetGpio(id, "direction", gpio_dirs_, kGpioDirMax,
433 reinterpret_cast<int*>(direction_p));
434}
435
436bool StandardGpioHandler::SetGpioValue(StandardGpioHandler::GpioId id,
437 StandardGpioHandler::GpioVal value,
438 bool is_check_direction) {
439 // If so instructed, ensure that the GPIO is indeed in the output direction
440 // before attempting to write to it.
441 if (is_check_direction) {
442 GpioDir dir;
443 if (!(GetGpioDirection(id, &dir) && dir == kGpioDirOut)) {
444 LOG(ERROR) << "couldn't verify that GPIO is in the output direction "
445 "prior to reading from it";
446 return false;
447 }
448 }
449
450 return SetGpio(id, "value", gpio_vals_, kGpioValMax, value);
451}
452
453bool StandardGpioHandler::GetGpioValue(StandardGpioHandler::GpioId id,
454 StandardGpioHandler::GpioVal* value_p,
455 bool is_check_direction) {
456 // If so instructed, ensure that the GPIO is indeed in the input direction
457 // before attempting to read from it.
458 if (is_check_direction) {
459 GpioDir dir;
460 if (!(GetGpioDirection(id, &dir) && dir == kGpioDirIn)) {
461 LOG(ERROR) << "couldn't verify that GPIO is in the input direction "
462 "prior to reading from it";
463 return false;
464 }
465 }
466
467 return GetGpio(id, "value", gpio_vals_, kGpioValMax,
468 reinterpret_cast<int*>(value_p));
469}
470
471bool StandardGpioHandler::DoTestModeSignalingProtocol() {
472 // The test mode signaling protocol is designed to provide a robust indication
473 // that a Chrome OS device is physically connected to a servo board in a lab
474 // setting. It is making very few assumptions about the soundness of the
475 // hardware, firmware and kernel driver implementation of the GPIO mechanism.
476 // In general, it is performing a three-way handshake between servo and the
477 // Chrome OS client, based on changes in the GPIO value readings. The
478 // client-side implementation does the following:
479 //
480 // 1. Check for an initial signal (0) on the input GPIO (dut_flaga).
481 //
482 // 2. Flip the signal (1 -> 0) on the output GPIO (dut_flagb).
483 //
484 // 3. Check for a flipped signal (1) on the input GPIO.
485 //
486 // TODO(garnold) the current implementation is based on sysfs access to GPIOs.
487 // We will likely change this to using a specialized in-kernel driver
488 // implementation, which would give us better performance and security
489 // guarantees.
490
491 LOG(INFO) << "attempting GPIO handshake";
492
493 const char* dutflaga_name = gpio_defs_[kGpioIdDutflaga].name;
494 const char* dutflagb_name = gpio_defs_[kGpioIdDutflagb].name;
495
496 // Flip GPIO direction, set it to "in".
497 // TODO(garnold) changing the GPIO direction back and forth is necessary for
498 // overcoming a firmware/kernel issue which causes the device to be in the
499 // "out" state whereas the kernel thinks it is in the "in" state. This should
500 // be abandoned once the firmware and/or kernel driver have been fixed.
501 // Details here: http://code.google.com/p/chromium-os/issues/detail?id=27680
502 if (!(SetGpioDirection(kGpioIdDutflaga, kGpioDirOut) &&
503 SetGpioDirection(kGpioIdDutflaga, kGpioDirIn))) {
504 LOG(ERROR) << "failed to flip direction of input GPIO " << dutflaga_name;
505 return false;
506 }
507
508 // Peek input GPIO state.
509 GpioVal dutflaga_gpio_value;
510 if (!GetGpioValue(kGpioIdDutflaga, &dutflaga_gpio_value, true)) {
511 LOG(ERROR) << "failed to read input GPIO " << dutflaga_name;
512 return false;
513 }
514
515 // If initial handshake signal not received, abort.
516 if (dutflaga_gpio_value != kGpioValDown) {
517 LOG(INFO) << "input GPIO " << dutflaga_name
518 << " unset, terminating handshake";
519 is_handshake_completed_ = true;
520 return true;
521 }
522
523 // Initialize output GPIO to a default state.
524 // TODO(garnold) a similar workaround for possible driver/firmware glitches,
525 // we insist on flipping the direction of the GPIO prior to assuming it is in
526 // the "out" direction.
527 GpioDirResetter dutflagb_dir_resetter(this, kGpioIdDutflagb, kGpioDirIn);
528 if (!(SetGpioDirection(kGpioIdDutflagb, kGpioDirIn) &&
529 dutflagb_dir_resetter.set_do_reset(
530 SetGpioDirection(kGpioIdDutflagb, kGpioDirOut)) &&
531 SetGpioValue(kGpioIdDutflagb, kGpioValUp, false))) {
532 LOG(ERROR) << "failed to initialize output GPIO " << dutflagb_name;
533 return false;
534 }
535
536 // Wait, giving the receiving end enough time to sense the fall.
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800537 g_usleep(kServoOutputResponseWaitInSecs * G_USEC_PER_SEC);
Gilad Arnold6eccc532012-05-17 15:44:22 -0700538
539 // Flip the output signal.
540 if (!SetGpioValue(kGpioIdDutflagb, kGpioValDown, false)) {
541 LOG(ERROR) << "failed to flip output GPIO " << dutflagb_name;
542 return false;
543 }
544
545 // Look for flipped input GPIO value, up to a preset timeout.
546 Time expires =
547 Time::Now() + TimeDelta::FromSeconds(kServoInputResponseTimeoutInSecs);
548 TimeDelta delay =
549 TimeDelta::FromMicroseconds(1000000 / kServoInputNumChecksPerSec);
550 bool is_first_response_check = true;
551 bool is_error = false;
552 while (Time::Now() < expires) {
553 if (is_first_response_check)
554 is_first_response_check = false;
555 else
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800556 g_usleep(delay.InMicroseconds());
Gilad Arnold6eccc532012-05-17 15:44:22 -0700557
558 // Read input GPIO.
559 if (!GetGpioValue(kGpioIdDutflaga, &dutflaga_gpio_value, true)) {
560 LOG(ERROR) << "failed to read input GPIO " << dutflaga_name;
561 is_error = true;
562 break;
563 }
564
565 // If dutflaga is now up (flipped), we got our signal!
566 if (dutflaga_gpio_value == kGpioValUp) {
567 is_test_mode_ = true;
568 break;
569 }
570 }
571
572 if (!is_error) {
573 if (is_test_mode_) {
574 is_handshake_completed_ = true;
575 LOG(INFO) << "GPIO handshake completed, test mode signaled";
576 } else {
577 LOG(INFO) << "timed out waiting for input GPIO " << dutflaga_name
578 << " to flip, terminating handshake";
579 }
580 }
581
582 return is_handshake_completed_;
583}
584
Gilad Arnoldbf7919b2013-01-08 13:07:37 -0800585
586bool NoopGpioHandler::IsTestModeSignaled() {
587 LOG(INFO) << "GPIOs not engaged, defaulting to "
588 << (is_test_mode_ ? "test" : "normal") << " mode";
589 return is_test_mode_;
590}
591
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800592} // namespace chromeos_update_engine