mmio.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. '''
  2. Stripped down version from https://github.com/vsergeev/python-periphery/blob/master/periphery/mmio.py
  3. '''
  4. import mmap
  5. import os
  6. import struct
  7. import sys
  8. # Alias long to int on Python 3
  9. if sys.version_info[0] >= 3:
  10. long = int
  11. class MMIOError(IOError):
  12. """Base class for MMIO errors."""
  13. pass
  14. class MMIO(object):
  15. def __init__(self, physaddr, size):
  16. """Instantiate an MMIO object and map the region of physical memory
  17. specified by the address base `physaddr` and size `size` in bytes.
  18. Args:
  19. physaddr (int, long): base physical address of memory region.
  20. size (int, long): size of memory region.
  21. Returns:
  22. MMIO: MMIO object.
  23. Raises:
  24. MMIOError: if an I/O or OS error occurs.
  25. TypeError: if `physaddr` or `size` types are invalid.
  26. """
  27. self.mapping = None
  28. self._open(physaddr, size)
  29. def __del__(self):
  30. self.close()
  31. def __enter__(self):
  32. pass
  33. def __exit__(self, t, value, traceback):
  34. self.close()
  35. def _open(self, physaddr, size):
  36. if not isinstance(physaddr, (int, long)):
  37. raise TypeError("Invalid physaddr type, should be integer.")
  38. if not isinstance(size, (int, long)):
  39. raise TypeError("Invalid size type, should be integer.")
  40. pagesize = os.sysconf(os.sysconf_names['SC_PAGESIZE'])
  41. self._physaddr = physaddr
  42. self._size = size
  43. self._aligned_physaddr = physaddr - (physaddr % pagesize)
  44. self._aligned_size = size + (physaddr - self._aligned_physaddr)
  45. try:
  46. fd = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
  47. except OSError as e:
  48. raise MMIOError(e.errno, "Opening /dev/mem: " + e.strerror)
  49. try:
  50. self.mapping = mmap.mmap(
  51. fd, self._aligned_size, flags=mmap.MAP_SHARED, prot=mmap.PROT_WRITE, offset=self._aligned_physaddr)
  52. except OSError as e:
  53. raise MMIOError(e.errno, "Mapping /dev/mem: " + e.strerror)
  54. try:
  55. os.close(fd)
  56. except OSError as e:
  57. raise MMIOError(e.errno, "Closing /dev/mem: " + e.strerror)
  58. # Methods
  59. def _adjust_offset(self, offset):
  60. return offset + (self._physaddr - self._aligned_physaddr)
  61. def _validate_offset(self, offset, length):
  62. if (offset + length) > self._aligned_size:
  63. raise ValueError("Offset out of bounds.")
  64. def read32(self, offset):
  65. """Read 32-bits from the specified `offset` in bytes, relative to the
  66. base physical address of the MMIO region.
  67. Args:
  68. offset (int, long): offset from base physical address, in bytes.
  69. Returns:
  70. int: 32-bit value read.
  71. Raises:
  72. TypeError: if `offset` type is invalid.
  73. ValueError: if `offset` is out of bounds.
  74. """
  75. if not isinstance(offset, (int, long)):
  76. raise TypeError("Invalid offset type, should be integer.")
  77. offset = self._adjust_offset(offset)
  78. self._validate_offset(offset, 4)
  79. return struct.unpack("=L", self.mapping[offset:offset + 4])[0]
  80. def write32(self, offset, value):
  81. """Write 32-bits to the specified `offset` in bytes, relative to the
  82. base physical address of the MMIO region.
  83. Args:
  84. offset (int, long): offset from base physical address, in bytes.
  85. value (int, long): 32-bit value to write.
  86. Raises:
  87. TypeError: if `offset` or `value` type are invalid.
  88. ValueError: if `offset` or `value` are out of bounds.
  89. """
  90. if not isinstance(offset, (int, long)):
  91. raise TypeError("Invalid offset type, should be integer.")
  92. if not isinstance(value, (int, long)):
  93. raise TypeError("Invalid value type, should be integer.")
  94. if value < 0 or value > 0xffffffff:
  95. raise ValueError("Value out of bounds.")
  96. offset = self._adjust_offset(offset)
  97. self._validate_offset(offset, 4)
  98. self.mapping[offset:offset + 4] = struct.pack("=L", value)
  99. def close(self):
  100. """Unmap the MMIO object's mapped physical memory."""
  101. if self.mapping is None:
  102. return
  103. self.mapping.close()
  104. self.mapping = None
  105. self._fd = None
  106. # String representation
  107. def __str__(self):
  108. return "MMIO 0x%08x (size=%d)" % (self.base, self.size)