You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
16 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. # The MIT License (MIT)
  2. #
  3. # Copyright (c) 2020 Melissa LeBlanc-Williams for Adafruit Industries
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. # THE SOFTWARE.
  22. """
  23. `adafruit_platformdetect.board`
  24. ================================================================================
  25. Detect boards
  26. * Author(s): Melissa LeBlanc-Williams
  27. Implementation Notes
  28. --------------------
  29. **Software and Dependencies:**
  30. * Linux and Python 3.5 or Higher
  31. """
  32. # imports
  33. import os
  34. import re
  35. from adafruit_platformdetect.constants import boards, chips
  36. __version__ = "0.0.0-auto.0"
  37. __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PlatformDetect.git"
  38. class Board:
  39. """Attempt to detect specific boards."""
  40. def __init__(self, detector):
  41. self.detector = detector
  42. # pylint: disable=invalid-name, protected-access
  43. @property
  44. def id(self):
  45. """Return a unique id for the detected board, if any."""
  46. # There are some times we want to trick the platform detection
  47. # say if a raspberry pi doesn't have the right ID, or for testing
  48. try:
  49. return os.environ["BLINKA_FORCEBOARD"]
  50. except KeyError: # no forced board, continue with testing!
  51. pass
  52. chip_id = self.detector.chip.id
  53. board_id = None
  54. if chip_id == chips.BCM2XXX:
  55. board_id = self._pi_id()
  56. elif chip_id == chips.AM33XX:
  57. board_id = self._beaglebone_id()
  58. elif chip_id == chips.GENERIC_X86:
  59. board_id = boards.GENERIC_LINUX_PC
  60. elif chip_id == chips.SUN8I:
  61. board_id = self._armbian_id()
  62. elif chip_id == chips.SAMA5:
  63. board_id = self._sama5_id()
  64. elif chip_id == chips.IMX8MX:
  65. board_id = self._imx8mx_id()
  66. elif chip_id == chips.ESP8266:
  67. board_id = boards.FEATHER_HUZZAH
  68. elif chip_id == chips.SAMD21:
  69. board_id = boards.FEATHER_M0_EXPRESS
  70. elif chip_id == chips.STM32:
  71. board_id = boards.PYBOARD
  72. elif chip_id == chips.S805:
  73. board_id = boards.ODROID_C1
  74. elif chip_id == chips.S905:
  75. board_id = boards.ODROID_C2
  76. elif chip_id == chips.S905X3:
  77. board_id = boards.ODROID_C4
  78. elif chip_id == chips.S922X:
  79. board_id = boards.ODROID_N2
  80. elif chip_id == chips.EXYNOS5422:
  81. board_id = boards.ODROID_XU4
  82. elif chip_id == chips.FT232H:
  83. board_id = boards.FTDI_FT232H
  84. elif chip_id == chips.APQ8016:
  85. board_id = boards.DRAGONBOARD_410C
  86. elif chip_id in (chips.T210, chips.T186, chips.T194):
  87. board_id = self._tegra_id()
  88. elif chip_id == chips.HFU540:
  89. board_id = self._sifive_id()
  90. elif chip_id == chips.MCP2221:
  91. board_id = boards.MICROCHIP_MCP2221
  92. elif chip_id == chips.BINHO:
  93. board_id = boards.BINHO_NOVA
  94. elif chip_id == chips.LPC4330:
  95. board_id = boards.GREATFET_ONE
  96. elif chip_id == chips.MIPS24KC:
  97. board_id = boards.ONION_OMEGA
  98. elif chip_id == chips.MIPS24KEC:
  99. board_id = boards.ONION_OMEGA2
  100. elif chip_id == chips.ZYNQ7000:
  101. board_id = self._pynq_id()
  102. elif chip_id == chips.A64:
  103. board_id = self._pine64_id()
  104. elif chip_id == chips.A33:
  105. board_id = self._clockwork_pi_id()
  106. elif chip_id == chips.RK3308:
  107. board_id = self._rock_pi_id()
  108. elif chip_id == chips.RYZEN_V1605B:
  109. board_id = self._udoo_id()
  110. return board_id
  111. # pylint: enable=invalid-name
  112. def _pi_id(self):
  113. """Try to detect id of a Raspberry Pi."""
  114. # Check for Pi boards:
  115. pi_rev_code = self._pi_rev_code()
  116. if pi_rev_code:
  117. for model, codes in boards._PI_REV_CODES.items():
  118. if pi_rev_code in codes:
  119. return model
  120. # We may be on a non-Raspbian OS, so try to lazily determine
  121. # the version based on `get_device_model`
  122. else:
  123. pi_model = self.detector.get_device_model()
  124. if pi_model:
  125. pi_model = pi_model.upper().replace(" ", "_")
  126. if "PLUS" in pi_model:
  127. re_model = re.search(r"(RASPBERRY_PI_\d).*([AB]_*)(PLUS)", pi_model)
  128. elif "CM" in pi_model: # untested for Compute Module
  129. re_model = re.search(r"(RASPBERRY_PI_CM)(\d)", pi_model)
  130. else: # untested for non-plus models
  131. re_model = re.search(r"(RASPBERRY_PI_\d).*([AB])", pi_model)
  132. if re_model:
  133. pi_model = "".join(re_model.groups())
  134. available_models = boards._PI_REV_CODES.keys()
  135. for model in available_models:
  136. if model == pi_model:
  137. return model
  138. return None
  139. def _pi_rev_code(self):
  140. """Attempt to find a Raspberry Pi revision code for this board."""
  141. # 2708 is Pi 1
  142. # 2709 is Pi 2
  143. # 2835 is Pi 3 (or greater) on 4.9.x kernel
  144. # Anything else is not a Pi.
  145. if self.detector.chip.id != chips.BCM2XXX:
  146. # Something else, not a Pi.
  147. return None
  148. rev = self.detector.get_cpuinfo_field("Revision")
  149. if rev is not None:
  150. return rev
  151. try:
  152. with open("/proc/device-tree/system/linux,revision", "rb") as revision:
  153. rev_bytes = revision.read()
  154. if rev_bytes[:1] == b"\x00":
  155. rev_bytes = rev_bytes[1:]
  156. return rev_bytes.hex()
  157. except FileNotFoundError:
  158. return None
  159. # pylint: disable=no-self-use
  160. def _beaglebone_id(self):
  161. """Try to detect id of a Beaglebone."""
  162. try:
  163. with open("/sys/bus/nvmem/devices/0-00500/nvmem", "rb") as eeprom:
  164. eeprom_bytes = eeprom.read(16)
  165. except FileNotFoundError:
  166. return None
  167. if eeprom_bytes[:4] != b"\xaaU3\xee":
  168. return None
  169. # special condition for BeagleBone Green rev. 1A
  170. # refer to GitHub issue #57 in this repo for more info
  171. if eeprom_bytes == b"\xaaU3\xeeA335BNLT\x1a\x00\x00\x00":
  172. return boards.BEAGLEBONE_GREEN
  173. id_string = eeprom_bytes[4:].decode("ascii")
  174. for model, bb_ids in boards._BEAGLEBONE_BOARD_IDS.items():
  175. for bb_id in bb_ids:
  176. if id_string == bb_id[1]:
  177. return model
  178. return None
  179. # pylint: enable=no-self-use
  180. # pylint: disable=too-many-return-statements
  181. def _armbian_id(self):
  182. """Check whether the current board is an OrangePi board."""
  183. board_value = self.detector.get_armbian_release_field("BOARD")
  184. board = None
  185. if board_value == "orangepipc":
  186. board = boards.ORANGE_PI_PC
  187. if board_value == "orangepi-r1":
  188. board = boards.ORANGE_PI_R1
  189. if board_value == "orangepizero":
  190. board = boards.ORANGE_PI_ZERO
  191. if board_value == "orangepione":
  192. board = boards.ORANGE_PI_ONE
  193. if board_value == "orangepilite":
  194. board = boards.ORANGE_PI_LITE
  195. if board_value == "orangepiplus2e":
  196. board = boards.ORANGE_PI_PLUS_2E
  197. if board_value == "orangepipcplus":
  198. board = boards.ORANGE_PI_PC_PLUS
  199. if board_value == "pinebook-a64":
  200. board = boards.PINEBOOK
  201. if board_value == "orangepi2":
  202. board = boards.ORANGE_PI_2
  203. return board
  204. # pylint: enable=too-many-return-statements
  205. # pylint: enable=too-many-return-statements
  206. def _sama5_id(self):
  207. """Check what type sama5 board."""
  208. board_value = self.detector.get_device_model()
  209. if "Giant Board" in board_value:
  210. return boards.GIANT_BOARD
  211. return None
  212. def _imx8mx_id(self):
  213. """Check what type iMX8M board."""
  214. board_value = self.detector.get_device_model()
  215. if "Phanbell" in board_value:
  216. return boards.CORAL_EDGE_TPU_DEV
  217. return None
  218. def _tegra_id(self):
  219. """Try to detect the id of aarch64 board."""
  220. compatible = self.detector.get_device_compatible()
  221. if not compatible:
  222. return None
  223. compats = compatible.split("\x00")
  224. for board_id, board_compats in boards._JETSON_IDS.items():
  225. if any(v in compats for v in board_compats):
  226. return board_id
  227. return None
  228. def _sifive_id(self):
  229. """Try to detect the id for Sifive RISCV64 board."""
  230. board_value = self.detector.get_device_model()
  231. if "hifive-unleashed-a00" in board_value:
  232. return boards.SIFIVE_UNLEASHED
  233. return None
  234. def _pine64_id(self):
  235. """Try to detect the id for Pine64 board or device."""
  236. board_value = self.detector.get_device_model()
  237. board = None
  238. if "pine64" in board_value.lower():
  239. board = boards.PINE64
  240. elif "pinebook" in board_value.lower():
  241. board = boards.PINEBOOK
  242. elif "pinephone" in board_value.lower():
  243. board = boards.PINEPHONE
  244. return board
  245. # pylint: disable=no-self-use
  246. def _pynq_id(self):
  247. """Try to detect the id for Xilinx PYNQ boards."""
  248. try:
  249. with open("/proc/device-tree/chosen/pynq_board", "r") as board_file:
  250. board_model = board_file.read()
  251. match = board_model.upper().replace("-", "_").rstrip("\x00")
  252. for model in boards._PYNQ_IDS:
  253. if model == match:
  254. return model
  255. return None
  256. except FileNotFoundError:
  257. return None
  258. def _rock_pi_id(self):
  259. """Check what type of Rock Pi board."""
  260. board_value = self.detector.get_device_model()
  261. board = None
  262. if board_value and "ROCK Pi S" in board_value:
  263. board = boards.ROCK_PI_S
  264. return board
  265. def _clockwork_pi_id(self):
  266. """Check what type of Clockwork Pi board."""
  267. board_value = self.detector.get_device_model()
  268. board = None
  269. if board_value and "Clockwork CPI3" in board_value:
  270. board = boards.CLOCKWORK_CPI3
  271. return board
  272. def _udoo_id(self):
  273. """Try to detect the id of udoo board."""
  274. board_asset_tag = self.detector.check_board_asset_tag_value()
  275. for board_id, board_tags in boards._UDOO_BOARD_IDS.items():
  276. if any(v == board_asset_tag for v in board_tags):
  277. return board_id
  278. return None
  279. @property
  280. def any_96boards(self):
  281. """Check whether the current board is any 96boards board."""
  282. return self.id in boards._LINARO_96BOARDS_IDS
  283. @property
  284. def any_raspberry_pi(self):
  285. """Check whether the current board is any Raspberry Pi."""
  286. return self._pi_rev_code() is not None
  287. @property
  288. def any_raspberry_pi_40_pin(self):
  289. """Check whether the current board is any 40-pin Raspberry Pi."""
  290. return self.id in boards._RASPBERRY_PI_40_PIN_IDS
  291. @property
  292. def any_raspberry_pi_cm(self):
  293. """Check whether the current board is any Compute Module Raspberry Pi."""
  294. return self.id in boards._RASPBERRY_PI_CM_IDS
  295. @property
  296. def any_beaglebone(self):
  297. """Check whether the current board is any Beaglebone-family system."""
  298. return self.id in boards._BEAGLEBONE_IDS
  299. @property
  300. def any_orange_pi(self):
  301. """Check whether the current board is any defined Orange Pi."""
  302. return self.id in boards._ORANGE_PI_IDS
  303. @property
  304. def any_coral_board(self):
  305. """Check whether the current board is any defined Coral."""
  306. return self.CORAL_EDGE_TPU_DEV
  307. @property
  308. def any_pynq_board(self):
  309. """Check whether the current board is any defined PYNQ Board."""
  310. return self.id in boards._PYNQ_IDS
  311. @property
  312. def any_giant_board(self):
  313. """Check whether the current board is any defined Giant Board."""
  314. return self.GIANT_BOARD
  315. @property
  316. def any_odroid_40_pin(self):
  317. """Check whether the current board is any defined 40-pin Odroid."""
  318. return self.id in boards._ODROID_40_PIN_IDS
  319. @property
  320. def any_jetson_board(self):
  321. """Check whether the current board is any defined Jetson Board."""
  322. return self.id in boards._JETSON_IDS
  323. @property
  324. def any_sifive_board(self):
  325. """Check whether the current board is any defined Jetson Board."""
  326. return self.id in boards._SIFIVE_IDS
  327. @property
  328. def any_onion_omega_board(self):
  329. """Check whether the current board is any defined OpenWRT board."""
  330. return self.id in boards._ONION_OMEGA_BOARD_IDS
  331. @property
  332. def any_pine64_board(self):
  333. """Check whether the current board is any Pine64 device."""
  334. return self.id in boards._PINE64_DEV_IDS
  335. @property
  336. def any_rock_pi_board(self):
  337. """Check whether the current board is any Clockwork Pi device."""
  338. return self.ROCK_PI_S
  339. @property
  340. def any_clockwork_pi_board(self):
  341. """Check whether the current board is any Clockwork Pi device."""
  342. return self.CLOCKWORK_CPI3
  343. @property
  344. def any_udoo_board(self):
  345. """Check to see if the current board is an UDOO board"""
  346. return self.id in boards._UDOO_BOARD_IDS
  347. @property
  348. def any_embedded_linux(self):
  349. """Check whether the current board is any embedded Linux device."""
  350. return any(
  351. [
  352. self.any_raspberry_pi,
  353. self.any_beaglebone,
  354. self.any_orange_pi,
  355. self.any_giant_board,
  356. self.any_jetson_board,
  357. self.any_coral_board,
  358. self.any_odroid_40_pin,
  359. self.any_96boards,
  360. self.any_sifive_board,
  361. self.any_onion_omega_board,
  362. self.any_pine64_board,
  363. self.any_pynq_board,
  364. self.any_rock_pi_board,
  365. self.any_clockwork_pi_board,
  366. self.any_udoo_board,
  367. ]
  368. )
  369. @property
  370. def ftdi_ft232h(self):
  371. """Check whether the current board is an FTDI FT232H."""
  372. return self.id == boards.FTDI_FT232H
  373. @property
  374. def microchip_mcp2221(self):
  375. """Check whether the current board is a Microchip MCP2221."""
  376. return self.id == boards.MICROCHIP_MCP2221
  377. @property
  378. def binho_nova(self):
  379. """Check whether the current board is an BINHO NOVA."""
  380. return self.id == boards.BINHO_NOVA
  381. @property
  382. def greatfet_one(self):
  383. """Check whether the current board is a GreatFET One."""
  384. return self.id == boards.GREATFET_ONE
  385. def __getattr__(self, attr):
  386. """
  387. Detect whether the given attribute is the currently-detected board. See list
  388. of constants at the top of this module for available options.
  389. """
  390. if self.id == attr:
  391. return True
  392. return False