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.

340 lines
10 KiB

  1. """Detect boards."""
  2. import os
  3. import adafruit_platformdetect.chip as ap_chip
  4. # Allow for aligned constant definitions:
  5. # pylint: disable=bad-whitespace
  6. BEAGLEBONE = 'BEAGLEBONE'
  7. BEAGLEBONE_BLACK = 'BEAGLEBONE_BLACK'
  8. BEAGLEBONE_BLUE = 'BEAGLEBONE_BLUE'
  9. BEAGLEBONE_BLACK_WIRELESS = 'BEAGLEBONE_BLACK_WIRELESS'
  10. BEAGLEBONE_POCKETBEAGLE = 'BEAGLEBONE_POCKETBEAGLE'
  11. BEAGLEBONE_GREEN = 'BEAGLEBONE_GREEN'
  12. BEAGLEBONE_GREEN_WIRELESS = 'BEAGLEBONE_GREEN_WIRELESS'
  13. BEAGLEBONE_BLACK_INDUSTRIAL = 'BEAGLEBONE_BLACK_INDUSTRIAL'
  14. BEAGLEBONE_ENHANCED = 'BEAGLEBONE_ENHANCED'
  15. BEAGLEBONE_USOMIQ = 'BEAGLEBONE_USOMIQ'
  16. BEAGLEBONE_AIR = 'BEAGLEBONE_AIR'
  17. BEAGLEBONE_POCKETBONE = 'BEAGLEBONE_POCKETBONE'
  18. BEAGLELOGIC_STANDALONE = 'BEAGLELOGIC_STANDALONE'
  19. OSD3358_DEV_BOARD = 'OSD3358_DEV_BOARD'
  20. OSD3358_SM_RED = 'OSD3358_SM_RED'
  21. FEATHER_HUZZAH = "FEATHER_HUZZAH"
  22. FEATHER_M0_EXPRESS = "FEATHER_M0_EXPRESS"
  23. GENERIC_LINUX_PC = "GENERIC_LINUX_PC"
  24. PYBOARD = "PYBOARD"
  25. NODEMCU = "NODEMCU"
  26. ORANGE_PI_PC = "ORANGE_PI_PC"
  27. RASPBERRY_PI_B_REV1 = "RASPBERRY_PI_B_REV1"
  28. RASPBERRY_PI_B_REV2 = "RASPBERRY_PI_B_REV2"
  29. RASPBERRY_PI_B_PLUS = "RASPBERRY_PI_B_PLUS"
  30. RASPBERRY_PI_A = "RASPBERRY_PI_A"
  31. RASPBERRY_PI_A_PLUS = "RASPBERRY_PI_A_PLUS"
  32. RASPBERRY_PI_CM1 = "RASPBERRY_PI_CM1"
  33. RASPBERRY_PI_ZERO = "RASPBERRY_PI_ZERO"
  34. RASPBERRY_PI_ZERO_W = "RASPBERRY_PI_ZERO_W"
  35. RASPBERRY_PI_2B = "RASPBERRY_PI_2B"
  36. RASPBERRY_PI_3B = "RASPBERRY_PI_3B"
  37. RASPBERRY_PI_3B_PLUS = "RASPBERRY_PI_3B_PLUS"
  38. RASPBERRY_PI_CM3 = "RASPBERRY_PI_CM3"
  39. RASPBERRY_PI_3A_PLUS = "RASPBERRY_PI_3A_PLUS"
  40. ODROID_C1 = "ODROID_C1"
  41. ODROID_C1_PLUS = "ODROID_C1_PLUS"
  42. ODROID_C2 = "ODROID_C2"
  43. FTDI_FT232H = "FT232H"
  44. # pylint: enable=bad-whitespace
  45. _RASPBERRY_PI_40_PIN_IDS = (
  46. RASPBERRY_PI_B_PLUS,
  47. RASPBERRY_PI_A_PLUS,
  48. RASPBERRY_PI_ZERO,
  49. RASPBERRY_PI_ZERO_W,
  50. RASPBERRY_PI_2B,
  51. RASPBERRY_PI_3B,
  52. RASPBERRY_PI_3B_PLUS,
  53. RASPBERRY_PI_3A_PLUS
  54. )
  55. _ODROID_40_PIN_IDS = (
  56. ODROID_C1,
  57. ODROID_C1_PLUS,
  58. ODROID_C2
  59. )
  60. _BEAGLEBONE_IDS = (
  61. BEAGLEBONE,
  62. BEAGLEBONE_BLACK,
  63. BEAGLEBONE_BLUE,
  64. BEAGLEBONE_BLACK_WIRELESS,
  65. BEAGLEBONE_POCKETBEAGLE,
  66. BEAGLEBONE_GREEN,
  67. BEAGLEBONE_GREEN_WIRELESS,
  68. BEAGLEBONE_BLACK_INDUSTRIAL,
  69. BEAGLEBONE_ENHANCED,
  70. BEAGLEBONE_USOMIQ,
  71. BEAGLEBONE_AIR,
  72. BEAGLEBONE_POCKETBONE,
  73. BEAGLELOGIC_STANDALONE,
  74. OSD3358_DEV_BOARD,
  75. OSD3358_SM_RED,
  76. )
  77. # BeagleBone eeprom board ids from:
  78. # https://github.com/beagleboard/image-builder
  79. # Thanks to zmatt on freenode #beagle for pointers.
  80. _BEAGLEBONE_BOARD_IDS = {
  81. # Original bone/white:
  82. BEAGLEBONE: (
  83. ('A4', 'A335BONE00A4'),
  84. ('A5', 'A335BONE00A5'),
  85. ('A6', 'A335BONE00A6'),
  86. ('A6A', 'A335BONE0A6A'),
  87. ('A6B', 'A335BONE0A6B'),
  88. ('B', 'A335BONE000B'),
  89. ),
  90. BEAGLEBONE_BLACK: (
  91. ('A5', 'A335BNLT00A5'),
  92. ('A5A', 'A335BNLT0A5A'),
  93. ('A5B', 'A335BNLT0A5B'),
  94. ('A5C', 'A335BNLT0A5C'),
  95. ('A6', 'A335BNLT00A6'),
  96. ('C', 'A335BNLT000C'),
  97. ('C', 'A335BNLT00C0'),
  98. ),
  99. BEAGLEBONE_BLUE: (
  100. ('A2', 'A335BNLTBLA2'),
  101. ),
  102. BEAGLEBONE_BLACK_WIRELESS: (
  103. ('A5', 'A335BNLTBWA5'),
  104. ),
  105. BEAGLEBONE_POCKETBEAGLE: (
  106. ('A2', 'A335PBGL00A2'),
  107. ),
  108. BEAGLEBONE_GREEN: (
  109. ('1A', 'A335BNLT....'),
  110. ('UNKNOWN', 'A335BNLTBBG1'),
  111. ),
  112. BEAGLEBONE_GREEN_WIRELESS: (
  113. ('W1A', 'A335BNLTGW1A'),
  114. ),
  115. BEAGLEBONE_BLACK_INDUSTRIAL: (
  116. ('A0', 'A335BNLTAIA0'), # Arrow
  117. ('A0', 'A335BNLTEIA0'), # Element14
  118. ),
  119. BEAGLEBONE_ENHANCED: (
  120. ('A', 'A335BNLTSE0A'),
  121. ),
  122. BEAGLEBONE_USOMIQ: (
  123. ('6', 'A335BNLTME06'),
  124. ),
  125. BEAGLEBONE_AIR: (
  126. ('A0', 'A335BNLTNAD0'),
  127. ),
  128. BEAGLEBONE_POCKETBONE: (
  129. ('0', 'A335BNLTBP00'),
  130. ),
  131. OSD3358_DEV_BOARD: (
  132. ('0.1', 'A335BNLTGH01'),
  133. ),
  134. OSD3358_SM_RED: (
  135. ('0', 'A335BNLTOS00'),
  136. ),
  137. BEAGLELOGIC_STANDALONE: (
  138. ('A', 'A335BLGC000A'),
  139. )
  140. }
  141. # Pi revision codes from:
  142. # https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
  143. # Each tuple here contains both the base codes, and the versions that indicate
  144. # the Pi is overvolted / overclocked - for 4-digit codes, this will be prefixed
  145. # with 1000, and for 6-digit codes it'll be prefixed with 1. These are placed
  146. # on separate lines.
  147. _PI_REV_CODES = {
  148. RASPBERRY_PI_B_REV1: (
  149. # Regular codes:
  150. '0002', '0003',
  151. # Overvolted/clocked versions:
  152. '10000002', '10000003',
  153. ),
  154. RASPBERRY_PI_B_REV2: (
  155. '0005', '0006', '000d', '000e', '000f',
  156. '10000005', '10000006', '1000000d', '1000000e', '1000000f',
  157. ),
  158. RASPBERRY_PI_B_PLUS: (
  159. '0010', '0013', '900032',
  160. '10000010', '10000013', '1900032',
  161. ),
  162. RASPBERRY_PI_A: (
  163. '0007', '0008', '0009',
  164. '10000007', '10000008', '10000009',
  165. ),
  166. RASPBERRY_PI_A_PLUS: (
  167. '0012', '0015', '900021',
  168. '10000012', '10000015', '1900021',
  169. ),
  170. RASPBERRY_PI_CM1: (
  171. '0011', '0014',
  172. '10000011', '10000014',
  173. ),
  174. RASPBERRY_PI_ZERO: (
  175. '900092', '920092', '900093', '920093'
  176. '1900092', '1920092', '1900093', '1920093',
  177. ),
  178. RASPBERRY_PI_ZERO_W: (
  179. '9000c1',
  180. '19000c1',
  181. ),
  182. RASPBERRY_PI_2B: (
  183. 'a01040', 'a01041', 'a21041', 'a22042',
  184. '1a01040', '1a01041', '1a21041', '1a22042',
  185. ),
  186. RASPBERRY_PI_3B: (
  187. 'a02082', 'a22082', 'a32082', 'a52082',
  188. '1a02082', '1a22082', '1a32082', '1a52082',
  189. ),
  190. RASPBERRY_PI_3B_PLUS: (
  191. 'a020d3',
  192. '1a020d3',
  193. ),
  194. RASPBERRY_PI_CM3: (
  195. 'a020a0',
  196. '1a020a0',
  197. ),
  198. RASPBERRY_PI_3A_PLUS: (
  199. '9020e0',
  200. '19020e0',
  201. ),
  202. }
  203. class Board:
  204. """Attempt to detect specific boards."""
  205. def __init__(self, detector):
  206. self.detector = detector
  207. # pylint: disable=invalid-name
  208. @property
  209. def id(self):
  210. """Return a unique id for the detected board, if any."""
  211. # There are some times we want to trick the platform detection
  212. # say if a raspberry pi doesn't have the right ID, or for testing
  213. try:
  214. return os.environ['BLINKA_FORCEBOARD']
  215. except KeyError: # no forced board, continue with testing!
  216. pass
  217. chip_id = self.detector.chip.id
  218. board_id = None
  219. if chip_id == ap_chip.BCM2XXX:
  220. board_id = self._pi_id()
  221. elif chip_id == ap_chip.AM33XX:
  222. board_id = self._beaglebone_id()
  223. elif chip_id == ap_chip.GENERIC_X86:
  224. board_id = GENERIC_LINUX_PC
  225. elif chip_id == ap_chip.SUN8I:
  226. board_id = self._armbian_id()
  227. elif chip_id == ap_chip.ESP8266:
  228. board_id = FEATHER_HUZZAH
  229. elif chip_id == ap_chip.SAMD21:
  230. board_id = FEATHER_M0_EXPRESS
  231. elif chip_id == ap_chip.STM32:
  232. board_id = PYBOARD
  233. elif chip_id == ap_chip.S805:
  234. board_id = ODROID_C1
  235. elif chip_id == ap_chip.S905:
  236. board_id = ODROID_C2
  237. elif chip_id == ap_chip.FT232H:
  238. board_id = FTDI_FT232H
  239. return board_id
  240. # pylint: enable=invalid-name
  241. def _pi_id(self):
  242. """Try to detect id of a Raspberry Pi."""
  243. # Check for Pi boards:
  244. pi_rev_code = self._pi_rev_code()
  245. if pi_rev_code:
  246. for model, codes in _PI_REV_CODES.items():
  247. if pi_rev_code in codes:
  248. return model
  249. return None
  250. def _pi_rev_code(self):
  251. """Attempt to find a Raspberry Pi revision code for this board."""
  252. # 2708 is Pi 1
  253. # 2709 is Pi 2
  254. # 2835 is Pi 3 (or greater) on 4.9.x kernel
  255. # Anything else is not a Pi.
  256. if self.detector.chip.id != ap_chip.BCM2XXX:
  257. # Something else, not a Pi.
  258. return None
  259. return self.detector.get_cpuinfo_field('Revision')
  260. # pylint: disable=no-self-use
  261. def _beaglebone_id(self):
  262. """Try to detect id of a Beaglebone."""
  263. try:
  264. with open("/sys/bus/nvmem/devices/0-00500/nvmem", "rb") as eeprom:
  265. eeprom_bytes = eeprom.read(16)
  266. except FileNotFoundError:
  267. return None
  268. if eeprom_bytes[:4] != b'\xaaU3\xee':
  269. return None
  270. id_string = eeprom_bytes[4:].decode("ascii")
  271. for model, bb_ids in _BEAGLEBONE_BOARD_IDS.items():
  272. for bb_id in bb_ids:
  273. if id_string == bb_id[1]:
  274. return model
  275. return None
  276. # pylint: enable=no-self-use
  277. def _armbian_id(self):
  278. """Check whether the current board is an OrangePi PC."""
  279. board_value = self.detector.get_armbian_release_field('BOARD')
  280. if board_value == "orangepipc":
  281. return ORANGE_PI_PC
  282. return None
  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 _RASPBERRY_PI_40_PIN_IDS
  291. @property
  292. def any_beaglebone(self):
  293. """Check whether the current board is any Beaglebone-family system."""
  294. return self.id in _BEAGLEBONE_IDS
  295. @property
  296. def any_orange_pi(self):
  297. """Check whether the current board is any defined Orange Pi."""
  298. return self.ORANGE_PI_PC
  299. @property
  300. def any_embedded_linux(self):
  301. """Check whether the current board is any embedded Linux device."""
  302. return self.any_raspberry_pi or self.any_beaglebone or self.any_orange_pi
  303. def __getattr__(self, attr):
  304. """
  305. Detect whether the given attribute is the currently-detected board. See list
  306. of constants at the top of this module for available options.
  307. """
  308. if self.id == attr:
  309. return True
  310. return False