blob: efd5e3df298c0194efcd47a6adf6a54b2ca0b9be [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 Arnold1ebd8132012-03-05 10:19:29 -080010
Gilad Arnold4d740eb2012-05-15 08:48:13 -070011#include "update_engine/file_descriptor.h"
Gilad Arnold1ebd8132012-03-05 10:19:29 -080012
Gilad Arnold4d740eb2012-05-15 08:48:13 -070013using base::Time;
14using base::TimeDelta;
Gilad Arnold1ebd8132012-03-05 10:19:29 -080015using std::string;
16
Gilad Arnold4d740eb2012-05-15 08:48:13 -070017using namespace chromeos_update_engine;
18
Gilad Arnold1ebd8132012-03-05 10:19:29 -080019namespace chromeos_update_engine {
20
Gilad Arnold4d740eb2012-05-15 08:48:13 -070021const char* StandardGpioHandler::gpio_dirs_[kGpioDirMax] = {
22 "in", // kGpioDirIn
23 "out", // kGpioDirOut
Gilad Arnold1ebd8132012-03-05 10:19:29 -080024};
25
Gilad Arnold4d740eb2012-05-15 08:48:13 -070026const char* StandardGpioHandler::gpio_vals_[kGpioValMax] = {
27 "1", // kGpioValUp
28 "0", // kGpioValDown
Gilad Arnold1ebd8132012-03-05 10:19:29 -080029};
Gilad Arnold1ebd8132012-03-05 10:19:29 -080030
Gilad Arnold4d740eb2012-05-15 08:48:13 -070031const StandardGpioHandler::GpioDef
32StandardGpioHandler::gpio_defs_[kGpioIdMax] = {
33 { "dutflaga", "ID_GPIO_DUTFLAGA" }, // kGpioDutflaga
34 { "dutflagb", "ID_GPIO_DUTFLAGB" }, // kGpioDutflagb
35};
36
37unsigned StandardGpioHandler::num_instances_ = 0;
38
39
40StandardGpioHandler::StandardGpioHandler(UdevInterface* udev_iface,
41 bool is_defer_discovery)
42 : udev_iface_(udev_iface),
43 is_discovery_attempted_(false) {
44 CHECK(udev_iface);
45
46 // Ensure there's only one instance of this class.
47 CHECK_EQ(num_instances_, static_cast<unsigned>(0));
48 num_instances_++;
49
50 // If GPIO discovery not deferred, do it.
51 if (!(is_defer_discovery || DiscoverGpios())) {
52 LOG(WARNING) << "GPIO discovery failed";
53 }
54}
55
56StandardGpioHandler::~StandardGpioHandler() {
57 num_instances_--;
58}
59
60// TODO(garnold) currently, this function always returns false and avoids the
61// GPIO signaling protocol altogether; to be extended later.
62bool StandardGpioHandler::IsTestModeSignaled() {
63 // Attempt GPIO discovery.
64 if (!DiscoverGpios()) {
65 LOG(WARNING) << "GPIO discovery failed";
66 }
67
68 return false;
69}
70
71bool StandardGpioHandler::GpioChipUdevEnumHelper::SetupEnumFilters(
72 udev_enumerate* udev_enum) {
73 CHECK(udev_enum);
74
75 return !(gpio_handler_->udev_iface_->EnumerateAddMatchSubsystem(
76 udev_enum, "gpio") ||
77 gpio_handler_->udev_iface_->EnumerateAddMatchSysname(
78 udev_enum, "gpiochip*"));
79}
80
81bool StandardGpioHandler::GpioChipUdevEnumHelper::ProcessDev(udev_device* dev) {
82 CHECK(dev);
83
84 // Ensure we did not encounter more than one chip.
85 if (num_gpio_chips_++) {
86 LOG(ERROR) << "enumerated multiple GPIO chips";
Gilad Arnold1ebd8132012-03-05 10:19:29 -080087 return false;
88 }
89
Gilad Arnold4d740eb2012-05-15 08:48:13 -070090 // Obtain GPIO descriptors.
91 for (int id = 0; id < kGpioIdMax; id++) {
92 const GpioDef* gpio_def = &gpio_defs_[id];
93 const char* descriptor =
94 gpio_handler_->udev_iface_->DeviceGetPropertyValue(
95 dev, gpio_def->udev_property);
96 if (!descriptor) {
97 LOG(ERROR) << "could not obtain " << gpio_def->name
98 << " descriptor using property " << gpio_def->udev_property;
99 return false;
100 }
101 gpio_handler_->gpios_[id].descriptor = descriptor;
102 }
103
104 return true;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800105}
106
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700107bool StandardGpioHandler::GpioChipUdevEnumHelper::Finalize() {
108 if (num_gpio_chips_ != 1) {
109 LOG(ERROR) << "could not enumerate a GPIO chip";
110 return false;
111 }
112 return true;
113}
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800114
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700115bool StandardGpioHandler::GpioUdevEnumHelper::SetupEnumFilters(
116 udev_enumerate* udev_enum) {
117 CHECK(udev_enum);
118 const string gpio_pattern =
119 string("*").append(gpio_handler_->gpios_[id_].descriptor);
120 return !(
121 gpio_handler_->udev_iface_->EnumerateAddMatchSubsystem(
122 udev_enum, "gpio") ||
123 gpio_handler_->udev_iface_->EnumerateAddMatchSysname(
124 udev_enum, gpio_pattern.c_str()));
125}
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800126
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700127bool StandardGpioHandler::GpioUdevEnumHelper::ProcessDev(udev_device* dev) {
128 CHECK(dev);
129
130 // Ensure we did not encounter more than one GPIO device.
131 if (num_gpios_++) {
132 LOG(ERROR) << "enumerated multiple GPIO devices for a given descriptor";
133 return false;
134 }
135
136 // Obtain GPIO device sysfs path.
137 const char* dev_path = gpio_handler_->udev_iface_->DeviceGetSyspath(dev);
138 if (!dev_path) {
139 LOG(ERROR) << "failed to obtain device syspath for GPIO "
140 << gpio_defs_[id_].name;
141 return false;
142 }
143 gpio_handler_->gpios_[id_].dev_path = dev_path;
144
145 LOG(INFO) << "obtained device syspath: " << gpio_defs_[id_].name << " -> "
146 << gpio_handler_->gpios_[id_].dev_path;
147 return true;
148}
149
150bool StandardGpioHandler::GpioUdevEnumHelper::Finalize() {
151 if (num_gpios_ != 1) {
152 LOG(ERROR) << "could not enumerate GPIO device " << gpio_defs_[id_].name;
153 return false;
154 }
155 return true;
156}
157
158
159bool StandardGpioHandler::InitUdevEnum(struct udev* udev,
160 UdevEnumHelper* enum_helper) {
161 // Obtain a udev enumerate object.
162 struct udev_enumerate* udev_enum;
163 if (!(udev_enum = udev_iface_->EnumerateNew(udev))) {
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800164 LOG(ERROR) << "failed to obtain udev enumerate context";
165 return false;
166 }
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800167
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700168 // Assign enumerate object to closer.
169 scoped_ptr<UdevInterface::UdevEnumerateCloser>
170 udev_enum_closer(udev_iface_->NewUdevEnumerateCloser(&udev_enum));
171
172 // Setup enumeration filters.
173 if (!enum_helper->SetupEnumFilters(udev_enum)) {
174 LOG(ERROR) << "failed to setup udev enumerate filters";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800175 return false;
176 }
177
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700178 // Scan for matching devices.
179 if (udev_iface_->EnumerateScanDevices(udev_enum)) {
180 LOG(ERROR) << "udev enumerate scan failed";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800181 return false;
182 }
183
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700184 // Iterate over matching devices.
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800185 struct udev_list_entry* list_entry;
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700186 for (list_entry = udev_iface_->EnumerateGetListEntry(udev_enum);
187 list_entry; list_entry = udev_iface_->ListEntryGetNext(list_entry)) {
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800188 // Obtain device name.
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700189 const char* dev_path = udev_iface_->ListEntryGetName(list_entry);
190 if (!dev_path) {
191 LOG(ERROR) << "enumerated device has a null name string";
192 return false;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800193 }
194
195 // Obtain device object.
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700196 struct udev_device* dev = udev_iface_->DeviceNewFromSyspath(udev, dev_path);
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800197 if (!dev) {
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700198 LOG(ERROR) << "obtained a null device object for enumerated device";
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800199 return false;
200 }
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700201 scoped_ptr<UdevInterface::UdevDeviceCloser>
202 dev_closer(udev_iface_->NewUdevDeviceCloser(&dev));
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800203
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700204 if (!enum_helper->ProcessDev(dev))
205 return false;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800206 }
207
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700208 // Make sure postconditions were met.
209 return enum_helper->Finalize();
210}
211
212bool StandardGpioHandler::DiscoverGpios() {
213 if (is_discovery_attempted_)
214 return true;
215
216 is_discovery_attempted_ = true;
217
218 // Obtain libudev instance and attach to a dedicated closer.
219 struct udev* udev;
220 if (!(udev = udev_iface_->New())) {
221 LOG(ERROR) << "failed to obtain libudev instance";
222 return false;
223 }
224 scoped_ptr<UdevInterface::UdevCloser>
225 udev_closer(udev_iface_->NewUdevCloser(&udev));
226
227 // Enumerate GPIO chips, scanning for GPIO descriptors.
228 GpioChipUdevEnumHelper chip_enum_helper(this);
229 if (!InitUdevEnum(udev, &chip_enum_helper)) {
230 LOG(ERROR) << "enumeration error, aborting GPIO discovery";
231 return false;
232 }
233
234 // Obtain device names for all discovered GPIOs, reusing the udev instance.
235 for (int id = 0; id < kGpioIdMax; id++) {
236 GpioUdevEnumHelper gpio_enum_helper(this, static_cast<GpioId>(id));
237 if (!InitUdevEnum(udev, &gpio_enum_helper)) {
238 LOG(ERROR) << "enumeration error, aborting GPIO discovery";
239 return false;
240 }
241 }
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800242
243 return true;
244}
245
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700246bool StandardGpioHandler::GetGpioDevName(StandardGpioHandler::GpioId id,
247 string* dev_path_p) {
248 CHECK(id >= 0 && id < kGpioIdMax && dev_path_p);
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800249
Gilad Arnold4d740eb2012-05-15 08:48:13 -0700250 *dev_path_p = gpios_[id].dev_path;
Gilad Arnold1ebd8132012-03-05 10:19:29 -0800251 return true;
252}
253
254} // namespace chromeos_update_engine