refactor board detection to dependent on chip; simplify getattr checking

Per feedback here and elsewhere:

https://forum.armbian.com/topic/8981-adafruit-circuitpython/?tab=comments#comment-67633

There's still plenty to be done here, but this is cleaner than it was.

Also makes some names more consistent, and uses uppercase string values for id
constants.
This commit is contained in:
Brennen Bearnes 2018-12-11 14:25:36 -07:00
parent 672713699e
commit 0c491f95a2
4 changed files with 134 additions and 131 deletions

View file

@ -34,18 +34,39 @@ class PlatformDetect:
self.board = Board(self)
self.chip = Chip(self)
def cpuinfo_field(self, field):
def get_cpuinfo_field(self, field):
"""
Search /proc/cpuinfo for a field and return its value, if found,
otherwise None.
"""
# Match a line like 'Hardware : BCM2709':
pattern = r'^' + field + r'\s+:\s+(.*)$'
with open('/proc/cpuinfo', 'r') as infile:
cpuinfo = infile.read().split('\n')
for line in cpuinfo:
# Match a line like 'Hardware : BCM2709':
pattern = r'^' + field + r'\s+:\s+(.*)$'
match = re.search(pattern, line, flags=re.IGNORECASE)
if match:
return match.group(1)
return None
def get_armbian_release_field(self, field):
"""
Search /etc/armbian-release, if it exists, for a field and return its
value, if found, otherwise None.
"""
field_value = None
pattern = r'^' + field + r'=(.*)'
try:
with open("/etc/armbian-release", 'r') as f:
armbian = f.read().split('\n')
for line in armbian:
match = re.search(pattern, line)
if match:
field_value = match.group(1)
except FileNotFoundError:
pass
return field_value

View file

