diff --git a/.gitignore b/.gitignore index 6a8c1a3..934ef1a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ unifi-firmware/*.bin BuildEnv output -downloads \ No newline at end of file +downloads +tools/ubnteeprom/test-files \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5da41e5..e35a0de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 \ No newline at end of file + && 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 \ No newline at end of file diff --git a/overlay/filesystem/etc/systemd/system/ubnt-init.service b/overlay/filesystem/etc/systemd/system/ubnt-init.service index 198064b..bf5af10 100644 --- a/overlay/filesystem/etc/systemd/system/ubnt-init.service +++ b/overlay/filesystem/etc/systemd/system/ubnt-init.service @@ -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] diff --git a/overlay/filesystem/usr/bin/ubnt-tools b/overlay/filesystem/usr/bin/ubnt-tools index 9fe9e7d..e68972f 100755 --- a/overlay/filesystem/usr/bin/ubnt-tools +++ b/overlay/filesystem/usr/bin/ubnt-tools @@ -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 diff --git a/overlay/filesystem/usr/lib/init/boot/ubnt-bt.sh b/overlay/filesystem/usr/lib/init/boot/ubnt-bt.sh new file mode 100755 index 0000000..969303e --- /dev/null +++ b/overlay/filesystem/usr/lib/init/boot/ubnt-bt.sh @@ -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: 0x00 0x00 + 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 "$@" diff --git a/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh b/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh index 97385c4..032eade 100755 --- a/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh +++ b/overlay/filesystem/usr/lib/init/boot/ubnt-init.sh @@ -1,8 +1,22 @@ #!/bin/bash -# Load our kernel modules -/usr/sbin/modprobe ubnthal -/usr/sbin/modprobe btrfs +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 + # 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 diff --git a/overlay/filesystem/usr/lib/systemd/system-shutdown/unifi-shutdown b/overlay/filesystem/usr/lib/systemd/system-shutdown/unifi-shutdown new file mode 100755 index 0000000..e3d72e8 --- /dev/null +++ b/overlay/filesystem/usr/lib/systemd/system-shutdown/unifi-shutdown @@ -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 diff --git a/scripts/02_download_dependencies.sh b/scripts/02_download_dependencies.sh index 318c0d2..4ba6371 100755 --- a/scripts/02_download_dependencies.sh +++ b/scripts/02_download_dependencies.sh @@ -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" diff --git a/scripts/03_docker.sh b/scripts/03_docker.sh index 04890d1..87817c1 100755 --- a/scripts/03_docker.sh +++ b/scripts/03_docker.sh @@ -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! diff --git a/scripts/docker/bootstrap/001-bootstrap b/scripts/docker/bootstrap/001-bootstrap index 8ca095f..c09c876 100755 --- a/scripts/docker/bootstrap/001-bootstrap +++ b/scripts/docker/bootstrap/001-bootstrap @@ -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 diff --git a/scripts/docker/build_kernel.sh b/scripts/docker/build_kernel.sh index 582f24a..2dcec3c 100755 --- a/scripts/docker/build_kernel.sh +++ b/scripts/docker/build_kernel.sh @@ -37,9 +37,9 @@ fi # some modules. This is why some lines below are commented out. # Build as normal, with our extra version set to a timestamp -make ${kernel_config} +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 diff --git a/scripts/docker/build_packages.sh b/scripts/docker/build_packages.sh new file mode 100755 index 0000000..d5f781c --- /dev/null +++ b/scripts/docker/build_packages.sh @@ -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} diff --git a/scripts/docker/run_debootstrap.sh b/scripts/docker/run_debootstrap.sh index 025c65c..44cb467 100755 --- a/scripts/docker/run_debootstrap.sh +++ b/scripts/docker/run_debootstrap.sh @@ -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 diff --git a/scripts/vars.sh b/scripts/vars.sh index 1d224d5..216f25a 100755 --- a/scripts/vars.sh +++ b/scripts/vars.sh @@ -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" diff --git a/tools/ubnteeprom/main.go b/tools/ubnteeprom/main.go new file mode 100644 index 0000000..c7d7e32 --- /dev/null +++ b/tools/ubnteeprom/main.go @@ -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) +}