From 89610a3568e1d96d6fea50daad14e966b3566fd6 Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Fri, 5 Nov 2021 12:18:45 -0600
Subject: [PATCH] Documenting for publication

---
 projects/clusterctrl/README.md                | 103 +++++++++++++++++-
 .../src/python/clusterctrl/xra1200.py         |  65 -----------
 2 files changed, 98 insertions(+), 70 deletions(-)
 delete mode 100644 projects/clusterctrl/src/python/clusterctrl/xra1200.py

diff --git a/projects/clusterctrl/README.md b/projects/clusterctrl/README.md
index 7710fa7..d2008ec 100644
--- a/projects/clusterctrl/README.md
+++ b/projects/clusterctrl/README.md
@@ -1,13 +1,106 @@
 # clusterctrl
 
-This project is a rewrite of the `clusterctrl` tool provided by the 8086 consultancy for interacting with their ClusterCTRL and ClusterHAT line of Raspberry Pi backplane products.
+This project is a clean-sheet rewrite of the `clusterctrl` tool and underlying device driver provided by the 8086 consultancy for interacting with their ClusterCTRL and ClusterHAT line of Raspberry Pi backplane products.
 
-It serves to factor out the underlying i²c driver common to the ClusterCTRL family of products, and aims to implement a far more idiomatic and better documented and far more consistent CLI tool.
+## Usage & driver API
+
+``` python
+from clusterctrl.driver import ClusterCTRLv2Driver as Driver
+from smbus2 import SMBus
+
+hat = Driver(SMBus(3))  # Note that 3 here is the number of the i2c device the HAT is on
+```
+
+A quick API overview -
+
+The CTRL/HAT products "order" themselves (identify boards) using an EEPROM stored value called 'order'.
+The official clients use "order" to enable for somewhat stable sequential addressing of "pi6" as being board 2 pi 1.
+However it's far more general purpose and predictable to directly expose and model the device tree.
+
+Note that the firmware default for "order" is `20` and this driver will automatically assign random IDs to boards with orders of 20.
+This behavior can be disabled by subclassing the driver and overloading `_post_init()`.
+
+This API is built atop a `PiRef(board_id, pi_id)` tuple which is intended to allow for the construction of cluster management APIs which allow for automatic but predictable mapping of requests (eg. `power_on`, `power_status`) to a given device.
+
+``` python
+hat.fw_version  # => (1, 6)
+hat.min_pi      # => <PiRef XXX-01>
+hat.max_pi      # => <PiRef XXX-05>
+hat.pis()       # => Iterable[PiRef] (iterate over all Pis on this device in order)
+hat.type        # => BoardType
+hat.max_adc     # => int (ADC support is incomplete)
+```
+
+### Power status
+
+```
+hat.power_on(<int | PiRef>)
+hat.power_off(<int | PiRef>)
+hat.power_status(<int | PiRef>)
+
+hat.power_all_off()
+hat.power_all_on()
+
+hat.eeprom_save_powerstate()
+```
+
+### Board identification
+
+``` python
+hat.get_order()
+hat.set_order(<order: uint8>)
+
+hat.eeprom_save_order()
+```
+
+### Alert lights
+
+``` python
+hat.alert_on()
+hat.alert_off()
+
+hat.led_on()
+hat.led_off()
+
+hat.eeprom_save_leds()
+```
+
+### Fan control
+
+``` python
+hat.fan_on()
+hat.fan_off()
+hat.fan_status()
+
+hat.read_temp()  # Temp in integer kelvin
+```
+
+### USB hub control
+
+``` python
+hat.hub_on()
+hat.hub_off()
+hat.hub_reset()
+```
+
+### USB booting
+
+``` python
+hat.usbboot_on()
+hat.usbboot_off()
+hat.usbboot_status()
+hat.eeprom_save_usbboot()
+```
+
+#### Driver.eeprom_reset
+Reset all EEPROM stored settings to their 'factory' firmware defaults.
+
+#### Driver.eeprom_save_all
+Save all values back to EEPROM.
+Note that this does not update the defaults restored by `Driver.eeprom_reset`.
 
 ## license
 
-Copyright (c) 2018 Chris Burton (8086 consultancy)
 Copyright (c) 2021 Reid McKenzie
 
-This software is published under the MIT License
-
+This software is published under the terms of the MIT License
diff --git a/projects/clusterctrl/src/python/clusterctrl/xra1200.py b/projects/clusterctrl/src/python/clusterctrl/xra1200.py
deleted file mode 100644
index 5185ffb..0000000
--- a/projects/clusterctrl/src/python/clusterctrl/xra1200.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import smbus
-
-
-# Registers
-# Direction
-IN = 0x00
-OUT = 0x01
-DIR = 0x03
-PUR = 0x04
-
-INPUT = 1
-OUTPUT = 0
-
-
-class Xra1200:
-    bus = -1
-    address = -1
-    port = -1
-
-    def __init__(self, bus=0, address=0x39, port=0, dir="Null"):
-        self.bus = smbus.SMBus(bus)
-        self.address = address
-        self.port = port
-        if dir == 1:
-            self.set_input()
-        elif dir == 0:
-            self.set_output()
-
-    def set_dir(self, dir):
-        self.bus.write_byte_data(self.address, DIR, dir)
-
-    def get_dir(self):
-        return self.bus.read_byte_data(self.address, DIR)
-
-    def set_pur(self, pur):
-        self.bus.write_byte_data(self.address, PUR, pur)
-
-    def get_pur(self):
-        try:
-            reg = self.bus.read_byte_data(self.address, PUR)
-        except IOError as err:
-            return -1
-        return reg
-
-    def set_input(self):
-        state = self.bus.read_byte_data(self.address, DIR)
-        self.bus.write_byte_data(self.address, DIR, state | 1 << self.port)
-
-    def write_byte(self, data):
-        self.bus.write_byte_data(self.address, OUT, data)
-
-    def read_byte(self):
-        return self.bus.read_byte_data(self.address, IN)
-
-    def on(self):
-        state = self.bus.read_byte_data(self.address, OUT)
-        self.bus.write_byte_data(self.address, OUT, state | 1 << self.port)
-
-    def off(self):
-        state = self.bus.read_byte_data(self.address, OUT)
-        self.bus.write_byte_data(self.address, OUT, state & (255 - (1 << self.port)))
-
-    def get(self):
-        state = self.bus.read_byte_data(self.address, IN)
-        return (state >> self.port) & 1