mmio.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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,
  52. self._aligned_size,
  53. flags=mmap.MAP_SHARED,
  54. prot=mmap.PROT_WRITE,
  55. offset=self._aligned_physaddr)
  56. except OSError as e:
  57. raise MMIOError(e.errno, "Mapping /dev/mem: " + e.strerror)
  58. try:
  59. os.close(fd)
  60. except OSError as e:
  61. raise MMIOError(e.errno, "Closing /dev/mem: " + e.strerror)
  62. # Methods
  63. def _adjust_offset(self, offset):
  64. return offset + (self._physaddr - self._aligned_physaddr)
  65. def _validate_offset(self, offset, length):
  66. if (offset + length) > self._aligned_size:
  67. raise ValueError("Offset out of bounds.")
  68. def write32(self, offset, value):
  69. """Write 32-bits to the specified `offset` in bytes, relative to the
  70. base physical address of the MMIO region.
  71. Args:
  72. offset (int, long): offset from base physical address, in bytes.
  73. value (int, long): 32-bit value to write.
  74. Raises:
  75. TypeError: if `offset` or `value` type are invalid.
  76. ValueError: if `offset` or `value` are out of bounds.
  77. """
  78. if not isinstance(offset, (int, long)):
  79. raise TypeError("Invalid offset type, should be integer.")
  80. if not isinstance(value, (int, long)):
  81. raise TypeError("Invalid value type, should be integer.")
  82. if value < 0 or value > 0xffffffff:
  83. raise ValueError("Value out of bounds.")
  84. offset = self._adjust_offset(offset)
  85. self._validate_offset(offset, 4)
  86. self.mapping[offset:offset + 4] = struct.pack("=L", value)
  87. def close(self):
  88. """Unmap the MMIO object's mapped physical memory."""
  89. if self.mapping is None:
  90. return
  91. self.mapping.close()
  92. self.mapping = None
  93. self._fd = None
  94. # String representation
  95. def __str__(self):
  96. return "MMIO 0x%08x (size=%d)" % (self.base, self.size)