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 |