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 |