Source code for periphery.mmio

import sys
import os
import mmap
import ctypes
import struct


# Alias long to int on Python 3
if sys.version_info[0] >= 3:
    long = int


[docs]class MMIOError(IOError): """Base class for MMIO errors.""" pass
[docs]class MMIO(object): def __init__(self, physaddr, size): """Instantiate an MMIO object and map the region of physical memory specified by the address base `physaddr` and size `size` in bytes. Args: physaddr (int, long): base physical address of memory region. size (int, long): size of memory region. Returns: MMIO: MMIO object. Raises: MMIOError: if an I/O or OS error occurs. TypeError: if `physaddr` or `size` types are invalid. """ self.mapping = None self._open(physaddr, size) def __del__(self): self.close() def __enter__(self): pass def __exit__(self, t, value, traceback): self.close() def _open(self, physaddr, size): if not isinstance(physaddr, (int, long)): raise TypeError("Invalid physaddr type, should be integer.") if not isinstance(size, (int, long)): raise TypeError("Invalid size type, should be integer.") pagesize = os.sysconf(os.sysconf_names['SC_PAGESIZE']) self._physaddr = physaddr self._size = size self._aligned_physaddr = physaddr - (physaddr % pagesize) self._aligned_size = size + (physaddr - self._aligned_physaddr) try: fd = os.open("/dev/mem", os.O_RDWR | os.O_SYNC) except OSError as e: raise MMIOError(e.errno, "Opening /dev/mem: " + e.strerror) try: self.mapping = mmap.mmap(fd, self._aligned_size, flags=mmap.MAP_SHARED, prot=(mmap.PROT_READ | mmap.PROT_WRITE), offset=self._aligned_physaddr) except OSError as e: raise MMIOError(e.errno, "Mapping /dev/mem: " + e.strerror) try: os.close(fd) except OSError as e: raise MMIOError(e.errno, "Closing /dev/mem: " + e.strerror) # Methods def _adjust_offset(self, offset): return offset + (self._physaddr - self._aligned_physaddr) def _validate_offset(self, offset, length): if (offset + length) > self._aligned_size: raise ValueError("Offset out of bounds.")
[docs] def read32(self, offset): """Read 32-bits from the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. Returns: int: 32-bit value read. Raises: TypeError: if `offset` type is invalid. ValueError: if `offset` is out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") offset = self._adjust_offset(offset) self._validate_offset(offset, 4) return struct.unpack("=L", self.mapping[offset:offset + 4])[0]
[docs] def read16(self, offset): """Read 16-bits from the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. Returns: int: 16-bit value read. Raises: TypeError: if `offset` type is invalid. ValueError: if `offset` is out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") offset = self._adjust_offset(offset) self._validate_offset(offset, 2) return struct.unpack("=H", self.mapping[offset:offset + 2])[0]
[docs] def read8(self, offset): """Read 8-bits from the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. Returns: int: 8-bit value read. Raises: TypeError: if `offset` type is invalid. ValueError: if `offset` is out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") offset = self._adjust_offset(offset) self._validate_offset(offset, 1) return struct.unpack("B", self.mapping[offset:offset + 1])[0]
[docs] def read(self, offset, length): """Read a string of bytes from the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. length (int): number of bytes to read. Returns: bytes: bytes read. Raises: TypeError: if `offset` type is invalid. ValueError: if `offset` is out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") offset = self._adjust_offset(offset) self._validate_offset(offset, length) return bytes(self.mapping[offset:offset + length])
[docs] def write32(self, offset, value): """Write 32-bits to the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. value (int, long): 32-bit value to write. Raises: TypeError: if `offset` or `value` type are invalid. ValueError: if `offset` or `value` are out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") if not isinstance(value, (int, long)): raise TypeError("Invalid value type, should be integer.") if value < 0 or value > 0xffffffff: raise ValueError("Value out of bounds.") offset = self._adjust_offset(offset) self._validate_offset(offset, 4) self.mapping[offset:offset + 4] = struct.pack("=L", value)
[docs] def write16(self, offset, value): """Write 16-bits to the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. value (int, long): 16-bit value to write. Raises: TypeError: if `offset` or `value` type are invalid. ValueError: if `offset` or `value` are out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") if not isinstance(value, (int, long)): raise TypeError("Invalid value type, should be integer.") if value < 0 or value > 0xffff: raise ValueError("Value out of bounds.") offset = self._adjust_offset(offset) self._validate_offset(offset, 2) self.mapping[offset:offset + 2] = struct.pack("=H", value)
[docs] def write8(self, offset, value): """Write 8-bits to the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. value (int, long): 8-bit value to write. Raises: TypeError: if `offset` or `value` type are invalid. ValueError: if `offset` or `value` are out of bounds. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") if not isinstance(value, (int, long)): raise TypeError("Invalid value type, should be integer.") if value < 0 or value > 0xff: raise ValueError("Value out of bounds.") offset = self._adjust_offset(offset) self._validate_offset(offset, 1) self.mapping[offset:offset + 1] = struct.pack("B", value)
[docs] def write(self, offset, data): """Write a string of bytes to the specified `offset` in bytes, relative to the base physical address of the MMIO region. Args: offset (int, long): offset from base physical address, in bytes. data (bytes, bytearray, list): a byte array or list of 8-bit integers to write. Raises: TypeError: if `offset` or `data` type are invalid. ValueError: if `offset` is out of bounds, or if data is not valid bytes. """ if not isinstance(offset, (int, long)): raise TypeError("Invalid offset type, should be integer.") if not isinstance(data, (bytes, bytearray, list)): raise TypeError("Invalid data type, expected bytes, bytearray, or list.") offset = self._adjust_offset(offset) self._validate_offset(offset, len(data)) data = bytes(bytearray(data)) self.mapping[offset:offset + len(data)] = data
[docs] def close(self): """Unmap the MMIO object's mapped physical memory.""" if self.mapping is None: return self.mapping.close() self.mapping = None self._fd = None
# Immutable properties @property def base(self): """Get the base physical address of the MMIO region. :type: int """ return self._physaddr @property def size(self): """Get the mapping size of the MMIO region. :type: int """ return self._size @property def pointer(self): """Get a ctypes void pointer to the memory mapped region. :type: ctypes.c_void_p """ return ctypes.cast(ctypes.pointer(ctypes.c_uint8.from_buffer(self.mapping, 0)), ctypes.c_void_p) # String representation def __str__(self): return "MMIO 0x%08x (size=%d)" % (self.base, self.size)