| Index: gpio_setup.cc | 
| diff --git a/gpio_setup.cc b/gpio_setup.cc | 
| index 31fa39e1ff7584cc3c61fcd3c6b04e909e87afc9..ee728472671e5d486bff5281f4a683445f4b933f 100644 | 
| --- a/gpio_setup.cc | 
| +++ b/gpio_setup.cc | 
| @@ -69,6 +69,7 @@ | 
| #define DEFAULT_SYMLINK_ROOT "/home/gpio" | 
|  | 
| #define GPIO_SIGNAL_TYPE_EXTENSION      "0" | 
| +#define GPIO_ATTRIBUTES_EXTENSION       "1" | 
| #define GPIO_PIN_NUMBER_EXTENSION       "2" | 
|  | 
| // Debug header signal type codes are offset by 0x100, the tuple below | 
| @@ -310,7 +311,12 @@ class GpioChip { | 
| GpioChip() { } | 
| }; | 
|  | 
| -typedef std::pair<string, int> AcpiMappingEntry; | 
| +typedef struct { | 
| +  string name; | 
| +  int index; | 
| +  int pin_number; | 
| +} AcpiMappingEntry; | 
| + | 
| typedef std::vector<AcpiMappingEntry> AcpiMapping; | 
|  | 
| // Scan ACPI information about GPIO and generate a mapping. | 
| @@ -322,26 +328,32 @@ 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++) { | 
| +    AcpiMappingEntry entry; | 
| const char *d = i->c_str(); | 
| +    const char *dot_index_string = strrchr(d, '.'); | 
| 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( | 
| + | 
| +    assert(dot_index_string); | 
| +    entry.index = atoi(dot_index_string + 1); | 
| +    entry.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)); | 
| +      entry.name = GPIO_SIGNAL_TYPES[signal_type]; | 
| +      acpi_gpio_mapping.push_back(entry); | 
| 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)); | 
| +      entry.name = format_string("debug_header_%d", debug_header); | 
| +      acpi_gpio_mapping.push_back(AcpiMappingEntry(entry)); | 
| continue; | 
| } | 
|  | 
| @@ -356,8 +368,23 @@ AcpiMapping ParseAcpiMappings(const string &acpi_root) { | 
| return acpi_gpio_mapping; | 
| } | 
|  | 
| +void CreateSymLink(const string &source_file, const string &symlink) { | 
| +    if (!os_path_exists(symlink)) { | 
| +      os_symlink(source_file.c_str(), symlink.c_str()); | 
| +      return; | 
| +    } | 
| + | 
| +    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()); | 
| +} | 
| + | 
| void CreateGpioSymlinks(const AcpiMapping& mappings, | 
| GpioChip &gpio, | 
| +                        const char *acpi_root, | 
| const char *symlink_root) { | 
| if (!os_path_exists(symlink_root)) | 
| GpioSetupError("%s does not exist", symlink_root); | 
| @@ -373,24 +400,20 @@ void CreateGpioSymlinks(const AcpiMapping& mappings, | 
|  | 
| 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()); | 
| +    string symlink, source_file; | 
| +    gpio.EnablePin(i->pin_number); | 
| + | 
| +    symlink = i->name; | 
| +    source_file = format_string("%s/gpio%d/value", GPIO_ROOT, | 
| +                                i->pin_number + gpio.base()); | 
| +    CreateSymLink(source_file, symlink); | 
| + | 
| +    symlink = i->name + ".attr"; | 
| +    source_file = format_string("%s/GPIO.%d/GPIO.%s", | 
| +                                acpi_root, | 
| +                                i->index, | 
| +                                GPIO_ATTRIBUTES_EXTENSION); | 
| +    CreateSymLink(source_file, symlink); | 
| } | 
| } | 
|  | 
| @@ -459,6 +482,8 @@ int main(int argc, char *argv[]) { | 
| gpioc.Attach(); | 
| CreateGpioSymlinks( | 
| ParseAcpiMappings(cmd_line_options.acpi_root), | 
| -      gpioc, cmd_line_options.symlink_root.c_str()); | 
| +      gpioc, | 
| +      cmd_line_options.acpi_root.c_str(), | 
| +      cmd_line_options.symlink_root.c_str()); | 
| return 0; | 
| } | 
|  |