Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Side by Side Diff: gpio_setup.cc

Issue 2656004: add c++ version of the firmware utilities (Closed) Base URL: ssh://gitrw.chromium.org/firmware-utils.git
Patch Set: removed python scripts and add comments for range names in gpio Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Makefile ('k') | gpio_setup.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 // NOTE: this file is translated from gpio_setup.py
6 //
7 // A script to create symlinks to platform specific GPIO pins.
8 //
9 // This script creates a set of symlinks pointing at the sys fs files returning
10 // the appropriate GPIO pin values. Each symlink is named to represent the
11 // actual GPIO pin function.
12 //
13 // The location of the symlinks generated by this script can be specified using
14 // the --symlink_root command line option. By default /home/gpio directory is
15 // used. The symlink directory must exist before this script is run.
16 //
17 // The GPIO pins' values are available through a GPIO device present in sys fs.
18 // The device is identified by its PCI bus address. The default PCI address of
19 // the GPIO device (set to 0:0:1f.0), can be changed using the --pci_address
20 // command line option.
21 //
22 // The platform specific bit usage of the GPIO device is derived from the ACPI,
23 // also using files found in a fixed location in sys fs. The default location of
24 // /sys/bus/platform/devices/chromeos_acpi could be changed using the
25 // --acpi_root command line option.
26 //
27 // Each GPIO pin is represented through ACPI as a subdirectory with several
28 // files in it. A typical name of the GPIO pin file looks as follows:
29 //
30 // <acpi_root>/GPIO.<instance>/GPIO.[0-3]
31 //
32 // where <instance> is a zero based number assigned to this GPIO pin by the BIOS
33 //
34 // In particular, file GPIO.0 represents encoded pin signal type (from which the
35 // symlink name is derived), and file GPIO.2 represents the actual zero based
36 // GPIO pin number within this GPIO device range.
37 //
38 // This script reads the ACPI provided mapping, enables the appropriate GPIO
39 // pins and creates symlinks mapping these GPIOs' values.
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <assert.h>
45 #include <sys/param.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <stdarg.h>
50 #include <ctype.h>
51 #include <getopt.h>
52 #include <glob.h>
53
54 #include <string>
55 #include <vector>
56 #include <map>
57
58 #define GPIO_ROOT "/sys/class/gpio"
59 #define GPIO_DEVICE_ROOT GPIO_ROOT "/gpiochip"
60 #define GPIO_ENABLE_FILE GPIO_ROOT "export"
61
62 // Can be changed using --pci_address command line option.
63 #define DEFAULT_GPIO_DEVICE_PCI_ADDRESS "0000:00:1f.0"
64
65 // Can be changed using --acpi_root command line option.
66 #define DEFAULT_ACPI_ROOT "/sys/bus/platform/devices/chromeos_acpi"
67
68 // can be changed using --symlink_root command line option.
69 #define DEFAULT_SYMLINK_ROOT "/home/gpio"
70
71 #define GPIO_SIGNAL_TYPE_EXTENSION "0"
72 #define GPIO_PIN_NUMBER_EXTENSION "2"
73
74 // Debug header signal type codes are offset by 0x100, the tuple below
75 // represents the range of valid codes for the debug header. The range
76 // starts at 0x100 and is 0x100 wide.
77 const int GPIO_DEBUG_HEADER_RANGE[2] = { 0x100, 0x100 };
78
79 // This dictionary maps GPIO signal types codes into their actual names.
80 const char *GPIO_SIGNAL_TYPES[] = {
81 NULL, // note: 0 is NOT a valid index, so we must
82 // check the range by GPIO_SIGNAL_TYPE_MIN/MAX.
83 "recovery_button", // index: 1
84 "developer_switch", // index: 2
85 "write_protect", // index: 3
86 };
87
88 // calculate the dimension and min/max values.
89 #define GPIO_SIGNAL_TYPES_DIM \
90 (sizeof(GPIO_SIGNAL_TYPES)/sizeof(GPIO_SIGNAL_TYPES[0]))
91 #define GPIO_SIGNAL_TYPE_MIN (1)
92 #define GPIO_SIGNAL_TYPE_MAX (GPIO_SIGNAL_TYPES_DIM-1)
93 // note: the above TYPE_MIN and TYPE_MAX refer to minimal and maximum valid
94 // index values, so the '1' in MAX definition (DIM-1) does not mean
95 // GPIO_SIGNAL_TYPE_MIN; it's for calculation to a 'zero-based array'.
96
97 ///////////////////////////////////////////////////////////////////////
98 // Utilities for quick python-C translation
99
100 using std::string;
101 #define GPIO_STRING_BUFFER_LEN (4096) // general buffer length
102
103 // Works like throwing an exception - directly exit here.
104 static void GpioSetupError(const char *message, ...) {
105 va_list args;
106 va_start(args, message);
107 vfprintf(stderr, message, args);
108 fprintf(stderr, "\n");
109 va_end(args);
110 exit(1);
111 }
112
113 // Return: python - open(filename).read().strip()
114 static string open_read_strip(const char *filename) {
115 FILE *fp = fopen(filename, "rt");
116 string result;
117 if (!fp)
118 return "";
119
120 // for virtual files (directly supported by kernel),
121 // it is better to 'read one line of text and strip'.
122 // for all files accessed by this utility should not
123 // contain very long text in first line, so using
124 // GPIO_STRING_BUFFER_LEN should be enough.
125 char buffer[GPIO_STRING_BUFFER_LEN] = "";
126
127 if (fgets(buffer, sizeof(buffer), fp) == NULL) {
128 perror(filename);
129 }
130 fclose(fp);
131
132 // now check leading and trailing spaces
133 char *head = buffer, *tail = head + strlen(head);
134 while (*head && isascii(*head) && isspace(*head))
135 head++;
136 while (tail > head && isascii(tail[-1]) && isspace(tail[-1]))
137 tail--;
138
139 return string(head, tail);
140 }
141
142 static string open_read_strip(const string &filename) {
143 return open_read_strip(filename.c_str());
144 }
145
146 static bool os_path_exists(const char *filename) {
147 return ::access(filename, 0) == 0;
148 }
149
150 static bool os_path_exists(const string &filename) {
151 return os_path_exists(filename.c_str());
152 }
153
154 static void os_symlink(const string &src, const string &dest) {
155 if (::symlink(src.c_str(), dest.c_str()) != 0) {
156 perror(src.c_str());
157 GpioSetupError("cannot create symlink (%s -> %s)",
158 src.c_str(), dest.c_str());
159 }
160 }
161
162 static string os_readlink(const string &filename) {
163 char buf[PATH_MAX] = "";
164 if (::readlink(filename.c_str(), buf, sizeof(buf)-1) == -1) {
165 perror(filename.c_str());
166 GpioSetupError("cannot read link (%s)", filename.c_str());
167 }
168 return buf;
169 }
170
171 static string os_path_abspath(const string &src) {
172 // TODO(hungte) there's no simple equivelent in POSIX.
173 // since this is for debug message only, let's ignore it.
174 return src;
175 }
176
177 static bool os_path_isdir(const char *filename) {
178 struct stat st = {0};
179 if (stat(filename, &st) != 0) {
180 perror(filename);
181 GpioSetupError("cannot query path status: %s", filename);
182 }
183 return S_ISDIR(st.st_mode);
184 }
185
186 static bool os_path_islink(const string &src) {
187 struct stat st = {0};
188 if (lstat(src.c_str(), &st) != 0) {
189 perror(src.c_str());
190 GpioSetupError("cannot query link status: %s", src.c_str());
191 }
192 return S_ISLNK(st.st_mode);
193 }
194
195 static string os_path_dirname(const string &filename) {
196 // XXX here a full non-directory input (dir+file) name is assumed.
197 assert(!os_path_isdir(filename.c_str()));
198 string newname = filename;
199 size_t pos_slash = newname.rfind('/');
200 if (pos_slash != newname.npos)
201 newname.erase(pos_slash);
202 return newname;
203 }
204
205 typedef std::vector<string> GlobResult;
206
207 static GlobResult glob_glob(const string &pattern) {
208 GlobResult r;
209 glob_t globtok = {0};
210 if (glob(pattern.c_str(),
211 GLOB_ERR | GLOB_TILDE, NULL, &globtok) == 0) {
212 for (size_t i = 0; i < globtok.gl_pathc; i++) {
213 r.push_back(globtok.gl_pathv[i]);
214 }
215 globfree(&globtok);
216 }
217 return r;
218 }
219
220 // Return: python - format % (...)
221 static string format_string(const char *format, ...) {
222 char buffer[GPIO_STRING_BUFFER_LEN]; // large enough for current version
223 va_list args;
224 va_start(args, format);
225 vsnprintf(buffer, sizeof(buffer), format, args);
226 va_end(args);
227 return buffer;
228 }
229
230 ///////////////////////////////////////////////////////////////////////
231
232 // Represent GPIO chip available through sys fs.
233 // Attributes:
234 // pci_address: a string, PCI address of this GPIO device
235 // base: a number, base global GPIO number of this device (mapped to pin zero
236 // in the device range)
237 // capacity: a number, shows the number of GPIO pins of this device.
238 // description: a multiline string description of this device, initialized
239 // after the device is attached. Can be used to dump device
240 // information.
241 class GpioChip {
242 public:
243 explicit GpioChip(const string &pci_address) {
244 pci_address_ = pci_address;
245 base_ = 0;
246 capacity_ = 0;
247 description_ = "not attached";
248 }
249
250 void Attach() {
251 string f;
252 GlobResult r = glob_glob(GPIO_DEVICE_ROOT "*/label");
253
254 for (GlobResult::iterator i = r.begin(); i != r.end(); ++i) {
255 string label = open_read_strip(*i);
256 if (label == pci_address_) {
257 f = *i;
258 break;
259 }
260 }
261
262 if (f.empty())
263 GpioSetupError("could not find GPIO PCI device %s", pci_address_.c_str());
264
265 string directory = os_path_dirname(f);
266 base_ = atoi(open_read_strip(directory + "/base").c_str());
267 capacity_ = atoi(open_read_strip(directory + "/ngpio").c_str());
268 description_ = format_string(
269 "GPIO device at PCI address %s\n"
270 "Base gpio pin %d\n"
271 "Capacity %d\n",
272 pci_address_.c_str(), base_, capacity_);
273 }
274
275 // Enable a certain GPIO pin.
276 // To enable the pin one needs to write its global GPIO number into
277 // /sys/class/gpio/export, if this pin has not been enabled yet.
278 // Inputs:
279 // pin: a number, zero based pin number within this device's range.
280 void EnablePin(int pin) {
281 if (pin >= capacity_)
282 GpioSetupError("pin %d exceeds capacity of %d", pin, capacity_);
283
284 // XXX in python version, this is named as 'global_gpio_number'
285 // although it's not really global.
286 int gpio_number = base_ + pin;
287
288 string target = format_string("%s/gpio%d", GPIO_ROOT, gpio_number);
289 if (!os_path_exists(target)) {
290 FILE *fp = fopen(GPIO_ENABLE_FILE, "w");
291 fprintf(fp, "%d", gpio_number);
292 fclose(fp);
293 }
294 }
295
296 // quick python-like translators
297 int base() const { return base_; }
298 int capacity() const { return capacity_; }
299
300 operator const char *() const {
301 return description_.c_str();
302 }
303
304 private:
305 string pci_address_;
306 int base_;
307 int capacity_;
308 string description_;
309
310 GpioChip() { }
311 };
312
313 typedef std::pair<string, int> AcpiMappingEntry;
314 typedef std::vector<AcpiMappingEntry> AcpiMapping;
315
316 // Scan ACPI information about GPIO and generate a mapping.
317 //
318 // Returns: a list of tuples, each tuple consisting of a string representing
319 // the GPIO pin name and a number, representing the GPIO pin within
320 // the GPIO device space.
321 AcpiMapping ParseAcpiMappings(const string &acpi_root) {
322 GlobResult r = glob_glob(format_string("%s/GPIO.[0-9]*", acpi_root.c_str()));
323 AcpiMapping acpi_gpio_mapping;
324 GlobResult::iterator i;
325 for (i = r.begin(); i != r.end(); i++) {
326 const char *d = i->c_str();
327 int signal_type = atoi(open_read_strip(
328 format_string("%s/GPIO.%s", d, GPIO_SIGNAL_TYPE_EXTENSION)).c_str());
329 int pin_number = atoi(open_read_strip(
330 format_string("%s/GPIO.%s", d, GPIO_PIN_NUMBER_EXTENSION)).c_str());
331
332 if (signal_type >= GPIO_SIGNAL_TYPE_MIN &&
333 signal_type <= static_cast<int>(GPIO_SIGNAL_TYPE_MAX) &&
334 GPIO_SIGNAL_TYPES[signal_type] != NULL) {
335 acpi_gpio_mapping.push_back(AcpiMappingEntry(
336 GPIO_SIGNAL_TYPES[signal_type], pin_number));
337 continue;
338 }
339
340 // This is not a specific signal, could be a debug header pin.
341 int debug_header = signal_type - GPIO_DEBUG_HEADER_RANGE[0];
342 if (debug_header >= 0 && debug_header < GPIO_DEBUG_HEADER_RANGE[1]) {
343 acpi_gpio_mapping.push_back(AcpiMappingEntry(
344 format_string("debug_header_%d", debug_header), pin_number));
345 continue;
346 }
347
348 // Unrecognized mapping, could happen if BIOS version is ahead of this
349 // script.
350 printf("unknown signal type encoding %d in %s\n", signal_type, d);
351 }
352
353 if (acpi_gpio_mapping.empty())
354 GpioSetupError("no gpio mapping found. Is ACPI driver installed?");
355
356 return acpi_gpio_mapping;
357 }
358
359 void CreateGpioSymlinks(const AcpiMapping& mappings,
360 GpioChip &gpio,
361 const char *symlink_root) {
362 if (!os_path_exists(symlink_root))
363 GpioSetupError("%s does not exist", symlink_root);
364
365 if (!os_path_isdir(symlink_root))
366 GpioSetupError("%s is not a directory", symlink_root);
367
368 if (access(symlink_root, W_OK) != 0)
369 GpioSetupError("%s is not writable", symlink_root);
370
371 if (chdir(symlink_root) != 0)
372 GpioSetupError("failed to change directory to %s", symlink_root);
373
374 AcpiMapping::const_iterator i;
375 for (i = mappings.begin(); i != mappings.end(); ++i) {
376 string symlink = i->first;
377 int pin = i->second;
378 gpio.EnablePin(pin);
379
380 string source_file = format_string("%s/gpio%d/value",
381 GPIO_ROOT, pin+gpio.base());
382 if (!os_path_exists(symlink)) {
383 os_symlink(source_file.c_str(), symlink.c_str());
384 continue;
385 }
386
387 if (!os_path_islink(symlink))
388 GpioSetupError("%s exists but is not a symlink",
389 os_path_abspath(symlink).c_str());
390
391 if (os_readlink(symlink) != source_file)
392 GpioSetupError("%s points to a wrong file",
393 os_path_abspath(symlink).c_str());
394 }
395 }
396
397 static const char *__name__ = "";
398 static void usage_help_exit(int ret) {
399 printf(
400 "Usage: %s [options]\n"
401 "\n"
402 "Options:\n"
403 " -h, --help \t show this help message and exit\n"
404 " --symlink_root=SYMLINK_ROOT\n"
405 " --pci_address=PCI_ADDRESS\n"
406 " --acpi_root=ACPI_ROOT\n", __name__);
407 exit(ret);
408 }
409
410 int main(int argc, char *argv[]) {
411 __name__ = argv[0];
412
413 struct GpioSetupOptions {
414 string pci_address,
415 symlink_root,
416 acpi_root;
417 } cmd_line_options;
418
419 // copy default values
420 cmd_line_options.pci_address = DEFAULT_GPIO_DEVICE_PCI_ADDRESS;
421 cmd_line_options.symlink_root = DEFAULT_SYMLINK_ROOT;
422 cmd_line_options.acpi_root = DEFAULT_ACPI_ROOT;
423
424 // ProcessOptions();
425 const struct option longopts[] = {
426 { "pci_address", 1, NULL, 'p' },
427 { "symlink_root", 1, NULL, 's' },
428 { "acpi_root", 1, NULL, 'a' },
429 { "help", 0, NULL, 'h'},
430 { 0 },
431 };
432
433 int optc;
434 while ((optc = getopt_long(argc, argv, "h", longopts, NULL)) != -1) {
435 switch (optc) {
436 case 'p':
437 cmd_line_options.pci_address = optarg;
438 break;
439
440 case 's':
441 cmd_line_options.symlink_root = optarg;
442 break;
443
444 case 'a':
445 cmd_line_options.acpi_root = optarg;
446 break;
447
448 default:
449 usage_help_exit(1);
450 break;
451 }
452 }
453
454 // currently no other non-dashed arguments allowed.
455 if (optind != argc)
456 usage_help_exit(1);
457
458 GpioChip gpioc(cmd_line_options.pci_address);
459 gpioc.Attach();
460 CreateGpioSymlinks(
461 ParseAcpiMappings(cmd_line_options.acpi_root),
462 gpioc, cmd_line_options.symlink_root.c_str());
463 return 0;
464 }
OLDNEW
« no previous file with comments | « Makefile ('k') | gpio_setup.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698