| Index: gpio_setup.cc
|
| diff --git a/gpio_setup.cc b/gpio_setup.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ac14d8343d32f5e451b9c4cbe2594d550a6a4ce0
|
| --- /dev/null
|
| +++ b/gpio_setup.cc
|
| @@ -0,0 +1,464 @@
|
| +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +// NOTE: this file is translated from gpio_setup.py
|
| +//
|
| +// A script to create symlinks to platform specific GPIO pins.
|
| +//
|
| +// This script creates a set of symlinks pointing at the sys fs files returning
|
| +// the appropriate GPIO pin values. Each symlink is named to represent the
|
| +// actual GPIO pin function.
|
| +//
|
| +// The location of the symlinks generated by this script can be specified using
|
| +// the --symlink_root command line option. By default /home/gpio directory is
|
| +// used. The symlink directory must exist before this script is run.
|
| +//
|
| +// The GPIO pins' values are available through a GPIO device present in sys fs.
|
| +// The device is identified by its PCI bus address. The default PCI address of
|
| +// the GPIO device (set to 0:0:1f.0), can be changed using the --pci_address
|
| +// command line option.
|
| +//
|
| +// The platform specific bit usage of the GPIO device is derived from the ACPI,
|
| +// also using files found in a fixed location in sys fs. The default location of
|
| +// /sys/bus/platform/devices/chromeos_acpi could be changed using the
|
| +// --acpi_root command line option.
|
| +//
|
| +// Each GPIO pin is represented through ACPI as a subdirectory with several
|
| +// files in it. A typical name of the GPIO pin file looks as follows:
|
| +//
|
| +// <acpi_root>/GPIO.<instance>/GPIO.[0-3]
|
| +//
|
| +// where <instance> is a zero based number assigned to this GPIO pin by the BIOS
|
| +//
|
| +// In particular, file GPIO.0 represents encoded pin signal type (from which the
|
| +// symlink name is derived), and file GPIO.2 represents the actual zero based
|
| +// GPIO pin number within this GPIO device range.
|
| +//
|
| +// This script reads the ACPI provided mapping, enables the appropriate GPIO
|
| +// pins and creates symlinks mapping these GPIOs' values.
|
| +
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +#include <assert.h>
|
| +#include <sys/param.h>
|
| +#include <sys/types.h>
|
| +#include <sys/stat.h>
|
| +#include <unistd.h>
|
| +#include <stdarg.h>
|
| +#include <ctype.h>
|
| +#include <getopt.h>
|
| +#include <glob.h>
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +#include <map>
|
| +
|
| +#define GPIO_ROOT "/sys/class/gpio"
|
| +#define GPIO_DEVICE_ROOT GPIO_ROOT "/gpiochip"
|
| +#define GPIO_ENABLE_FILE GPIO_ROOT "export"
|
| +
|
| +// Can be changed using --pci_address command line option.
|
| +#define DEFAULT_GPIO_DEVICE_PCI_ADDRESS "0000:00:1f.0"
|
| +
|
| +// Can be changed using --acpi_root command line option.
|
| +#define DEFAULT_ACPI_ROOT "/sys/bus/platform/devices/chromeos_acpi"
|
| +
|
| +// can be changed using --symlink_root command line option.
|
| +#define DEFAULT_SYMLINK_ROOT "/home/gpio"
|
| +
|
| +#define GPIO_SIGNAL_TYPE_EXTENSION "0"
|
| +#define GPIO_PIN_NUMBER_EXTENSION "2"
|
| +
|
| +// Debug header signal type codes are offset by 0x100, the tuple below
|
| +// represents the range of valid codes for the debug header. The range
|
| +// starts at 0x100 and is 0x100 wide.
|
| +const int GPIO_DEBUG_HEADER_RANGE[2] = { 0x100, 0x100 };
|
| +
|
| +// This dictionary maps GPIO signal types codes into their actual names.
|
| +const char *GPIO_SIGNAL_TYPES[] = {
|
| + NULL, // note: 0 is NOT a valid index, so we must
|
| + // check the range by GPIO_SIGNAL_TYPE_MIN/MAX.
|
| + "recovery_button", // index: 1
|
| + "developer_switch", // index: 2
|
| + "write_protect", // index: 3
|
| +};
|
| +
|
| +// calculate the dimension and min/max values.
|
| +#define GPIO_SIGNAL_TYPES_DIM \
|
| + (sizeof(GPIO_SIGNAL_TYPES)/sizeof(GPIO_SIGNAL_TYPES[0]))
|
| +#define GPIO_SIGNAL_TYPE_MIN (1)
|
| +#define GPIO_SIGNAL_TYPE_MAX (GPIO_SIGNAL_TYPES_DIM-1)
|
| +// note: the above TYPE_MIN and TYPE_MAX refer to minimal and maximum valid
|
| +// index values, so the '1' in MAX definition (DIM-1) does not mean
|
| +// GPIO_SIGNAL_TYPE_MIN; it's for calculation to a 'zero-based array'.
|
| +
|
| +///////////////////////////////////////////////////////////////////////
|
| +// Utilities for quick python-C translation
|
| +
|
| +using std::string;
|
| +#define GPIO_STRING_BUFFER_LEN (4096) // general buffer length
|
| +
|
| +// Works like throwing an exception - directly exit here.
|
| +static void GpioSetupError(const char *message, ...) {
|
| + va_list args;
|
| + va_start(args, message);
|
| + vfprintf(stderr, message, args);
|
| + fprintf(stderr, "\n");
|
| + va_end(args);
|
| + exit(1);
|
| +}
|
| +
|
| +// Return: python - open(filename).read().strip()
|
| +static string open_read_strip(const char *filename) {
|
| + FILE *fp = fopen(filename, "rt");
|
| + string result;
|
| + if (!fp)
|
| + return "";
|
| +
|
| + // for virtual files (directly supported by kernel),
|
| + // it is better to 'read one line of text and strip'.
|
| + // for all files accessed by this utility should not
|
| + // contain very long text in first line, so using
|
| + // GPIO_STRING_BUFFER_LEN should be enough.
|
| + char buffer[GPIO_STRING_BUFFER_LEN] = "";
|
| +
|
| + if (fgets(buffer, sizeof(buffer), fp) == NULL) {
|
| + perror(filename);
|
| + }
|
| + fclose(fp);
|
| +
|
| + // now check leading and trailing spaces
|
| + char *head = buffer, *tail = head + strlen(head);
|
| + while (*head && isascii(*head) && isspace(*head))
|
| + head++;
|
| + while (tail > head && isascii(tail[-1]) && isspace(tail[-1]))
|
| + tail--;
|
| +
|
| + return string(head, tail);
|
| +}
|
| +
|
| +static string open_read_strip(const string &filename) {
|
| + return open_read_strip(filename.c_str());
|
| +}
|
| +
|
| +static bool os_path_exists(const char *filename) {
|
| + return ::access(filename, 0) == 0;
|
| +}
|
| +
|
| +static bool os_path_exists(const string &filename) {
|
| + return os_path_exists(filename.c_str());
|
| +}
|
| +
|
| +static void os_symlink(const string &src, const string &dest) {
|
| + if (::symlink(src.c_str(), dest.c_str()) != 0) {
|
| + perror(src.c_str());
|
| + GpioSetupError("cannot create symlink (%s -> %s)",
|
| + src.c_str(), dest.c_str());
|
| + }
|
| +}
|
| +
|
| +static string os_readlink(const string &filename) {
|
| + char buf[PATH_MAX] = "";
|
| + if (::readlink(filename.c_str(), buf, sizeof(buf)-1) == -1) {
|
| + perror(filename.c_str());
|
| + GpioSetupError("cannot read link (%s)", filename.c_str());
|
| + }
|
| + return buf;
|
| +}
|
| +
|
| +static string os_path_abspath(const string &src) {
|
| + // TODO(hungte) there's no simple equivelent in POSIX.
|
| + // since this is for debug message only, let's ignore it.
|
| + return src;
|
| +}
|
| +
|
| +static bool os_path_isdir(const char *filename) {
|
| + struct stat st = {0};
|
| + if (stat(filename, &st) != 0) {
|
| + perror(filename);
|
| + GpioSetupError("cannot query path status: %s", filename);
|
| + }
|
| + return S_ISDIR(st.st_mode);
|
| +}
|
| +
|
| +static bool os_path_islink(const string &src) {
|
| + struct stat st = {0};
|
| + if (lstat(src.c_str(), &st) != 0) {
|
| + perror(src.c_str());
|
| + GpioSetupError("cannot query link status: %s", src.c_str());
|
| + }
|
| + return S_ISLNK(st.st_mode);
|
| +}
|
| +
|
| +static string os_path_dirname(const string &filename) {
|
| + // XXX here a full non-directory input (dir+file) name is assumed.
|
| + assert(!os_path_isdir(filename.c_str()));
|
| + string newname = filename;
|
| + size_t pos_slash = newname.rfind('/');
|
| + if (pos_slash != newname.npos)
|
| + newname.erase(pos_slash);
|
| + return newname;
|
| +}
|
| +
|
| +typedef std::vector<string> GlobResult;
|
| +
|
| +static GlobResult glob_glob(const string &pattern) {
|
| + GlobResult r;
|
| + glob_t globtok = {0};
|
| + if (glob(pattern.c_str(),
|
| + GLOB_ERR | GLOB_TILDE, NULL, &globtok) == 0) {
|
| + for (size_t i = 0; i < globtok.gl_pathc; i++) {
|
| + r.push_back(globtok.gl_pathv[i]);
|
| + }
|
| + globfree(&globtok);
|
| + }
|
| + return r;
|
| +}
|
| +
|
| +// Return: python - format % (...)
|
| +static string format_string(const char *format, ...) {
|
| + char buffer[GPIO_STRING_BUFFER_LEN]; // large enough for current version
|
| + va_list args;
|
| + va_start(args, format);
|
| + vsnprintf(buffer, sizeof(buffer), format, args);
|
| + va_end(args);
|
| + return buffer;
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////
|
| +
|
| +// Represent GPIO chip available through sys fs.
|
| +// Attributes:
|
| +// pci_address: a string, PCI address of this GPIO device
|
| +// base: a number, base global GPIO number of this device (mapped to pin zero
|
| +// in the device range)
|
| +// capacity: a number, shows the number of GPIO pins of this device.
|
| +// description: a multiline string description of this device, initialized
|
| +// after the device is attached. Can be used to dump device
|
| +// information.
|
| +class GpioChip {
|
| + public:
|
| + explicit GpioChip(const string &pci_address) {
|
| + pci_address_ = pci_address;
|
| + base_ = 0;
|
| + capacity_ = 0;
|
| + description_ = "not attached";
|
| + }
|
| +
|
| + void Attach() {
|
| + string f;
|
| + GlobResult r = glob_glob(GPIO_DEVICE_ROOT "*/label");
|
| +
|
| + for (GlobResult::iterator i = r.begin(); i != r.end(); ++i) {
|
| + string label = open_read_strip(*i);
|
| + if (label == pci_address_) {
|
| + f = *i;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (f.empty())
|
| + GpioSetupError("could not find GPIO PCI device %s", pci_address_.c_str());
|
| +
|
| + string directory = os_path_dirname(f);
|
| + base_ = atoi(open_read_strip(directory + "/base").c_str());
|
| + capacity_ = atoi(open_read_strip(directory + "/ngpio").c_str());
|
| + description_ = format_string(
|
| + "GPIO device at PCI address %s\n"
|
| + "Base gpio pin %d\n"
|
| + "Capacity %d\n",
|
| + pci_address_.c_str(), base_, capacity_);
|
| + }
|
| +
|
| + // Enable a certain GPIO pin.
|
| + // To enable the pin one needs to write its global GPIO number into
|
| + // /sys/class/gpio/export, if this pin has not been enabled yet.
|
| + // Inputs:
|
| + // pin: a number, zero based pin number within this device's range.
|
| + void EnablePin(int pin) {
|
| + if (pin >= capacity_)
|
| + GpioSetupError("pin %d exceeds capacity of %d", pin, capacity_);
|
| +
|
| + // XXX in python version, this is named as 'global_gpio_number'
|
| + // although it's not really global.
|
| + int gpio_number = base_ + pin;
|
| +
|
| + string target = format_string("%s/gpio%d", GPIO_ROOT, gpio_number);
|
| + if (!os_path_exists(target)) {
|
| + FILE *fp = fopen(GPIO_ENABLE_FILE, "w");
|
| + fprintf(fp, "%d", gpio_number);
|
| + fclose(fp);
|
| + }
|
| + }
|
| +
|
| + // quick python-like translators
|
| + int base() const { return base_; }
|
| + int capacity() const { return capacity_; }
|
| +
|
| + operator const char *() const {
|
| + return description_.c_str();
|
| + }
|
| +
|
| + private:
|
| + string pci_address_;
|
| + int base_;
|
| + int capacity_;
|
| + string description_;
|
| +
|
| + GpioChip() { }
|
| +};
|
| +
|
| +typedef std::pair<string, int> AcpiMappingEntry;
|
| +typedef std::vector<AcpiMappingEntry> AcpiMapping;
|
| +
|
| +// Scan ACPI information about GPIO and generate a mapping.
|
| +//
|
| +// Returns: a list of tuples, each tuple consisting of a string representing
|
| +// the GPIO pin name and a number, representing the GPIO pin within
|
| +// the GPIO device space.
|
| +AcpiMapping ParseAcpiMappings(const string &acpi_root) {
|
| + GlobResult r = glob_glob(format_string("%s/GPIO.[0-9]*", acpi_root.c_str()));
|
| + AcpiMapping acpi_gpio_mapping;
|
| + GlobResult::iterator i;
|
| + for (i = r.begin(); i != r.end(); i++) {
|
| + const char *d = i->c_str();
|
| + int signal_type = atoi(open_read_strip(
|
| + format_string("%s/GPIO.%s", d, GPIO_SIGNAL_TYPE_EXTENSION)).c_str());
|
| + int pin_number = atoi(open_read_strip(
|
| + format_string("%s/GPIO.%s", d, GPIO_PIN_NUMBER_EXTENSION)).c_str());
|
| +
|
| + if (signal_type >= GPIO_SIGNAL_TYPE_MIN &&
|
| + signal_type <= static_cast<int>(GPIO_SIGNAL_TYPE_MAX) &&
|
| + GPIO_SIGNAL_TYPES[signal_type] != NULL) {
|
| + acpi_gpio_mapping.push_back(AcpiMappingEntry(
|
| + GPIO_SIGNAL_TYPES[signal_type], pin_number));
|
| + continue;
|
| + }
|
| +
|
| + // This is not a specific signal, could be a debug header pin.
|
| + int debug_header = signal_type - GPIO_DEBUG_HEADER_RANGE[0];
|
| + if (debug_header >= 0 && debug_header < GPIO_DEBUG_HEADER_RANGE[1]) {
|
| + acpi_gpio_mapping.push_back(AcpiMappingEntry(
|
| + format_string("debug_header_%d", debug_header), pin_number));
|
| + continue;
|
| + }
|
| +
|
| + // Unrecognized mapping, could happen if BIOS version is ahead of this
|
| + // script.
|
| + printf("unknown signal type encoding %d in %s\n", signal_type, d);
|
| + }
|
| +
|
| + if (acpi_gpio_mapping.empty())
|
| + GpioSetupError("no gpio mapping found. Is ACPI driver installed?");
|
| +
|
| + return acpi_gpio_mapping;
|
| +}
|
| +
|
| +void CreateGpioSymlinks(const AcpiMapping& mappings,
|
| + GpioChip &gpio,
|
| + const char *symlink_root) {
|
| + if (!os_path_exists(symlink_root))
|
| + GpioSetupError("%s does not exist", symlink_root);
|
| +
|
| + if (!os_path_isdir(symlink_root))
|
| + GpioSetupError("%s is not a directory", symlink_root);
|
| +
|
| + if (access(symlink_root, W_OK) != 0)
|
| + GpioSetupError("%s is not writable", symlink_root);
|
| +
|
| + if (chdir(symlink_root) != 0)
|
| + GpioSetupError("failed to change directory to %s", symlink_root);
|
| +
|
| + AcpiMapping::const_iterator i;
|
| + for (i = mappings.begin(); i != mappings.end(); ++i) {
|
| + string symlink = i->first;
|
| + int pin = i->second;
|
| + gpio.EnablePin(pin);
|
| +
|
| + string source_file = format_string("%s/gpio%d/value",
|
| + GPIO_ROOT, pin+gpio.base());
|
| + if (!os_path_exists(symlink)) {
|
| + os_symlink(source_file.c_str(), symlink.c_str());
|
| + continue;
|
| + }
|
| +
|
| + if (!os_path_islink(symlink))
|
| + GpioSetupError("%s exists but is not a symlink",
|
| + os_path_abspath(symlink).c_str());
|
| +
|
| + if (os_readlink(symlink) != source_file)
|
| + GpioSetupError("%s points to a wrong file",
|
| + os_path_abspath(symlink).c_str());
|
| + }
|
| +}
|
| +
|
| +static const char *__name__ = "";
|
| +static void usage_help_exit(int ret) {
|
| + printf(
|
| + "Usage: %s [options]\n"
|
| + "\n"
|
| + "Options:\n"
|
| + " -h, --help \t show this help message and exit\n"
|
| + " --symlink_root=SYMLINK_ROOT\n"
|
| + " --pci_address=PCI_ADDRESS\n"
|
| + " --acpi_root=ACPI_ROOT\n", __name__);
|
| + exit(ret);
|
| +}
|
| +
|
| +int main(int argc, char *argv[]) {
|
| + __name__ = argv[0];
|
| +
|
| + struct GpioSetupOptions {
|
| + string pci_address,
|
| + symlink_root,
|
| + acpi_root;
|
| + } cmd_line_options;
|
| +
|
| + // copy default values
|
| + cmd_line_options.pci_address = DEFAULT_GPIO_DEVICE_PCI_ADDRESS;
|
| + cmd_line_options.symlink_root = DEFAULT_SYMLINK_ROOT;
|
| + cmd_line_options.acpi_root = DEFAULT_ACPI_ROOT;
|
| +
|
| + // ProcessOptions();
|
| + const struct option longopts[] = {
|
| + { "pci_address", 1, NULL, 'p' },
|
| + { "symlink_root", 1, NULL, 's' },
|
| + { "acpi_root", 1, NULL, 'a' },
|
| + { "help", 0, NULL, 'h'},
|
| + { 0 },
|
| + };
|
| +
|
| + int optc;
|
| + while ((optc = getopt_long(argc, argv, "h", longopts, NULL)) != -1) {
|
| + switch (optc) {
|
| + case 'p':
|
| + cmd_line_options.pci_address = optarg;
|
| + break;
|
| +
|
| + case 's':
|
| + cmd_line_options.symlink_root = optarg;
|
| + break;
|
| +
|
| + case 'a':
|
| + cmd_line_options.acpi_root = optarg;
|
| + break;
|
| +
|
| + default:
|
| + usage_help_exit(1);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // currently no other non-dashed arguments allowed.
|
| + if (optind != argc)
|
| + usage_help_exit(1);
|
| +
|
| + GpioChip gpioc(cmd_line_options.pci_address);
|
| + gpioc.Attach();
|
| + CreateGpioSymlinks(
|
| + ParseAcpiMappings(cmd_line_options.acpi_root),
|
| + gpioc, cmd_line_options.symlink_root.c_str());
|
| + return 0;
|
| +}
|
|
|