@ -1,3 +1,4 @@
import adafruit_platformdetect.chip as ap_chip
import platform
import sys
import re
@ -5,25 +6,25 @@ import re
# Pi revision codes from:
# https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
BEAGLEBONE_BLACK = "beaglebone_black"
FEATHER_HUZZAH="feather_huzzah"
FEATHER_M0_EXPRESS="feather_m0_express"
PYBOARD = "pyboard"
NODEMCU = "nodemcu"
ORANGEPI_PC = "orangepipc"
BEAGLEBONE_BLACK = "BEAGLEBONE_BLACK"
FEATHER_HUZZAH = "FEATHER_HUZZAH"
FEATHER_M0_EXPRESS="FEATHER_M0_EXPRESS"
PYBOARD = "PYBOARD"
NODEMCU = "NODEMCU"
ORANGE_PI_PC = "ORANGE_PI_PC"
RASPBERRY_PI_B = "raspberry_pi_b"
RASPBERRY_PI_B_PLUS = "raspberry_pi_b_plus"
RASPBERRY_PI_A = "raspberry_pi_a"
RASPBERRY_PI_A_PLUS = "raspberry_pi_a_plus"
RASPBERRY_PI_CM1 = "raspberry_pi_cm1"
RASPBERRY_PI_ZERO = "raspberry_pi_zero"
RASPBERRY_PI_ZERO_W = "raspberry_pi_zero_w"
RASPBERRY_PI_2B = "raspberry_pi_2b"
RASPBERRY_PI_3B = "raspberry_pi_3b"
RASPBERRY_PI_3B_PLUS = "raspberry_pi_3b_plus"
RASPBERRY_PI_CM3 = "raspberry_pi_cm3"
RASPBERRY_PI_3A_PLUS = "raspberry_pi_3a_plus"
RASPBERRY_PI_B = "RASPBERRY_PI_B"
RASPBERRY_PI_B_PLUS = "RASPBERRY_PI_B_PLUS"
RASPBERRY_PI_A = "RASPBERRY_PI_A"
RASPBERRY_PI_A_PLUS = "RASPBERRY_PI_A_PLUS"
RASPBERRY_PI_CM1 = "RASPBERRY_PI_CM1"
RASPBERRY_PI_ZERO = "RASPBERRY_PI_ZERO"
RASPBERRY_PI_ZERO_W = "RASPBERRY_PI_ZERO_W"
RASPBERRY_PI_2B = "RASPBERRY_PI_2B"
RASPBERRY_PI_3B = "RASPBERRY_PI_3B"
RASPBERRY_PI_3B_PLUS = "RASPBERRY_PI_3B_PLUS"
RASPBERRY_PI_CM3 = "RASPBERRY_PI_CM3"
RASPBERRY_PI_3A_PLUS = "RASPBERRY_PI_3A_PLUS"
# TODO: Should this include RASPBERRY_PI_3A_PLUS or any other models?
ANY_RASPBERRY_PI_2_OR_3 = (
@ -55,109 +56,81 @@ class Board:
self.detect = detect
@property
def name(self):
"""Return a human-readable name for the detected board, if any."""
name = None
if sys.platform == "linux":
name = self._linux_computer_name()
elif sys_platform == "esp8266": # TODO more conservative board-guessing
def id(self):
"""Return a unique id for the detected board, if any."""
chip_id = self.detect.chip.id
if chip_id == ap_chip.BCM2XXX:
return self._pi_id()
elif chip_id == ap_chip.AM33XX:
return BEAGLEBONE_BLACK
elif chip_id == ap_chip.SUN8I:
return self._armbian_id()
elif chip_id == ap_chip.ESP8266:
return FEATHER_HUZZAH
elif sys_platform == "samd21":
elif chip_id == ap_chip.SAMD21:
return FEATHER_M0_EXPRESS
elif sys_platform == "pyboard":
elif chip_id == ap_chip.STM32:
return PYBOARD
return name
return None
def _linux_computer_name(self):
"""Try to detect name of a Linux SBC."""
def _pi_id(self):
"""Try to detect id of a Raspberry Pi."""
# Check for Pi boards:
pi_rev_code = self.pi_rev_code
pi_rev_code = self._pi_rev_code()
if pi_rev_code:
for model, codes in _PI_REV_CODES.items():
if pi_rev_code in codes:
return model
# Check for BBB:
if self.beaglebone_black:
return BEAGLEBONE_BLACK
# Check for OrangePiPC
if self.orangepi_pc:
return ORANGEPI_PC
return None
@property
def orangepi_pc(self):
"""Check whether the current board is an OrangePi PC."""
if self.detect.chip.name != "sun8i":
return False
try:
with open("/etc/armbian-release", 'r') as f:
armbian = f.read().split('\n')
for line in armbian:
match = re.search('^BOARD=(.*)', line)
if match:
return match.group(1) == "orangepipc"
except FileNotFoundError:
return False
return False
@property
def beaglebone_black(self):
"""Check whether the current board is a Beaglebone Black."""
if sys.platform != "linux" or self.any_raspberry_pi:
return False
# TODO: beaglebone_black detection is too sloppy, needs to be more specific
# before we can detect it
return False
# TODO: Check the Beaglebone Black /proc/cpuinfo value instead of first
# looking for a Raspberry Pi and then falling back to platform.
plat = platform.platform().lower()
if plat.find('armv7l-with-debian') > -1:
return True
elif plat.find('armv7l-with-ubuntu') > -1:
return True
elif plat.find('armv7l-with-glibc2.4') > -1:
return True
return False
@property
def any_raspberry_pi(self):
return self.pi_rev_code is not None
@property
def any_raspberry_pi_2_or_3(self):
return self.name in ANY_RASPBERRY_PI_2_OR_3
@property
def pi_rev_code(self):
def _pi_rev_code(self):
"""Attempt to find a Raspberry Pi revision code for this board."""
# 2708 is Pi 1
# 2709 is Pi 2
# 2835 is Pi 3 (or greater) on 4.9.x kernel
# Anything else is not a Pi.
if self.detect.cpuinfo_field('Hardware') not in ('BCM2708', 'BCM2709', 'BCM2835'):
if self.detect.chip.id != ap_chip.BCM2XXX:
# Something else, not a Pi.
return None
return self.detect.cpuinfo_field('Revision')
return self.detect.get_cpuinfo_field('Revision')
@property
def _armbian_id(self):
"""Check whether the current board is an OrangePi PC."""
board_value = self.detect.get_armbian_release_field('BOARD')
if board_value == "orangepipc":
return ORANGE_PI_PC
return None
@property
def beaglebone_black(self):
"""Check whether the current board is a Beaglebone Black."""
return self.id == BEAGLEBONE_BLACK
@property
def orange_pi_pc(self):
"""Check whether the current board is an Orange Pi PC."""
return self.id == ORANGE_PI_PC
@property
def any_raspberry_pi(self):
"""Check whether the current board is any Raspberry Pi."""
return self._pi_rev_code() is not None
@property
def any_raspberry_pi_2_or_3(self):
return self.id in ANY_RASPBERRY_PI_2_OR_3
def __getattr__(self, attr):
"""
Detect whether the given attribute is the current board. Currently
handles Raspberry Pi models.
handles Raspberry Pi models; other board properties are handled by
individual methods for the time being.
"""
# Check Raspberry Pi values:
if attr in _PI_REV_CODES:
if sys.platform != "linux":
return False
return self.pi_rev_code in _PI_REV_CODES[attr]
raise AttributeError(attr + " is not a defined board")
if self.id == attr:
return True
else:
return False

View file

@ -1,35 +1,41 @@
import sys
ESP8266 = "esp8266"
SAMD21 = "samd21"
STM32 = "stm32"
SUN8I = "sun8i"
AM33XX = "AM33XX"
BCM2XXX = "BCM2XXX"
ESP8266 = "ESP8266"
SAMD21 = "SAMD21"
STM32 = "STM32"
SUN8I = "SUN8I"
class Chip:
"""Attempt detection of current chip / CPU."""
def __init__(self, detect):
self.detect = detect
@property
def name(self):
name = None
def id(self):
platform = sys.platform
if platform is not None:
if platform == "esp8266":
name = ESP8266
elif platform == "samd21":
name = SAMD21
elif platform == "pyboard":
name = STM32
elif platform == "linux":
# XXX: Here is where some work to detect ARM / x86 stuff for
# real needs to happen.
hardwarename = self.detect.cpuinfo_field("Hardware")
if not hardwarename:
return None
if "sun8i" in hardwarename:
name = SUN8I
else:
name = hardwarename
if platform == "linux":
return self._linux_id()
elif platform == "esp8266":
return ESP8266
elif platform == "samd21":
return SAMD21
elif platform == "pyboard":
return STM32
else:
return None
return name
def _linux_id(self):
"""Attempt to detect the CPU on a computer running the Linux kernel."""
id = None
hardware = self.detect.get_cpuinfo_field("Hardware")
if hardware in ('BCM2708', 'BCM2708', 'BCM2835'):
id = BCM2XXX
elif "AM33XX" in hardwarename:
id = AM33XX
elif "sun8i" in hardwarename:
id = SUN8I
return id

View file

@ -4,13 +4,16 @@ import adafruit_platformdetect
detect = adafruit_platformdetect.PlatformDetect()
print("Chip name: ", detect.chip.name)
print("Chip id: ", detect.chip.id)
print("Board name: ", detect.board.name)
print("Board id: ", detect.board.id)
print("Is this a Pi 3B+?", detect.board.RASPBERRY_PI_3B_PLUS)
print("Is this a BBB?", detect.board.BEAGLEBONE_BLACK)
if detect.board.any_raspberry_pi:
print("Raspberry Pi detected.")
print("Revision code: ", detect.board.pi_rev_code)
print("Revision code: ", detect.board._pi_rev_code())
if detect.board.beaglebone_black:
print("BBB detected")