import logging
from ctypes import addressof, byref, c_char_p, c_int8, sizeof
from mmap import mmap, PROT_READ, MAP_SHARED
from typing import Any, Dict, NamedTuple
from .core import PSP, get_psp_exc_msg
from .exc import (
PSPError,
PSPNotOpened,
PSPNotSupport,
)
from .lmbinc import (
DLLVersion,
ERR_Success,
ERR_NotOpened,
ERR_NotSupport,
)
logger = logging.getLogger(__name__)
[docs]
class DLLVersionModel(NamedTuple):
"""To store DLL and Board Library version information."""
dll_major: int
dll_minor: int
dll_build: int
platform_id: str
board_major: int
board_minor: int
board_build: int
def __str__(self) -> str:
return f"PSP/SDK version: {self.dll_major}.{self.dll_minor}.{self.dll_build}\n" \
f"IODRV version: {self.platform_id}.{self.board_major}.{self.board_minor}.{self.board_build}"
[docs]
def to_dict(self) -> Dict[str, Any]:
"""Convert to dict."""
return dict(self._asdict())
[docs]
class DLL:
"""
Dynamic Link Library.
"""
def __init__(self) -> None:
pass
[docs]
def get_version(self) -> DLLVersionModel:
"""
Load the Lanner board-level library.
Example:
.. code-block:: pycon
>>> dll = DLL()
>>> version = dll.get_version()
>>> version
DLLVersionModel(dll_major=2, dll_minor=1, dll_build=2, platform_id='LEB-7242', board_major=1, board_minor=0, board_build=2)
>>> version.to_dict()
{'dll_major': 2, 'dll_minor': 1, 'dll_build': 2, 'platform_id': 'LEB-7242', 'board_major': 1, 'board_minor': 0, 'board_build': 2}
>>> str(version)
'PSP/SDK version: 2.1.2\\nIODRV version: LEB-7242.1.0.2'
>>> version.dll_major
2
>>> version.dll_minor
1
>>> version.dll_build
2
>>> version.platform_id
'LEB-7242'
>>> version.board_major
1
>>> version.board_minor
0
>>> version.board_build
2
:return: the DLL and Board Library version information
:rtype: DLLVersionModel
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This platform does not support this function.
:raises PSPError: General PSP functional error.
"""
stu_dll_ver = DLLVersion()
with PSP() as psp:
i_ret = psp.lib.LMB_DLL_Version(byref(stu_dll_ver))
msg = get_psp_exc_msg("LMB_DLL_Version", i_ret)
if i_ret == ERR_Success:
return DLLVersionModel(
dll_major=stu_dll_ver.uw_dll_major,
dll_minor=stu_dll_ver.uw_dll_minor,
dll_build=stu_dll_ver.uw_dll_build,
# https://stackoverflow.com/a/29293102/9611854
platform_id=c_char_p(addressof(stu_dll_ver.str_platform_id)).value.decode(),
board_major=stu_dll_ver.uw_board_major,
board_minor=stu_dll_ver.uw_board_minor,
board_build=stu_dll_ver.uw_board_build,
)
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
[docs]
def get_bios_id(self) -> str:
"""
Get the Lanner mother-board BIOS infromation.
Example:
.. code-block:: pycon
>>> dll = DLL()
>>> dll.get_bios_id()
'LEB-7242B BIOS V1.12 "03/09/2022"'
:return: the mother board BIOS information
:rtype: str
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This platform does not support this function.
:raises PSPError: General PSP functional error.
"""
try:
str_bios_id = (c_int8 * 50)(*range(50)) # str_bios_id = create_string_buffer(50)
with PSP() as psp:
i_ret = psp.lib.LMB_DLL_BIOSID(str_bios_id, sizeof(str_bios_id))
msg = get_psp_exc_msg("LMB_DLL_BIOSID", i_ret)
if i_ret == ERR_Success:
return c_char_p(addressof(str_bios_id)).value.decode().strip()
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == PSPNotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
except AttributeError:
# `sudo usermod -g kmem yourID`
# `sudo busybox devmem 0x00ff58b 8 | xxd -r -p`
key = "*LIID "
with open("/dev/mem", "rb") as f:
mem = mmap(f.fileno(), 0x10000, MAP_SHARED, PROT_READ, offset=0x000f0000)
if mem is None:
return ""
if not mem.read(33 + len(key)).startswith(key.encode()):
# not found "*LIID"
# add here for BIOS uses traditional position F000:F58B
mem.seek(0xF58B)
return mem.read(33 + len(key)).decode().replace(key, "").strip().rsplit('"', 1)[0] + '"'