OLD | NEW |
(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 } |
OLD | NEW |