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 | 8e3f126 | 2013-01-08 14:59:54 -0800 | [diff] [blame] | 10 | #include <base/time.h> |
| 11 | #include <glib.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 | #include "update_engine/file_descriptor.h" |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 14 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 15 | using base::Time; |
| 16 | using base::TimeDelta; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 17 | using std::string; |
| 18 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 19 | using namespace chromeos_update_engine; |
| 20 | |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 21 | namespace chromeos_update_engine { |
| 22 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 23 | const char* StandardGpioHandler::gpio_dirs_[kGpioDirMax] = { |
| 24 | "in", // kGpioDirIn |
| 25 | "out", // kGpioDirOut |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 26 | }; |
| 27 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 28 | const char* StandardGpioHandler::gpio_vals_[kGpioValMax] = { |
| 29 | "1", // kGpioValUp |
| 30 | "0", // kGpioValDown |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 31 | }; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 32 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 33 | const StandardGpioHandler::GpioDef |
| 34 | StandardGpioHandler::gpio_defs_[kGpioIdMax] = { |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 35 | { "dutflaga", "ID_GPIO_DUTFLAGA" }, // kGpioIdDutflaga |
| 36 | { "dutflagb", "ID_GPIO_DUTFLAGB" }, // kGpioIdDutflagb |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 37 | }; |
| 38 | |
| 39 | unsigned StandardGpioHandler::num_instances_ = 0; |
| 40 | |
| 41 | |
| 42 | StandardGpioHandler::StandardGpioHandler(UdevInterface* udev_iface, |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 43 | FileDescriptor* fd, |
| 44 | bool is_defer_discovery, |
| 45 | bool is_cache_test_mode) |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 46 | : udev_iface_(udev_iface), |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 47 | fd_(fd), |
| 48 | is_cache_test_mode_(is_cache_test_mode), |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 49 | is_discovery_attempted_(false), |
| 50 | is_discovery_successful_(false) { |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 51 | CHECK(udev_iface && fd); |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 52 | |
| 53 | // Ensure there's only one instance of this class. |
| 54 | CHECK_EQ(num_instances_, static_cast<unsigned>(0)); |
| 55 | num_instances_++; |
| 56 | |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 57 | // Reset test signal flags. |
| 58 | ResetTestModeSignalingFlags(); |
| 59 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 60 | // If GPIO discovery not deferred, do it. |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 61 | if (!is_defer_discovery) |
| 62 | DiscoverGpios(); |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 63 | } |
| 64 | |
| 65 | StandardGpioHandler::~StandardGpioHandler() { |
| 66 | num_instances_--; |
| 67 | } |
| 68 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 69 | bool StandardGpioHandler::IsTestModeSignaled() { |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 70 | bool is_returning_cached = false; // for logging purposes |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 71 | |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 72 | // Attempt GPIO discovery first. |
| 73 | if (DiscoverGpios()) { |
| 74 | // Force a check if so requested. |
| 75 | if (!is_cache_test_mode_) |
| 76 | ResetTestModeSignalingFlags(); |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 77 | |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 78 | is_returning_cached = !is_first_check_; // for logging purposes |
| 79 | if (is_first_check_) { |
| 80 | is_first_check_ = false; |
| 81 | DoTestModeSignalingProtocol(); |
| 82 | } |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 83 | } |
| 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 Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 89 | } |
| 90 | |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 91 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 92 | bool 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 | |
| 102 | bool 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 108 | return false; |
| 109 | } |
| 110 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 111 | // 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 126 | } |
| 127 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 128 | bool 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 135 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 136 | bool 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 147 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 148 | bool 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 | |
| 171 | bool 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 Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 179 | StandardGpioHandler::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 | |
| 189 | StandardGpioHandler::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 Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 196 | |
| 197 | bool 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 202 | LOG(ERROR) << "failed to obtain udev enumerate context"; |
| 203 | return false; |
| 204 | } |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 205 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 206 | // 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 213 | return false; |
| 214 | } |
| 215 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 216 | // Scan for matching devices. |
| 217 | if (udev_iface_->EnumerateScanDevices(udev_enum)) { |
| 218 | LOG(ERROR) << "udev enumerate scan failed"; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 219 | return false; |
| 220 | } |
| 221 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 222 | // Iterate over matching devices. |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 223 | struct udev_list_entry* list_entry; |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 224 | for (list_entry = udev_iface_->EnumerateGetListEntry(udev_enum); |
| 225 | list_entry; list_entry = udev_iface_->ListEntryGetNext(list_entry)) { |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 226 | // Obtain device name. |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 227 | 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 231 | } |
| 232 | |
| 233 | // Obtain device object. |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 234 | struct udev_device* dev = udev_iface_->DeviceNewFromSyspath(udev, dev_path); |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 235 | if (!dev) { |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 236 | LOG(ERROR) << "obtained a null device object for enumerated device"; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 237 | return false; |
| 238 | } |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 239 | scoped_ptr<UdevInterface::UdevDeviceCloser> |
| 240 | dev_closer(udev_iface_->NewUdevDeviceCloser(&dev)); |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 241 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 242 | if (!enum_helper->ProcessDev(dev)) |
| 243 | return false; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 244 | } |
| 245 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 246 | // Make sure postconditions were met. |
| 247 | return enum_helper->Finalize(); |
| 248 | } |
| 249 | |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 250 | void StandardGpioHandler::ResetTestModeSignalingFlags() { |
| 251 | is_first_check_ = true; |
| 252 | is_handshake_completed_ = false; |
| 253 | is_test_mode_ = false; |
| 254 | } |
| 255 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 256 | bool StandardGpioHandler::DiscoverGpios() { |
| 257 | if (is_discovery_attempted_) |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 258 | return is_discovery_successful_; |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 259 | |
| 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 Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 265 | LOG(ERROR) << "failed to obtain libudev instance, aborting GPIO discovery"; |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 266 | 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 286 | |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 287 | is_discovery_successful_ = true; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 288 | return true; |
| 289 | } |
| 290 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 291 | bool StandardGpioHandler::GetGpioDevName(StandardGpioHandler::GpioId id, |
| 292 | string* dev_path_p) { |
| 293 | CHECK(id >= 0 && id < kGpioIdMax && dev_path_p); |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 294 | |
Gilad Arnold | 4d740eb | 2012-05-15 08:48:13 -0700 | [diff] [blame] | 295 | *dev_path_p = gpios_[id].dev_path; |
Gilad Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 296 | return true; |
| 297 | } |
| 298 | |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 299 | bool 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 Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 306 | if (fd_->IsSettingErrno()) { |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 307 | PLOG(ERROR) << "failed to open " << file_name |
| 308 | << " (" << gpio_defs_[id].name << ") for " |
| 309 | << (is_write ? "writing" : "reading"); |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 310 | } else { |
Gilad Arnold | 95931b8 | 2013-01-09 10:37:17 -0800 | [diff] [blame] | 311 | LOG(ERROR) << "failed to open " << file_name |
| 312 | << " (" << gpio_defs_[id].name << ") for " |
| 313 | << (is_write ? "writing" : "reading"); |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 314 | } |
| 315 | return false; |
| 316 | } |
| 317 | return true; |
| 318 | } |
| 319 | |
| 320 | bool 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 | |
| 360 | bool 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 | |
| 424 | bool StandardGpioHandler::SetGpioDirection(StandardGpioHandler::GpioId id, |
| 425 | StandardGpioHandler::GpioDir dir) { |
| 426 | return SetGpio(id, "direction", gpio_dirs_, kGpioDirMax, dir); |
| 427 | } |
| 428 | |
| 429 | bool 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 | |
| 436 | bool 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 | |
| 453 | bool 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 | |
| 471 | bool 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 Arnold | 8e3f126 | 2013-01-08 14:59:54 -0800 | [diff] [blame] | 537 | g_usleep(kServoOutputResponseWaitInSecs * G_USEC_PER_SEC); |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 538 | |
| 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 Arnold | 8e3f126 | 2013-01-08 14:59:54 -0800 | [diff] [blame] | 556 | g_usleep(delay.InMicroseconds()); |
Gilad Arnold | 6eccc53 | 2012-05-17 15:44:22 -0700 | [diff] [blame] | 557 | |
| 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 Arnold | bf7919b | 2013-01-08 13:07:37 -0800 | [diff] [blame] | 585 | |
| 586 | bool 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 Arnold | 1ebd813 | 2012-03-05 10:19:29 -0800 | [diff] [blame] | 592 | } // namespace chromeos_update_engine |