| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python | |
| 2 | |
| 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 """A script to create symlinks to platform specific GPIO pins. | |
| 8 | |
| 9 This script creates a set of symlinks pointing at the sys fs files returning | |
| 10 the appropriate GPIO pin values. Each symlink is named to represent the actual | |
| 11 GPIO pin function. | |
| 12 | |
| 13 The location of the symlinks generated by this script can be specified using | |
| 14 the --symlink_root command line option. By default /home/gpio directory is | |
| 15 used. The symlink directory must exist before this script is run. | |
| 16 | |
| 17 The GPIO pins' values are available through a GPIO device present in sys fs. | |
| 18 The device is identified by its PCI bus address. The default PCI address of | |
| 19 the GPIO device (set to 0:0:1f.0), can be changed using the --pci_address | |
| 20 command line option. | |
| 21 | |
| 22 The platform specific bit usage of the GPIO device is derived from the ACPI, | |
| 23 also using files found in a fixed location in sys fs. The default location of | |
| 24 /sys/bus/platform/devices/chromeos_acpi could be changed using the --acpi_root | |
| 25 command line option. | |
| 26 | |
| 27 Each GPIO pin is represented through ACPI as a subdirectory with several files | |
| 28 in it. A typical name of the GPIO pin file looks as follows: | |
| 29 | |
| 30 <acpi_root>/GPIO.<instance>/GPIO.[0-3] | |
| 31 | |
| 32 where <instance> is a zero based number assigned to this GPIO pin by the BIOS. | |
| 33 | |
| 34 In particular, file GPIO.0 represents encoded pin signal type (from which the | |
| 35 symlink name is derived), and file GPIO.2 represents the actual zero based | |
| 36 GPIO pin number within this GPIO device range. | |
| 37 | |
| 38 This script reads the ACPI provided mapping, enables the appropriate GPIO pins | |
| 39 and creates symlinks mapping these GPIOs' values. | |
| 40 """ | |
| 41 | |
| 42 __author__ = 'The Chromium OS Authors' | |
| 43 | |
| 44 import glob | |
| 45 import os | |
| 46 import optparse | |
| 47 import sys | |
| 48 | |
| 49 GPIO_ROOT = '/sys/class/gpio' | |
| 50 GPIO_DEVICE_ROOT = GPIO_ROOT + '/gpiochip' | |
| 51 GPIO_ENABLE_FILE = '/sys/class/gpio/' + 'export' | |
| 52 | |
| 53 # Can be changed using --pci_address command line option. | |
| 54 GPIO_DEVICE_PCI_ADDRESS = '0000:00:1f.0' | |
| 55 | |
| 56 # Can be changed using --acpi_root command line option. | |
| 57 ACPI_ROOT = '/sys/bus/platform/devices/chromeos_acpi' | |
| 58 GPIO_SIGNAL_TYPE_EXTENSION = '0' | |
| 59 GPIO_PIN_NUMBER_EXTENSION = '2' | |
| 60 | |
| 61 # can be changed using --symlink_root command line option. | |
| 62 DEFAULT_SYMLINK_ROOT = '/home/gpio' | |
| 63 | |
| 64 # This dictionary maps GPIO signal types codes into their actual names. | |
| 65 GPIO_SIGNAL_TYPES = { | |
| 66 1: 'recovery_button', | |
| 67 2: 'developer_switch', | |
| 68 3: 'write_protect' | |
| 69 } | |
| 70 | |
| 71 # Debug header signal type codes are offset by 0x100, the tuple below | |
| 72 # represents the range of valid codes for the debug header. The range starts | |
| 73 # at 0x100 and is 0x100 wide. | |
| 74 GPIO_DEBUG_HEADER_RANGE = (0x100, 0x100) | |
| 75 | |
| 76 # This is used to prepare the option parser: each element is a tuple of | |
| 77 # strings, including the option name and the option default value. | |
| 78 option_list = (('symlink_root', DEFAULT_SYMLINK_ROOT), | |
| 79 ('pci_address', GPIO_DEVICE_PCI_ADDRESS), | |
| 80 ('acpi_root', ACPI_ROOT)) | |
| 81 | |
| 82 # This becomes the option object after the command line is parsed. | |
| 83 cmd_line_options = None | |
| 84 | |
| 85 class GpioSetupError(Exception): | |
| 86 pass | |
| 87 | |
| 88 | |
| 89 class GpioChip(object): | |
| 90 """Represent GPIO chip available through sys fs. | |
| 91 | |
| 92 Attributes: | |
| 93 pci_address: a string, PCI address of this GPIO device | |
| 94 base: a number, base global GPIO number of this device (mapped to pin zero | |
| 95 in the device range) | |
| 96 capacity: a number, shows the number of GPIO pins of this device. | |
| 97 description: a multiline string description of this device, initialized | |
| 98 after the device is attached. Can be used to dump device | |
| 99 information. | |
| 100 """ | |
| 101 | |
| 102 def __init__(self, pci_address): | |
| 103 self.pci_address = pci_address | |
| 104 self.base = 0 | |
| 105 self.capacity = 0 | |
| 106 self.description = 'not attached' | |
| 107 | |
| 108 def Attach(self): | |
| 109 for f in glob.glob(GPIO_DEVICE_ROOT + '*/label'): | |
| 110 label = open(f).read().strip() | |
| 111 if label == self.pci_address: | |
| 112 break | |
| 113 else: | |
| 114 raise GpioSetupError( | |
| 115 'could not find GPIO PCI device %s' % self.pci_address) | |
| 116 directory = os.path.dirname(f) | |
| 117 self.base = int(open(directory + '/base').read()) | |
| 118 self.capacity = int(open(directory + '/ngpio').read()) | |
| 119 self.description = '\n'.join(['GPIO device at PCI address %s' % | |
| 120 self.pci_address, | |
| 121 'Base gpio pin %d' % self.base, | |
| 122 'Capacity %d' % self.capacity]) | |
| 123 | |
| 124 def EnablePin(self, pin): | |
| 125 """Enable a certain GPIO pin. | |
| 126 | |
| 127 To enable the pin one needs to write its global GPIO number into | |
| 128 /sys/class/gpio/export, if this pin has not been enabled yet. | |
| 129 | |
| 130 Inputs: | |
| 131 pin: a number, zero based pin number within this device's range. | |
| 132 """ | |
| 133 | |
| 134 if pin >= self.capacity: | |
| 135 raise GpioSetupError('pin %d exceeds capacity of %d' % ( | |
| 136 pin, self.capacity)) | |
| 137 global_gpio_number = self.base + pin | |
| 138 if not os.path.exists('%s/gpio%d' % (GPIO_ROOT, global_gpio_number)): | |
| 139 open(GPIO_ENABLE_FILE, 'w').write('%d' % (global_gpio_number)) | |
| 140 | |
| 141 def __str__(self): | |
| 142 return self.description | |
| 143 | |
| 144 | |
| 145 def ParseAcpiMappings(): | |
| 146 """Scan ACPI information about GPIO and generate a mapping. | |
| 147 | |
| 148 Returns: a list of tuples, each tuple consisting of a string representing | |
| 149 the GPIO pin name and a number, representing the GPIO pin within | |
| 150 the GPIO device space. | |
| 151 """ | |
| 152 acpi_gpio_mapping = [] | |
| 153 for d in glob.glob('%s/GPIO.[0-9]*' % cmd_line_options.acpi_root): | |
| 154 signal_type = int(open('%s/GPIO.%s' % ( | |
| 155 d, GPIO_SIGNAL_TYPE_EXTENSION)).read()) | |
| 156 | |
| 157 pin_number = int(open('%s/GPIO.%s' % ( | |
| 158 d, GPIO_PIN_NUMBER_EXTENSION)).read()) | |
| 159 | |
| 160 if signal_type in GPIO_SIGNAL_TYPES: | |
| 161 acpi_gpio_mapping.append((GPIO_SIGNAL_TYPES[signal_type], pin_number)) | |
| 162 continue | |
| 163 | |
| 164 # This is not a specific signal, could be a debug header pin. | |
| 165 debug_header = signal_type - GPIO_DEBUG_HEADER_RANGE[0] | |
| 166 if debug_header >= 0 and debug_header < GPIO_DEBUG_HEADER_RANGE[1]: | |
| 167 acpi_gpio_mapping.append(('debug_header_%d' % debug_header, pin_number)) | |
| 168 continue | |
| 169 | |
| 170 # Unrecognized mapping, could happen if BIOS version is ahead of this | |
| 171 # script. | |
| 172 print 'unknown signal type encoding %d in %d' % (signal_type, d) | |
| 173 | |
| 174 if not acpi_gpio_mapping: | |
| 175 raise GpioSetupError('no gpio mapping found. Is ACPI driver installed?') | |
| 176 | |
| 177 return acpi_gpio_mapping | |
| 178 | |
| 179 | |
| 180 def CreateGpioSymlinks(mappings, gpio, symlink_root): | |
| 181 if not os.path.exists(symlink_root): | |
| 182 raise GpioSetupError('%s does not exist' % symlink_root) | |
| 183 | |
| 184 if not os.path.isdir(symlink_root): | |
| 185 raise GpioSetupError('%s is not a directory' % symlink_root) | |
| 186 | |
| 187 if not os.access(symlink_root, os.W_OK): | |
| 188 raise GpioSetupError('%s is not writable' % symlink_root) | |
| 189 | |
| 190 try: | |
| 191 os.chdir(symlink_root) | |
| 192 except OSError: | |
| 193 raise GpioSetupError('failed to change directory to %s' % symlink_root) | |
| 194 | |
| 195 for (symlink, pin) in mappings: | |
| 196 gpio.EnablePin(pin) | |
| 197 source_file = '%s/gpio%d/value' % (GPIO_ROOT, pin + gpio.base) | |
| 198 if not os.path.exists(symlink): | |
| 199 os.symlink(source_file, symlink) | |
| 200 continue | |
| 201 if not os.path.islink(symlink): | |
| 202 raise GpioSetupError( | |
| 203 '%s exists but is not a symlink' % os.path.abspath(symlink)) | |
| 204 if os.readlink(symlink) != source_file: | |
| 205 raise GpioSetupError( | |
| 206 '%s points to a wrong file' % os.path.abspath(symlink)) | |
| 207 | |
| 208 | |
| 209 def ProcessOptions(): | |
| 210 global cmd_line_options | |
| 211 parser = optparse.OptionParser() | |
| 212 for (name, default_value) in option_list: | |
| 213 parser.add_option('--' + name, dest=name, default=default_value) | |
| 214 (cmd_line_options, _) = parser.parse_args() | |
| 215 | |
| 216 | |
| 217 def main(): | |
| 218 ProcessOptions() | |
| 219 gpioc = GpioChip(cmd_line_options.pci_address) | |
| 220 gpioc.Attach() | |
| 221 CreateGpioSymlinks(ParseAcpiMappings(), gpioc, cmd_line_options.symlink_root) | |
| 222 | |
| 223 | |
| 224 if __name__ == '__main__': | |
| 225 try: | |
| 226 main() | |
| 227 except GpioSetupError, e: | |
| 228 print >> sys.stderr, '%s: %s' % (sys.argv[0].split('/')[-1], e) | |
| 229 sys.exit(1) | |
| 230 sys.exit(0) | |
| OLD | NEW |