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)