c++ version of the firmware utilities.
This was translated from the original python scripts.
For more information (including the test procedure) please refer to http://codereview.chromium.org/2421006/show
Review URL: http://codereview.chromium.org/2656004
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..73b14c0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+# 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.
+
+CXXFLAGS += -fPIC -Wall -Werror
+
+TARGETS = gpio_setup reboot_mode
+
+%: %.cc
+ $(CXX) $(CXXFLAGS) -o $@ $^
+
+all: $(TARGETS)
+
+clean:
+ @rm -f $(TARGETS)
diff --git a/gpio_setup.cc b/gpio_setup.cc
new file mode 100644
index 0000000..ac14d83
--- /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;
+}
diff --git a/gpio_setup.py b/gpio_setup.py
deleted file mode 100644
index 3c71b75..0000000
--- a/gpio_setup.py
+++ /dev/null
@@ -1,230 +0,0 @@
-#!/usr/bin/python
-
-# 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.
-
-"""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.
-"""
-
-__author__ = 'The Chromium OS Authors'
-
-import glob
-import os
-import optparse
-import sys
-
-GPIO_ROOT = '/sys/class/gpio'
-GPIO_DEVICE_ROOT = GPIO_ROOT + '/gpiochip'
-GPIO_ENABLE_FILE = '/sys/class/gpio/' + 'export'
-
-# Can be changed using --pci_address command line option.
-GPIO_DEVICE_PCI_ADDRESS = '0000:00:1f.0'
-
-# Can be changed using --acpi_root command line option.
-ACPI_ROOT = '/sys/bus/platform/devices/chromeos_acpi'
-GPIO_SIGNAL_TYPE_EXTENSION = '0'
-GPIO_PIN_NUMBER_EXTENSION = '2'
-
-# can be changed using --symlink_root command line option.
-DEFAULT_SYMLINK_ROOT = '/home/gpio'
-
-# This dictionary maps GPIO signal types codes into their actual names.
-GPIO_SIGNAL_TYPES = {
- 1: 'recovery_button',
- 2: 'developer_switch',
- 3: 'write_protect'
- }
-
-# 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.
-GPIO_DEBUG_HEADER_RANGE = (0x100, 0x100)
-
-# This is used to prepare the option parser: each element is a tuple of
-# strings, including the option name and the option default value.
-option_list = (('symlink_root', DEFAULT_SYMLINK_ROOT),
- ('pci_address', GPIO_DEVICE_PCI_ADDRESS),
- ('acpi_root', ACPI_ROOT))
-
-# This becomes the option object after the command line is parsed.
-cmd_line_options = None
-
-class GpioSetupError(Exception):
- pass
-
-
-class GpioChip(object):
- """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.
- """
-
- def __init__(self, pci_address):
- self.pci_address = pci_address
- self.base = 0
- self.capacity = 0
- self.description = 'not attached'
-
- def Attach(self):
- for f in glob.glob(GPIO_DEVICE_ROOT + '*/label'):
- label = open(f).read().strip()
- if label == self.pci_address:
- break
- else:
- raise GpioSetupError(
- 'could not find GPIO PCI device %s' % self.pci_address)
- directory = os.path.dirname(f)
- self.base = int(open(directory + '/base').read())
- self.capacity = int(open(directory + '/ngpio').read())
- self.description = '\n'.join(['GPIO device at PCI address %s' %
- self.pci_address,
- 'Base gpio pin %d' % self.base,
- 'Capacity %d' % self.capacity])
-
- def EnablePin(self, pin):
- """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.
- """
-
- if pin >= self.capacity:
- raise GpioSetupError('pin %d exceeds capacity of %d' % (
- pin, self.capacity))
- global_gpio_number = self.base + pin
- if not os.path.exists('%s/gpio%d' % (GPIO_ROOT, global_gpio_number)):
- open(GPIO_ENABLE_FILE, 'w').write('%d' % (global_gpio_number))
-
- def __str__(self):
- return self.description
-
-
-def ParseAcpiMappings():
- """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.
- """
- acpi_gpio_mapping = []
- for d in glob.glob('%s/GPIO.[0-9]*' % cmd_line_options.acpi_root):
- signal_type = int(open('%s/GPIO.%s' % (
- d, GPIO_SIGNAL_TYPE_EXTENSION)).read())
-
- pin_number = int(open('%s/GPIO.%s' % (
- d, GPIO_PIN_NUMBER_EXTENSION)).read())
-
- if signal_type in GPIO_SIGNAL_TYPES:
- acpi_gpio_mapping.append((GPIO_SIGNAL_TYPES[signal_type], pin_number))
- continue
-
- # This is not a specific signal, could be a debug header pin.
- debug_header = signal_type - GPIO_DEBUG_HEADER_RANGE[0]
- if debug_header >= 0 and debug_header < GPIO_DEBUG_HEADER_RANGE[1]:
- acpi_gpio_mapping.append(('debug_header_%d' % debug_header, pin_number))
- continue
-
- # Unrecognized mapping, could happen if BIOS version is ahead of this
- # script.
- print 'unknown signal type encoding %d in %d' % (signal_type, d)
-
- if not acpi_gpio_mapping:
- raise GpioSetupError('no gpio mapping found. Is ACPI driver installed?')
-
- return acpi_gpio_mapping
-
-
-def CreateGpioSymlinks(mappings, gpio, symlink_root):
- if not os.path.exists(symlink_root):
- raise GpioSetupError('%s does not exist' % symlink_root)
-
- if not os.path.isdir(symlink_root):
- raise GpioSetupError('%s is not a directory' % symlink_root)
-
- if not os.access(symlink_root, os.W_OK):
- raise GpioSetupError('%s is not writable' % symlink_root)
-
- try:
- os.chdir(symlink_root)
- except OSError:
- raise GpioSetupError('failed to change directory to %s' % symlink_root)
-
- for (symlink, pin) in mappings:
- gpio.EnablePin(pin)
- source_file = '%s/gpio%d/value' % (GPIO_ROOT, pin + gpio.base)
- if not os.path.exists(symlink):
- os.symlink(source_file, symlink)
- continue
- if not os.path.islink(symlink):
- raise GpioSetupError(
- '%s exists but is not a symlink' % os.path.abspath(symlink))
- if os.readlink(symlink) != source_file:
- raise GpioSetupError(
- '%s points to a wrong file' % os.path.abspath(symlink))
-
-
-def ProcessOptions():
- global cmd_line_options
- parser = optparse.OptionParser()
- for (name, default_value) in option_list:
- parser.add_option('--' + name, dest=name, default=default_value)
- (cmd_line_options, _) = parser.parse_args()
-
-
-def main():
- ProcessOptions()
- gpioc = GpioChip(cmd_line_options.pci_address)
- gpioc.Attach()
- CreateGpioSymlinks(ParseAcpiMappings(), gpioc, cmd_line_options.symlink_root)
-
-
-if __name__ == '__main__':
- try:
- main()
- except GpioSetupError, e:
- print >> sys.stderr, '%s: %s' % (sys.argv[0].split('/')[-1], e)
- sys.exit(1)
- sys.exit(0)
diff --git a/reboot_mode.cc b/reboot_mode.cc
new file mode 100644
index 0000000..57a5bc8
--- /dev/null
+++ b/reboot_mode.cc
@@ -0,0 +1,298 @@
+// 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 reboot_mode.py
+//
+// A script to manipulate ChromeOS CMOS reboot Field.
+//
+// A few bits in a byte in CMOS (called RDB, short for 'Reboot Data Byte'
+// hereafter) are dedicated to information exchange between Linux and BIOS.
+//
+// The CMOS contents are available through /dev/nvrom. The location of RDB is
+// reported by BIOS through ACPI. The few bits in RDB operated on by this script
+// are defined in the all_mode_fields dictionary below along with their
+// functions.
+//
+// When invoked without parameters this script prints values of the fields. When
+// invoked with any of the bit field names as parameter(s), the script sets the
+// bit(s) as required.
+//
+// Additional parameters allow to specify alternative ACPI and NVRAM files for
+// testing.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <getopt.h>
+
+#include <string>
+#include <algorithm>
+#include <vector>
+#include <map>
+
+#define DEFAULT_ACPI_FILE "/sys/devices/platform/chromeos_acpi/CHNV"
+#define DEFAULT_NVRAM_FILE "/dev/nvram"
+
+///////////////////////////////////////////////////////////////////////
+// Utilities for quick python-C translation
+
+using std::string;
+
+// Works like throwing an exception - directly exit here.
+static void RebootModeError(const char *message, ...) {
+ va_list args;
+ va_start(args, message);
+ vfprintf(stderr, message, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(1);
+}
+
+// return a string which is upper case of input parameter.
+static string str_to_upper(const string str) {
+ string newstr = str;
+ std::transform(str.begin(), str.end(), newstr.begin(), toupper);
+ return newstr;
+}
+
+///////////////////////////////////////////////////////////////////////
+
+typedef std::map<string, uint8_t> RDB_BitField;
+
+struct RebootModeConfig {
+ // file path
+ string acpi_file;
+ string nvram_file;
+
+ // Offset of RDB in NVRAM
+ int rdb_offset;
+
+ // NVRAM contents read on startup
+ uint8_t rdb_value;
+
+ // A dictionary of fields to be updated as requested by the command line
+ // parameters
+ RDB_BitField fields_to_set;
+
+ // All bitfields in RDB this script provides access to. Field names prepended
+ // by `--' become this script's command line options
+ RDB_BitField all_mode_fields;
+
+ // write constructure here to ease python translation
+ RebootModeConfig() {
+ acpi_file = DEFAULT_ACPI_FILE;
+ nvram_file = DEFAULT_NVRAM_FILE;
+ rdb_offset = 0;
+ all_mode_fields["recovery"] = 0x80;
+ all_mode_fields["debug_reset"] = 0x40;
+ all_mode_fields["try_firmware_b"] = 0x20;
+ }
+} cfg; // this is a special global object to ease python translation.
+
+// Process bit field name command line parameter.
+//
+// Verifies that the value is in range (0 or 1) and adds the appropriate
+// element to fields_to_set dictionary. Should the same field specified in the
+// command line more than once, only the last value will be used.
+//
+// Raises:
+// RebootModeError in case the parameter value is out of range.
+void OptionHandler(const char *opt_str, const char *value_str) {
+ const char *key = opt_str;
+ int value = atoi(value_str);
+
+ if (!value && *value_str != '0')
+ RebootModeError("--%s needs numeric argument value", key);
+
+ if (value < 0 || value > 1)
+ RebootModeError("--%s should be either 0 or 1", key);
+
+ if (cfg.all_mode_fields.find(key) == cfg.all_mode_fields.end())
+ RebootModeError("INTERNAL ERROR: UNKNOWN FIELD: %s", key);
+
+ cfg.fields_to_set[key] = value;
+}
+
+void GetRDBOffset() {
+ const char *acpi_file = cfg.acpi_file.c_str();
+
+ FILE *f = fopen(acpi_file, "rb");
+ if (!f) {
+ perror(acpi_file);
+ RebootModeError("Cannot read acpi_file: %s", acpi_file);
+ }
+
+ char buffer[PATH_MAX]; // this should be large enough
+ if (fgets(buffer, sizeof(buffer), f) == NULL) {
+ perror(acpi_file);
+ RebootModeError("Trying to read %s but failed.", acpi_file);
+ }
+
+ cfg.rdb_offset = atoi(buffer);
+ if (!cfg.rdb_offset && *buffer != '0') { // consider this as error
+ RebootModeError("%s contents are corrupted", acpi_file);
+ }
+}
+
+void ReadNvram() {
+ FILE *f = fopen(cfg.nvram_file.c_str(), "rb");
+ if (!f) {
+ if (errno == 2)
+ RebootModeError("%s does not exist. Is nvram module installed?",
+ cfg.nvram_file.c_str());
+ if (errno == 13)
+ RebootModeError("Access to %s denied. Are you root?",
+ cfg.nvram_file.c_str());
+ // all other error
+ perror(cfg.nvram_file.c_str());
+ }
+
+ if (fseek(f, cfg.rdb_offset, SEEK_SET) == -1 ||
+ fread(&cfg.rdb_value, 1, 1, f) != 1) {
+ perror(cfg.nvram_file.c_str());
+ RebootModeError("Failed reading mode byte from %s", cfg.nvram_file.c_str());
+ }
+ fclose(f);
+}
+
+void PrintCurrentMode() {
+ printf("Current reboot mode settings:\n");
+ for (RDB_BitField::iterator i = cfg.all_mode_fields.begin();
+ i != cfg.all_mode_fields.end();
+ ++i) {
+ printf("%-15s: %d\n", i->first.c_str(),
+ (cfg.rdb_value & i->second) ? 1 : 0);
+ }
+}
+
+void SetNewMode() {
+ uint8_t new_mode = 0;
+ uint8_t updated_bits_mask = 0;
+
+ for (RDB_BitField::iterator i = cfg.fields_to_set.begin();
+ i != cfg.fields_to_set.end();
+ ++i) {
+ string opt = i->first;
+ uint8_t value = i->second;
+ assert(cfg.all_mode_fields.find(opt) != cfg.all_mode_fields.end());
+
+ uint8_t mask = cfg.all_mode_fields[opt];
+ if (value)
+ new_mode |= mask;
+
+ updated_bits_mask |= mask;
+ }
+
+ if ((cfg.rdb_value & updated_bits_mask) == new_mode) {
+ printf("No update required\n");
+ return;
+ }
+
+ FILE *f = fopen(cfg.nvram_file.c_str(), "rb+");
+ fseek(f, cfg.rdb_offset, SEEK_SET);
+ new_mode |= (cfg.rdb_value & ~updated_bits_mask);
+ if (fwrite(&new_mode, 1, 1, f) != 1) {
+ RebootModeError("Failed writing mode byte to %s", cfg.nvram_file.c_str());
+ }
+ fclose(f);
+}
+
+const char *__name__;
+
+void usage_help_exit(int err) {
+ printf("Usage: %s [options]\n"
+ "\n"
+ "Options:\n"
+ " -h, --help\t\tshow this help message and exit\n"
+ " --acpi_file=ACPI_FILE\n"
+ " --nvram_file=NVRAM_FILE\n"
+ , __name__);
+
+ // list all possible fields
+ for (RDB_BitField::iterator i = cfg.all_mode_fields.begin();
+ i != cfg.all_mode_fields.end();
+ ++i) {
+ printf(" --%s=%s\n",
+ i->first.c_str(),
+ str_to_upper(i->first).c_str());
+ }
+ exit(err);
+}
+
+int main(int argc, char *argv[]) {
+ __name__ = argv[0];
+
+ struct option optv = {0};
+ std::vector<struct option> longopts;
+ int max_field_index = 0;
+
+ optv.has_arg = 0;
+ optv.val = 0;
+ optv.name = "help"; // value = 0
+ longopts.push_back(optv);
+ optv.has_arg = 1;
+
+ optv.val++;
+ optv.name = "acpi_file"; // value = 1
+ longopts.push_back(optv);
+ optv.val++;
+ optv.name = "nvram_file"; // value = 2
+ longopts.push_back(optv);
+
+ // add all known RDB fields
+ for (RDB_BitField::iterator i = cfg.all_mode_fields.begin();
+ i != cfg.all_mode_fields.end();
+ ++i) {
+ optv.name = i->first.c_str();
+ optv.val++;
+ longopts.push_back(optv);
+ }
+ max_field_index = optv.val;
+
+ // add a zero for closing options
+ optv.name = NULL;
+ optv.val = optv.has_arg = 0;
+ longopts.push_back(optv);
+
+ int optc;
+ while ((optc = getopt_long(argc, argv, "h", &longopts.front(), NULL)) != -1) {
+ switch (optc) {
+ case 1:
+ cfg.acpi_file = optarg;
+ break;
+
+ case 2:
+ cfg.nvram_file = optarg;
+ break;
+
+ default:
+ // RDB fields?
+ // printf("optc: %d %s %s\n", optc, longopts[optc].name, optarg);
+ if (optc > 0 && optc <= max_field_index)
+ OptionHandler(longopts[optc].name, optarg);
+ else
+ usage_help_exit(1);
+ break;
+ }
+ }
+
+ // currently no other non-dashed arguments allowed.
+ if (optind != argc)
+ usage_help_exit(1);
+
+ GetRDBOffset();
+ ReadNvram();
+
+ if (!cfg.fields_to_set.empty()) {
+ SetNewMode();
+ } else {
+ PrintCurrentMode();
+ }
+ return 0;
+}
diff --git a/reboot_mode.py b/reboot_mode.py
deleted file mode 100644
index 18f336f..0000000
--- a/reboot_mode.py
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/usr/bin/python
-
-# 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.
-
-"""A script to manipulate ChromeOS CMOS reboot Field.
-
-A few bits in a byte in CMOS (called RDB, short for 'Reboot Data Byte'
-hereafter) are dedicated to information exchange between Linux and BIOS.
-
-The CMOS contents are available through /dev/nvrom. The location of RDB is
-reported by BIOS through ACPI. The few bits in RDB operated on by this script
-are defined in the all_mode_fields dictionary below along with their
-functions.
-
-When invoked without parameters this script prints values of the fields. When
-invoked with any of the bit field names as parameter(s), the script sets the
-bit(s) as required.
-
-Additional parameters allow to specify alternative ACPI and NVRAM files for
-testing.
-"""
-
-__author__ = 'The Chromium OS Authors'
-
-import optparse
-import sys
-
-class RebootModeError(Exception):
- pass
-
-# global variables
-parser = optparse.OptionParser()
-acpi_file = '/sys/devices/platform/chromeos_acpi/CHNV'
-nvram_file = '/dev/nvram'
-
-# Offset of RDB in NVRAM
-rdb_offset = 0
-
-# NVRAM contents read on startup
-rdb_value = ''
-
-# All bitfields in RDB this script provides access to. Field names prepended
-# by `--' become this script's command line options
-all_mode_fields = {
- 'recovery' : 0x80,
- 'debug_reset' : 0x40,
- 'try_firmware_b': 0x20
- }
-
-# A dictionary of fields to be updated as requested by the command line
-# parameters
-fields_to_set = {}
-
-
-def OptionHandler(option, opt_str, value, p):
- """Process bit field name command line parameter.
-
- Verifies that the value is in range (0 or 1) and adds the appropriate
- element to fields_to_set dictionary. Should the same field specified in the
- command line more than once, only the last value will be used.
-
- Raises:
- RebootModeError in case the parameter value is out of range.
- """
-
- global fields_to_set
- key = opt_str[2:]
- if value < 0 or value > 1:
- raise RebootModeError('--%s should be either 0 or 1' % key)
-
- fields_to_set[key] = value
-
-
-def PrepareOptions():
- global parser
- for field in all_mode_fields:
- parser.add_option('--' + field, action='callback', type='int',
- callback=OptionHandler)
- parser.add_option('--acpi_file', dest='acpi_file')
- parser.add_option('--nvram_file', dest='nvram_file')
-
-
-def ParseOptions():
- global acpi_file
- global nvram_file
- (opts, args) = parser.parse_args()
-
- if opts.acpi_file is not None:
- acpi_file = opts.acpi_file
-
- if opts.nvram_file is not None:
- nvram_file = opts.nvram_file
-
-
-def GetRDBOffset():
- global rdb_offset
- try:
- f = open(acpi_file, 'r')
- rdb_offset = int(f.read())
- f.close()
- except IOError, e:
- raise RebootModeError('Trying to read %s got this: %s' % (acpi_file, e))
- except ValueError:
- raise RebootModeError('%s contents are corrupted' % acpi_file)
-
-
-def ReadNvram():
- global rdb_value
- try:
- f = open(nvram_file, 'rb')
- f.seek(rdb_offset)
- rdb_value = ord(f.read(1)[0])
- f.close()
- except IOError, e:
- if e[0] == 2:
- raise RebootModeError(
- '%s does not exist. Is nvram module installed?' % nvram_file)
- if e[0] == 13:
- raise RebootModeError('Access to %s denied. Are you root?' % nvram_file)
- raise RebootModeError(e[1])
- except IndexError, e:
- raise RebootModeError('Failed reading mode byte from %s' % nvram_file)
-
-
-def PrintCurrentMode():
- print 'Current reboot mode settings:'
- for (field, mask) in all_mode_fields.iteritems():
- print '%-15s: %d' % (field, int(not not (rdb_value & mask)))
-
-
-def SetNewMode():
- new_mode = 0
- updated_bits_mask = 0
-
- for (opt, value) in fields_to_set.iteritems():
- mask = all_mode_fields[opt]
- if value:
- new_mode |= mask
- updated_bits_mask |= mask
-
- if (rdb_value & updated_bits_mask) == new_mode:
- print 'No update required'
- return
-
- f = open(nvram_file, 'rb+')
- f.seek(rdb_offset)
- new_mode |= (rdb_value & ~updated_bits_mask)
- f.write('%c' % new_mode)
- f.close()
-
-
-def DoRebootMode():
- PrepareOptions()
- ParseOptions()
- GetRDBOffset()
- ReadNvram()
- if fields_to_set:
- SetNewMode()
- else:
- PrintCurrentMode()
-
-
-if __name__ == '__main__':
- try:
- DoRebootMode()
- except RebootModeError, e:
- print >> sys.stderr, '%s: %s' % (sys.argv[0].split('/')[-1], e)
- sys.exit(1)
- sys.exit(0)