# # Copyright 2017 Ettus Research (National Instruments) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # """ Access to GPIOs mapped into the PS via sysfs """ import os import pyudev from .mpmlog import get_logger GPIO_SYSFS_BASE_DIR = '/sys/class/gpio' GPIO_SYSFS_LABELFILE = 'label' GPIO_SYSFS_VALUEFILE = 'value' def get_all_gpio_devs(): """ Returns a list of all GPIO chips available through sysfs. Will look something like ['gpiochip882', 'gpiochip123', ...] """ try: context = pyudev.Context() gpios = [device.sys_name.encode('ascii') for device in context.list_devices(subsystem="gpio") if os.path.exists(os.path.join( # udev probably has better ways to do this GPIO_SYSFS_BASE_DIR, device.sys_name.encode('ascii'), GPIO_SYSFS_LABELFILE )) ] return gpios except OSError: # Typically means GPIO not available, maybe no overlay return [] def get_gpio_map_info(gpio_dev): """ Returns all the map info for a given GPIO device. Example: If pio_dev is 'gpio882', it will list all files in /sys/class/gpio/gpio882/ and create a dictionary with filenames as keys and content as value. Subdirs are skipped. Numbers are casted to numbers automatically. Strings remain strings. """ map_info = {} map_info_path = os.path.join( GPIO_SYSFS_BASE_DIR, gpio_dev, ) for info_file in os.listdir(map_info_path): if not os.path.isfile(os.path.join(map_info_path, info_file)): continue map_info_value = open(os.path.join(map_info_path, info_file), 'r').read().strip() try: map_info[info_file] = int(map_info_value, 0) except ValueError: map_info[info_file] = map_info_value # Manually add GPIO number context = pyudev.Context() map_info['sys_number'] = int( pyudev.Devices.from_name(context, subsystem="gpio", sys_name=gpio_dev).sys_number ) return map_info def find_gpio_device(label, logger=None): """ Given a label, returns a tuple (uio_device, map_info). uio_device is something like 'gpio882'. map_info is a dictionary with information regarding the GPIO device read from the map info sysfs dir. """ gpio_devices = get_all_gpio_devs() if logger: logger.trace("Found the following UIO devices: `{0}'".format(','.join(gpio_devices))) for gpio_device in gpio_devices: map_info = get_gpio_map_info(gpio_device) if logger: logger.trace("{0} has map info: {1}".format(gpio_device, map_info)) if map_info.get('label') == label: if logger: logger.trace("Device matches label: `{0}'".format(gpio_device)) return gpio_device, map_info if logger: logger.warning("Found no matching gpio device for label `{0}'".format(label)) return None, None class SysFSGPIO(object): """ API for accessing GPIOs mapped into userland via sysfs """ def __init__(self, label, use_mask, ddr): assert (use_mask & ddr) == ddr self.log = get_logger("SysFSGPIO") self._label = label self._use_mask = use_mask self._ddr = ddr self.log.trace("Generating SysFSGPIO object for label `{}'...".format(label)) self._gpio_dev, self._map_info = find_gpio_device(label, self.log) if self._gpio_dev is None: self.log.error("Could not find GPIO device with label `{}'.".format(label)) self.log.trace("GPIO base number is {}".format(self._map_info.get("sys_number"))) self._base_gpio = self._map_info.get("sys_number") self.init(self._map_info['ngpio'], self._base_gpio, self._use_mask, self._ddr) def init(self, n_gpio, base, use_mask, ddr): """ Guarantees that all the devices are created accordingly E.g., if use_mask & 0x1 is True, it makes sure that 'gpioXXX' is exported. Also sets the DDRs. """ gpio_list = [x for x in xrange(n_gpio) if (1<