feat: initial bluetooth support (#9)

* Enable Bluetooth LE radio support
* Build our own bccmd which we need to setup/enable this BT chipset sadly
* Use our own tool we build to interface with the ubnt eeprom, so we can not rely on their custom kernel module
* Also fix HDDs not spinning down on shutdown, doing something similar to how unifi does it but a tad more generic.
This commit is contained in:
Chris Blake 2024-06-12 20:38:37 -05:00 committed by GitHub
parent fe3b4cd76f
commit 9ef1a72228
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 678 additions and 20 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ unifi-firmware/*.bin
BuildEnv
output
downloads
tools/ubnteeprom/test-files

View file

@ -13,6 +13,7 @@ RUN apt-get update && apt-get install -yq \
bsdextrautils \
build-essential \
cpio \
curl \
debootstrap \
debhelper \
device-tree-compiler \
@ -26,9 +27,14 @@ RUN apt-get update && apt-get install -yq \
kpartx \
libconfuse-common \
libconfuse-dev \
libdbus-1-dev \
libelf-dev \
libglib2.0-dev \
libical-dev \
libncurses-dev \
libreadline-dev \
libssl-dev \
libudev-dev \
lvm2 \
mtools \
parted \
@ -47,4 +53,11 @@ RUN apt-get update && apt-get install -yq \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& /setup_mkimage.sh \
&& rm /setup_mkimage.sh
&& rm /setup_mkimage.sh \
&& curl -fsSL "https://go.dev/dl/go1.22.4.linux-amd64.tar.gz" -o golang.tar.gz \
&& tar -C /usr/local -xzf golang.tar.gz \
&& rm golang.tar.gz \
&& for bin in `ls /usr/local/go/bin/`; do \
update-alternatives --install "/usr/bin/$bin" "$bin" "/usr/local/go/bin/$bin" 1; \
update-alternatives --set "$bin" "/usr/local/go/bin/$bin"; \
done

View file

@ -4,7 +4,8 @@ Description=UBNT bootup init script
[Service]
User=root
Type=oneshot
ExecStart=/usr/lib/init/boot/ubnt-init.sh
ExecStart=/usr/lib/init/boot/ubnt-init.sh start
ExecStop=/usr/lib/init/boot/ubnt-init.sh stop
RemainAfterExit=yes
[Install]

View file

@ -1,13 +1,7 @@
#!/bin/bash
if [ "$1" == "id" ]; then
BOARD_SYSID=$(grep systemid /proc/ubnthal/system.info | sed 's|systemid=||g')
BOARD_SERIALNO=$(grep serialno /proc/ubnthal/system.info | sed 's|serialno=||g')
# BOM is in SPI on EEPROM part, offset D000 is start of TlvInfo. BOM starts with 113-
BOARD_BOM=$(dd if=/dev/mtd4 bs=64 skip=832 count=1 status=none | strings | grep 113-)
echo "board.sysid=0x${BOARD_SYSID}"
echo "board.serialno=${BOARD_SERIALNO}"
echo "board.bom=${BOARD_BOM}"
ubnteeprom -tools
else
echo "Unknown ubnt-tools cmd: $@" >> /tmp/ubnt-tools-unknown.log
fi

View file

@ -0,0 +1,178 @@
#!/bin/bash
function log_error() {
echo "<3> ${*}" 1>&2
}
function get_bt_mac() {
local bt_device_num="$1"
if [ -z "${bt_device_num}" ]; then
log_error "get_bt_mac: Unknown device num"
return 1
fi
local hw_addr_base=$(ubnteeprom -board -key "hwaddrbbase")
local eth_count=$(ubnteeprom -board -key "EthMACAddrCount")
local wifi_count=$(ubnteeprom -board -key "WiFiMACAddrCount")
local bt_count=$(ubnteeprom -board -key "BtMACAddrCount")
if [ -z "${hw_addr_base}" ] || [ -z "${eth_count}" ] || [ -z "${wifi_count}" ] || [ -z "${bt_count}" ]; then
log_error "Unexpected contents in $UBNTHAL_BOARD"
return 2
fi
if [ ${bt_device_num} -ge ${bt_count} ]; then
log_error "Unsupported device number (bt_device_num[${bt_device_num}] >= bt_count[${bt_count}])"
return 3
fi
local mac=$(echo "${hw_addr_base}" | sed s/":"//g)
local mac_dec=$(printf '%d\n' 0x${mac})
local bt_mac_dec=$(expr ${mac_dec} + ${eth_count} + ${wifi_count} + ${bt_device_num})
printf '%012X\n' "${bt_mac_dec}" | tr A-Z a-z
}
function main(){
BT_DEVICE="$1"
[ -z "$BT_DEVICE" ] && return 2
BT_DEVICE_NUM=$(echo "${BT_DEVICE}" | sed s/"hci"//g)
if [[ ! "${BT_DEVICE_NUM}" =~ ^[0-9]+$ ]]; then
log_error "Invalid bluetooth device number [${BT_DEVICE_NUM}]"
return 3
fi
BT_MAC=$(get_bt_mac "${BT_DEVICE_NUM}")
[ $? -eq 0 ] || return 4
local board_id=$(ubnteeprom -board -key "boardid")
case ${board_id} in
ea16)
# unvr: nothing to do here
;;
ea1a)
usb_based_init
;;
ea20|ea50|ea51)
gpio_num=$(find_gpio_on_expander 0 0020 8)
if [ $gpio_num -lt 0 ]; then
return 5
fi
gpio_reset $gpio_num
uart_based_init /dev/ttyS3 "/lib/firmware/csr8x11/csr8x11-a12-bt4.2-patch-2018_uart.psr"
;;
ea2c|ea15|ea11|ea32)
gpio_reset 37
uart_based_init /dev/ttyS1 "/lib/firmware/csr8x11/csr8x11-a12-bt4.2-patch-2018_uart.psr"
;;
ea3d|ea3e)
gpio_num=$(find_gpio_on_expander 3 0029 13)
if [ $gpio_num -lt 0 ]; then
return 5
fi
gpio_reset $gpio_num
uart_based_init /dev/ttyAMA1 "/lib/firmware/csr8x11/pb-207-csr8x11-rev7-flowcontrol.psr" "flow"
;;
a678|a690|a69a)
gpio_reset 490
usb_based_init
;;
e990)
gpio_reset 306
usb_based_init
;;
*)
return 4
;;
esac
}
function find_gpio_on_expander() {
local bus=$1
local addr=$2
local pin=$3
local gpiochip_dir="/sys/bus/i2c/devices/$bus-$addr/gpio"
local base
for chip in $(find $gpiochip_dir -maxdepth 1 -name 'gpiochip*' -printf "%f\n"); do
base=$(echo $chip | sed 's/gpiochip//g')
echo $((base + pin))
return
done
echo -1
}
function gpio_reset(){
local gpio=$1
if [ ! -d /sys/class/gpio/gpio${gpio} ]; then
echo ${gpio} > /sys/class/gpio/export
fi
echo out > /sys/class/gpio/gpio${gpio}/direction
echo 1 > /sys/class/gpio/gpio${gpio}/value
sleep 1
echo 0 > /sys/class/gpio/gpio${gpio}/value
sleep 1
echo 1 > /sys/class/gpio/gpio${gpio}/value
sleep 1
echo ${gpio} > /sys/class/gpio/unexport
}
function uart_based_init(){
local bt_serial_dev="$1"
local bt_serial_speed="115200"
local bt_fw="$2"
local bt_option="$3"
local bt_proto="bcsp"
local loop_no=10
local i=0
for i in $(seq 0 ${loop_no}); do
if [ ${i} -gt 0 ]; then
if [ ${i} -eq ${loop_no} ]; then
log_error "Failed to initialize bluetooth (BT is not operational)"
break
fi
log_error "Unable to initialize bluetooth. Give it another try (${i})"
fi
#load psr file
bccmd -t "${bt_proto}" -b "${bt_serial_speed}" -d "${bt_serial_dev}" psload -r ${bt_fw} || continue
#set bt address: <device index> <mac byte 4> 0x00 <mac byte 6> <mac byte 5> <mac byte 3> 0x00 <mac byte 2> <mac byte 1>
bccmd -t "${bt_proto}" -d "${bt_serial_dev}" -b "${bt_serial_speed}" psset -r \
0x$((BT_DEVICE_NUM+1)) \
0x${BT_MAC:6:2} \
0x00 \
0x${BT_MAC:10:2} \
0x${BT_MAC:8:2} \
0x${BT_MAC:4:2} \
0x00 \
0x${BT_MAC:2:2} \
0x${BT_MAC:0:2} \
2>&1
# attach UART interface
hciattach -s "${bt_serial_speed}" "${bt_serial_dev}" "${bt_proto}" "${bt_serial_speed}" "${bt_option}"
sleep 0.5
# check if the device exists
[ -d "/sys/class/bluetooth/${BT_DEVICE}" ] && break
done
hciconfig ${BT_DEVICE} up
}
function usb_based_init(){
hciconfig ${BT_DEVICE} up; hciconfig ${BT_DEVICE} up
sleep 1
bccmd psset -r 0x$((BT_DEVICE_NUM+1)) 0x${BT_MAC:6:2} 00 0x${BT_MAC:10:2} 0x${BT_MAC:8:2} 0x${BT_MAC:4:2} 00 0x${BT_MAC:2:2} 0x${BT_MAC:0:2}
sleep 2
hciconfig ${BT_DEVICE} down
hciconfig ${BT_DEVICE} up; hciconfig ${BT_DEVICE} up
}
main "$@"

View file

@ -1,8 +1,22 @@
#!/bin/bash
case "$1" in
start)
# Load our kernel modules
/usr/sbin/modprobe ubnthal
/usr/sbin/modprobe btrfs
# Set our kernel panic timeout SUPER short so we reboot on crash
echo 2 > /proc/sys/kernel/panic
# Setup bluetooth hci0 device
/usr/lib/init/boot/ubnt-bt.sh hci0
;;
stop)
# Tear down BT
hciconfig hci0 down
;;
*)
echo "Invalid command $1"
;;
esac

View file

@ -0,0 +1,29 @@
#!/bin/bash
# turn off fans for shutdown
if [ -d "/sys/class/hwmon/hwmon0/device" ]; then
for pwm in `ls /sys/class/hwmon/hwmon0/device/pwm[0-6]`; do
echo 0 > ${pwm}
done
fi
# Ensure all mounts in /srv from OMV are cleaned up
for dir in `ls /srv | grep dev-disk`; do
umount -q /srv/${dir};
done
# Now for all disks, delete from bus
for dsk in sd{a..z}; do
if [ -d "/sys/block/${dsk}/device" ]; then
echo 1 > "/sys/block/${dsk}/device/delete"
fi
done
# Now for all disks, remove them with ui-hdd-pwrctl-v2 if we have it
if [ -d "/sys/bus/platform/drivers/ui-hdd-pwrctl-v2" ]; then
for bay in `seq 0 7`; do
echo ${bay} > "$(realpath /sys/bus/platform/drivers/ui-hdd-pwrctl-v2/*hdd_pwrctl-v2)/hdd_force_poweroff"
done
fi
# Let the disks spool down a sec...
sleep 2

View file

@ -24,4 +24,10 @@ if [ ! -f ${root_path}/downloads/${kernel_filename} ]; then
wget ${kernel_src} -O ${root_path}/downloads/${kernel_filename}
fi
# Bluez
if [ ! -f ${root_path}/downloads/${bluez_filename} ]; then
debug_msg "Downloading Package Bluez..."
wget ${bluez_src} -O ${root_path}/downloads/${bluez_filename}
fi
debug_msg "Finished 02_download_dependencies.sh"

View file

@ -18,6 +18,10 @@ if [ ! -d ${build_path}/kernel ]; then
debug_msg "Docker: Building Kernel..."
docker run --ulimit nofile=1024 --rm -v "${root_path}:/repo:Z" -it ${docker_tag} /repo/scripts/docker/build_kernel.sh
fi
if [ ! -d ${build_path}/packages ]; then
debug_msg "Docker: Building Packages..."
docker run --ulimit nofile=1024 --rm -v "${root_path}:/repo:Z" -it ${docker_tag} /repo/scripts/docker/build_packages.sh
fi
debug_msg "Doing safety checks... please enter your password for sudo if prompted..."
# Before we do anything, make our dirs, and validate they are not mounted atm. If they are, exit!

View file

@ -32,7 +32,10 @@ apt-get -o Dpkg::Options::="--force-confold" -y --allow-downgrades \
bsdextrautils git binutils ca-certificates e2fsprogs haveged parted curl \
locales console-common openssh-server less vim net-tools wireguard-tools \
ntpsec u-boot-tools wget initramfs-tools python3-flask gnupg libc-ares2 \
dfu-util
dfu-util bluez
# Enable bluetooth
systemctl enable bluetooth
# Locale gen
locale-gen

View file

@ -39,7 +39,7 @@ fi
# Build as normal, with our extra version set to a timestamp
make ${kernel_config}
make -j`getconf _NPROCESSORS_ONLN` EXTRAVERSION=-alpine-unvr # Build kernel and modules
#make -j`getconf _NPROCESSORS_ONLN` EXTRAVERSION=-alpine-unvr Image.gz # makes gzip image
#make -j`getconf _NPROCESSORS_ONLN` EXTRAVERSION=-alpine-unvr Image.gz # makes gzip image (we should just do this ourselves, skip using make)
make INSTALL_MOD_PATH=./modules-dir -j`getconf _NPROCESSORS_ONLN` EXTRAVERSION=-alpine-unvr modules_install # installs modules to dir
#mkimage -A arm64 -O linux -T kernel -C gzip -a 04080000 -e 04080000 -n "Linux-UNVR-NAS-$(date +%Y%m%d-%H%M%S)" -d ./arch/arm64/boot/Image.gz uImage

View file

@ -0,0 +1,55 @@
#!/bin/bash
set -e
scripts_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )"
. ${scripts_path}/vars.sh
# Exports baby
export PATH=${build_path}/toolchain/${toolchain_bin_path}:${PATH}
export GCC_COLORS=auto
export CROSS_COMPILE=${toolchain_cross_compile}
export ARCH=arm64
# Make our temp builddir for bluez so we can make bccmd
bluez_builddir=$(mktemp -d)
tar -xzf ${root_path}/downloads/${bluez_filename} -C ${bluez_builddir}
# Start with Bluez
cd ${bluez_builddir}/${bluez_repopath}
# If we have patches, apply them
if [[ -d ${root_path}/patches/bluez/ ]]; then
for file in ${root_path}/patches/bluez/*.patch; do
echo "Applying bluez patch ${file}"
patch -p1 < ${file}
done
fi
# Build bccmd from bluez
./bootstrap
./configure --disable-systemd \
--enable-deprecated \
--disable-library \
--disable-cups \
--disable-datafiles \
--disable-manpages \
--disable-pie \
--disable-client \
--disable-obex \
--disable-udev \
--build=x86_64-linux-gnu \
--host=aarch64-none-linux-gnu \
--target=aarch64-none-linux-gnu
make lib/bluetooth/hci.h lib/bluetooth/bluetooth.h lib/libbluetooth-internal.la tools/bccmd -j`getconf _NPROCESSORS_ONLN`
# Save our binary
mkdir -p ${build_path}/packages/bluez
mv ./tools/bccmd ${build_path}/packages/bluez/
# Build ubnteeprom (our own tool)
mkdir -p ${build_path}/packages/ubnteeprom
env GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ${build_path}/packages/ubnteeprom/ubnteeprom ${root_path}/tools/ubnteeprom/main.go
# Cleanup
cd - > /dev/null
rm -rf ${bluez_builddir}

View file

@ -35,8 +35,8 @@ if [[ -d ${root_path}/overlay/${fs_overlay_dir}/ ]]; then
fi
# Apply our part UUIDs to fstab
sed -i "s|BOOTUUIDPLACEHOLDER|$(blkid -o value -s UUID ${build_path}/boot.ext4)|g" ${build_path}/rootfs/etc/fstab
sed -i "s|ROOTUUIDPLACEHOLDER|$(blkid -o value -s UUID ${build_path}/rootfs.ext4)|g" ${build_path}/rootfs/etc/fstab
#sed -i "s|BOOTUUIDPLACEHOLDER|$(blkid -o value -s UUID ${build_path}/boot.ext4)|g" ${build_path}/rootfs/etc/fstab
#sed -i "s|ROOTUUIDPLACEHOLDER|$(blkid -o value -s UUID ${build_path}/rootfs.ext4)|g" ${build_path}/rootfs/etc/fstab
# Hostname
echo "${distrib_name}" > ${build_path}/rootfs/etc/hostname
@ -56,6 +56,18 @@ for file in libgrpc++.so.1 libgrpc.so.10 libprotobuf.so.23 \
cp -H ${build_path}/fw-extract/rootfs/usr/lib/aarch64-linux-gnu/${file} "${build_path}/rootfs/usr/lib/ubnt-fw/"
done
# Copy over bluetooth firmware files
mkdir -p "${build_path}/rootfs/lib/firmware"
cp -R "${build_path}/fw-extract/rootfs/lib/firmware/csr8x11" "${build_path}/rootfs/lib/firmware/" # LCD panel firmwares
# Install our bccmd we compiled (less we use from unifi the better)
cp -R "${build_path}/packages/bluez/bccmd" "${build_path}/rootfs/usr/bin"
chmod +x "${build_path}/rootfs/usr/bin/bccmd"
# Install our ubnteeprom tool
cp -R "${build_path}/packages/ubnteeprom/ubnteeprom" "${build_path}/rootfs/usr/bin"
chmod +x "${build_path}/rootfs/usr/bin/ubnteeprom"
# Kick off bash setup script within chroot
cp ${docker_scripts_path}/bootstrap/001-bootstrap ${build_path}/rootfs/bootstrap
chroot ${build_path}/rootfs /bootstrap

View file

@ -27,6 +27,11 @@ genimage_src="https://github.com/pengutronix/genimage/releases/download/v16/geni
genimage_filename="$(basename ${genimage_src})"
genimage_repopath="${genimage_filename%.tar.xz}"
# bluez
bluez_src="https://github.com/bluez/bluez/archive/refs/tags/5.55.tar.gz"
bluez_filename="bluez-$(basename ${bluez_src})"
bluez_repopath="${bluez_filename%.tar.gz}"
# Distro
distrib_name="debian"
#deb_mirror="http://ftp.us.debian.org/debian"

343
tools/ubnteeprom/main.go Normal file
View file

@ -0,0 +1,343 @@
package main
import (
"encoding/hex"
"flag"
"fmt"
"net"
"os"
"strconv"
"strings"
)
// Type for EEPROM structure
type EEPROM_Value struct {
name, description, vtype string
offset, length int64
}
// Build our BOARD eeprom structure
var BOARD = []EEPROM_Value{
{
name: "format",
description: "EEPROM Format",
offset: 0x800C,
length: 0x2,
vtype: "hex",
},
{
name: "version",
description: "EEPROM Version",
offset: 0x800E,
length: 0x2,
vtype: "hex",
},
{
name: "boardid",
description: "Board Identifier (bomrev+model identifier)",
offset: 0x8012,
length: 0x2,
vtype: "hex",
},
{
name: "vendorid",
description: "Vendor Identifier",
offset: 0x8010,
length: 0x2,
vtype: "hex",
},
{
name: "bomrev",
description: "Bill of Materials Revision",
offset: 0x8014,
length: 0x4,
vtype: "hex",
},
{
name: "hwaddrbbase",
description: "Base Mac Address for Device",
offset: 0x8018,
length: 0x6,
vtype: "hexmac",
},
{
name: "EthMACAddrCount",
description: "Number of MAC addresses for ethernet interfaces",
offset: 0x801E,
length: 0x1,
vtype: "hexint",
},
{
name: "WiFiMACAddrCount",
description: "Number of MAC addresses for WiFi interfaces",
offset: 0x801F,
length: 0x1,
vtype: "hexint",
},
{
name: "BtMACAddrCount",
description: "Number of MAC addresses for Bluetooth interfaces",
offset: 0x8070,
length: 0x1,
vtype: "hexint",
},
{
name: "regdmn[0]",
description: "Region Domain 0",
offset: 0x8020,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[1]",
description: "Region Domain 1",
offset: 0x8022,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[2]",
description: "Region Domain 2",
offset: 0x8024,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[3]",
description: "Region Domain 3",
offset: 0x8026,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[4]",
description: "Region Domain 4",
offset: 0x8028,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[5]",
description: "Region Domain 5",
offset: 0x802A,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[6]",
description: "Region Domain 6",
offset: 0x802C,
length: 0x2,
vtype: "hex",
},
{
name: "regdmn[7]",
description: "Region Domain 7",
offset: 0x802E,
length: 0x2,
vtype: "hex",
},
}
// Build our SYSTEM_INFO eeprom structure
var SYSTEM_INFO = []EEPROM_Value{
// Missing values:
// cpu
// cpuid
// flashSize (this is static in unifi's kernel module -_-)
// ramsize
{
name: "vendorid",
description: "Vendor Identifier",
offset: 0x8010,
length: 0x2,
vtype: "hex",
},
{
name: "systemid",
description: "Device Model/Revision Identifier",
offset: 0xC,
length: 0x2,
vtype: "hex",
},
// shortname (we may wanna map boardid for this)
{
name: "boardrevision",
description: "Board Revision for the device",
offset: 0x13,
length: 0x1,
vtype: "hextobase10",
},
{
name: "serialno",
description: "Serial Number",
offset: 0x0,
length: 0x5,
vtype: "hex",
},
// manufid
// mfgweek
// qrid
// eth*.macaddr (generated mac's for all eth interfaces)
// device.hashid
// device.anonid
// bt0.macaddr (generated bt mac)
{
name: "regdmn[]",
description: "Region Domain",
offset: 0x8020,
length: 0x10,
vtype: "hex",
},
// cpu_rev_id
}
// Build our vars that are similar to running ubnt-tools id
var UBNT_TOOLS = []EEPROM_Value{
{
name: "board.sysid",
description: "Device Model/Revision Identifier",
offset: 0xC,
length: 0x2,
vtype: "hex",
},
{
name: "board.serialno",
description: "Serial Number",
offset: 0x0,
length: 0x5,
vtype: "hex",
},
{
name: "board.bom",
description: "Board Bill of Materials Revision Code",
offset: 0xD024,
length: 0xC,
vtype: "str",
},
}
func check(e error) {
if e != nil {
if strings.Contains(e.Error(), "encoding/hex: invalid byte:") {
fmt.Printf("Error: Unable to parse hex, please check your input.\n")
} else if strings.Contains(e.Error(), "EOF") {
fmt.Printf("Error: Unable to read contents from EEPROM/file.\n")
} else if strings.Contains(e.Error(), "encoding/hex: odd length hex string") {
fmt.Printf("Error: Incorrect length of input. Please try again.\n")
} else {
fmt.Printf("Fatal Error!!! ")
panic(e)
}
os.Exit(1)
}
}
func eeprom_read(vl []EEPROM_Value, f *os.File, key string) string {
var offs, leng int64
var vtype string
// Lookup our item
for _, v := range vl {
if v.name == key {
offs = v.offset
leng = v.length
vtype = v.vtype
break
}
}
// Make sure we found it
if (offs == 0) && (leng == 0) {
fmt.Printf("Error: Invalid key %s!\n", key)
os.Exit(1)
}
// Seek our file
_, err := f.Seek(offs, 0)
check(err)
// Make var and read into it
b2 := make([]byte, leng)
_, err = f.Read(b2)
check(err)
// Format as needed depending on type
switch vtype {
case "hexmac":
// Mac is stored in hex, but we need to format the return
macstr := net.HardwareAddr(b2[:]).String()
return macstr
case "hexint":
// Int is stored in hex
if b2[0]&0x0 == 0x0 {
// We start with 0, so strip
return strings.TrimPrefix(hex.EncodeToString(b2), `0`)
} else {
// We do not start with 0, so carry on
return hex.EncodeToString(b2)
}
case "hextobase10":
// Hex value needs to be moved to base 10
base_conversion, err := strconv.ParseInt(hex.EncodeToString(b2), 16, 64)
check(err)
return strconv.Itoa(int(base_conversion))
case "str":
// String value
return string(b2)
default:
// Default is to assume hex
return hex.EncodeToString(b2)
}
}
func return_values(values []EEPROM_Value, file string, filter string) {
// Open EEPROM for read only
f, err := os.Open(file)
defer f.Close()
check(err)
// If select is set, select our item, else return all
if len(filter) > 0 {
// Read value
fmt.Printf("%s\n", eeprom_read(values, f, filter))
} else {
// read all
for _, v := range values {
fmt.Printf("%s=%s\n", v.name, eeprom_read(values, f, v.name))
}
}
}
func main() {
// Start by defining our args
argfile := flag.String("file", "/dev/mtd4", "The path to the EEPROM for the device")
argboard := flag.Bool("board", false, "Print the board values")
argsystem := flag.Bool("systeminfo", false, "Print the system info values")
argtools := flag.Bool("tools", false, "Print similar values to ubnt-tools id")
argfilter := flag.String("key", "", "Used to select a specific EEPROM value")
flag.Parse()
// Verify we can read our eeprom file
if _, err := os.Stat(*argfile); os.IsNotExist(err) {
fmt.Printf("Error: Unable to access %s.\n", *argfile)
os.Exit(1)
}
// Is board set?
if *argboard {
return_values(BOARD, *argfile, *argfilter)
} else if *argsystem {
return_values(SYSTEM_INFO, *argfile, *argfilter)
} else if *argtools {
return_values(UBNT_TOOLS, *argfile, *argfilter)
} else {
// Tell user noting was submitted
fmt.Fprintf(os.Stderr, "Error Invalid usage of %s:\n", os.Args[0])
flag.PrintDefaults()
os.Exit(1)
}
// We be done
os.Exit(0)
}