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