-
How to install armbian in h618?
Same board here (SuperBox S6 Pro = Transpeed 8K618-T, RTL8211F). I got the gigabit port working under Android (SlimBOX), write-up below. A few board findings may help Armbian device trees for this box too: the PI pin drive strength was level 0, working delays are tx 5 / rx 6 (with the RTL8211F's own TX delay off), and fd628 holds PI11/PI12. I may not check back often, so everything is in this post. [FIX] Gigabit Ethernet port working on SuperBox S6 Pro / H618 (Transpeed 8K618-T board, RTL8211F) with SlimBOX Android 12 Short version (for everyone)The problem: on the SuperBox S6 Pro (and other H618 boxes sold as "1000M Ethernet"), the network port looks alive (the light comes on and your router shows a gigabit link) but no data ever goes through. This happens on every firmware I could find. Why: the box has a real gigabit network chip (Realtek RTL8211F), but every firmware is copied from a cheaper 100M board and talks to the wrong chip. The driver also has three bugs that crash the box if you just switch the right chip on. The result: with the fix below, the built-in port runs at about 790 Mbit/s with zero errors, survives reboots, and gets its address from the router normally. You don't need a serial adapter or a new firmware. Who can do this: you need a rooted box (Magisk), adb over the network or USB, and a PC with Python. If "adb" and "dd" are new to you, ask someone who has done it before. It's easy to undo, though: the box can always be reflashed with PhoenixSuit, because burn mode is built into the chip and can't be erased. Important: the kernel part of the fix only fits one firmware build: SlimBOX sbx_transpeed_h618_atv_12_16 (kernel 5.4.125, file sbx_transpeed_h618_aosp_12_16.zip). The script checks this and refuses to change anything on a different build. Nothing gets written in that case. Step by stepWhat you need: the box with SlimBOX atv_12_16 flashed and rooted with Magisk, adb working (adb connect <box-ip>:5555 or USB), and a PC with python3 and device-tree-compiler (Linux: sudo apt install device-tree-compiler). 1. Back up the two partitions (keep these files safe, they're your undo): adb shell "su -c 'cat /dev/block/by-name/boot_a'" > boot_a.img adb shell "su -c 'cat /dev/block/by-name/vendor_boot_a'" > vendor_boot_a.img cp boot_a.img boot_a.ORIGINAL.img; cp vendor_boot_a.img vendor_boot_a.ORIGINAL.img(If your box runs from slot _b, check with adb shell getprop ro.boot.slot_suffix and use _b.) 2. Run the script (below) on your PC: python3 h618_gigabit_fix.py boot_a.img vendor_boot_a.imgIt prints each change and writes boot_a.patched.img and vendor_boot_a.patched.img. If it says STOP, your firmware is a different build. Don't continue. 3. Write them back and reboot: adb push boot_a.patched.img vendor_boot_a.patched.img /data/local/tmp/ adb shell "su -c 'dd if=/data/local/tmp/boot_a.patched.img of=/dev/block/by-name/boot_a bs=1M; dd if=/data/local/tmp/vendor_boot_a.patched.img of=/dev/block/by-name/vendor_boot_a bs=1M; sync; reboot'"4. Check it: plug a cable into the box's port. Settings → Network should show Ethernet connected. Or run adb shell ip addr show eth0. To undo: write the .ORIGINAL.img files back the same way. Or reflash SlimBOX with PhoenixSuit. Magisk note: if you later re-patch boot with Magisk, run the script on the new boot image again. It skips patches that are already in place. Optional: port on a network with no internet (for example a cable straight to a PC). Android only sends replies over its "default" network, and a network with no internet never becomes the default. The box then gets an address but answers nothing (ping/adb time out). This script copies the route into the main table every 5 seconds, so it also survives cable replugs. Change NET to your network. You don't need this if the box is plugged into a normal router with internet. #!/system/bin/sh # Magisk: put in /data/adb/service.d/eth-local.sh, chmod 755 NET=192.168.50 # first three numbers of your network ( while true; do a=$(ip -4 -o addr show eth0 2>/dev/null | grep -o "$NET\.[0-9]*" | head -1) if [ -n "$a" ]; then ip route replace $NET.0/24 dev eth0 src $a table main 2>/dev/null ip rule list | grep -q "from $a lookup main" || ip rule add from $a lookup main pref 97 ip rule list | grep -q "to $NET.0/24 lookup main" || ip rule add to $NET.0/24 lookup main pref 98 fi; sleep 5; done ) &What the fix changes (for the tinkerers)Six problems, found one by one. A box that hard-freezes with no log line was handled with the trick at the end of this section. Wrong port enabled. The device tree has eth@5020000 (gmac0, RGMII to the RTL8211F) disabled and eth@5030000 (gmac1, internal AC300 100M PHY) enabled. Swapped. Wrong pin function name. function = "gmac0" gives unsupported function gmac0 on pin PI0 / gmac pinctrl error! (-22). This kernel calls it emac0. Front display driver steals RGMII pins. fd628_dev claims PI11/PI12 (pin PI11 already requested ... cannot claim for 5020000.eth). Those are TXCK/TXCTL on this board. Set fd628_dev to status = "fail" (the front LED display goes dark). Kernel panic in the MAC code. geth_probe() → get_addr_by_name() sprintfs the address from addr_mgt into mac_str, which points at the string literal "00:00:00:00:00:00" in read-only kernel data. Result: Unable to handle kernel write to read-only memory, every time. Fix: NOP the call, and put mac_addr=02:.. on the vendor_boot boot line (__setup("mac_addr=") copies it in before rodata becomes read-only). PHY scan only accepts the AC300 and crashes on empty addresses. geth_phy_init() calls get_phy_device() on MDIO 0–31 with no IS_ERR check (→ virtual address 00000000000003fd) and only accepts phy_id 0x00441400. Fix: skip get_phy_device (libphy has already registered the RTL8211F at address 0) and accept the first real PHY. Link up, zero frames. The PI pin drive strength was level 0 (PI_DRV0 = 0; drive-strength = <10> maps to level 0 in this kernel). TX worked at once with level 3 (<40>). The RX delay of 31 was also outside the window. Live sweep: with RTL8211F TX delay off (the driver default), EMAC tx-delay 3–7 and rx-delay 0–12 work. Chosen: tx 5, rx 6. Kernel patches (VA, _text = 0xffffffc010080000, file offset = VA − _text + 4096 in boot.img): VA original patched 0xffffffc010786a94 bl get_addr_by_name 94d2fe97 nop 1f2003d5 0xffffffc010788f70 bl get_phy_device cbc4ff97 mov x0,xzr e0031faa 0xffffffc010788f30 b.eq a0030054 b 1d000014 The no-serial-adapter debugging trick: this kernel has CONFIG_PSTORE_RAM=y, but the device tree has no ramoops region. Add one to reserved-memory (ramoops@13ab00000, 1 MB) and start the hardware watchdog before each test (busybox watchdog -t 2 -T 16 /dev/watchdog). Every hard freeze then warm-resets after 16 s, and the full panic is waiting in /sys/fs/pstore/console-ramoops-0. Also useful: /sys/class/sunxi_dump (read/write physical registers as root) and the driver's mii_read / mii_write sysfs files (raw PHY registers). Together those let you sweep RGMII delays live with no reboots. The script leaves the ramoops region in (use --no-ramoops to skip it). Hardware: H618, 4 GB, RTL8211F (25 MHz crystal) + TFS5009 magnetics, Wi-Fi module Cdtech 20AI880-20 (AIC8800, not working yet: aic8800_bsp loads, no wlan0). UART header B/VCC/R/GND at 1500000 baud (not needed). Not fixed: Wi-Fi. Also, a proper fix would be a rebuilt sunxi-gmac.c (IS_ERR check, no AC300-only ID check, writable mac_str). These binary patches only fit this build. On another build, find get_addr_by_name, geth_probe and geth_phy_init in /proc/kallsyms, disassemble, and look for the same three spots. The script (h618_gigabit_fix.py)#!/usr/bin/env python3 """ h618_gigabit_fix.py - enable the RTL8211F gigabit Ethernet port on H618 TV boxes (SuperBox S6 Pro / Transpeed 8K618-T board) running SlimBOX Android 12 build "sbx_transpeed_h618_atv_12_16" (from sbx_transpeed_h618_aosp_12_16.zip) (kernel 5.4.125). It patches two partition images that you pulled off the box: boot_a.img -> 3 four-byte kernel patches (only if the exact original bytes are found) vendor_boot_a.img -> device tree + boot line changes It REFUSES to patch anything that does not match the known build. Needs: python3, and dtc/fdtget/fdtput (package "device-tree-compiler"). Usage: python3 h618_gigabit_fix.py boot_a.img vendor_boot_a.img [--mac 02:xx:xx:xx:xx:xx] [--no-ramoops] Writes boot_a.patched.img and vendor_boot_a.patched.img next to the originals. """ import argparse, hashlib, os, random, struct, subprocess, sys, tempfile TEXT = 0xffffffc010080000 # kernel _text for this build KOFF = 4096 # kernel Image offset inside boot v3 image KPATCH = [ # (virtual address, original bytes, new bytes, what) (0xffffffc010786a94, "94d2fe97", "1f2003d5", "nop: skip get_addr_by_name (writes into read-only memory)"), (0xffffffc010788f70, "cbc4ff97", "e0031faa", "mov x0,xzr: skip get_phy_device (unchecked error pointer)"), (0xffffffc010788f30, "a0030054", "1d000014", "b: accept the RTL8211F, not only the AC300 100M PHY"), ] ETH = "/soc@3000000/eth@5020000" ETH1 = "/soc@3000000/eth@5030000" PIN = "/soc@3000000/pinctrl@300b000/gmac0@0" FD628 = "/soc@3000000/fd628_dev" RAMOOPS = "/reserved-memory/ramoops@13ab00000" DTB_OFF = 6144 # dtb offset in this vendor_boot (header v3, page 2048) def die(msg): sys.exit("STOP: " + msg) def run(*a): r = subprocess.run(a, capture_output=True, text=True) if r.returncode: die(f"{' '.join(a)} failed: {r.stderr.strip()}") return r.stdout.strip() def patch_boot(path): b = bytearray(open(path, "rb").read()) if b[:8] != b"ANDROID!" or struct.unpack("<I", b[40:44])[0] != 3: die(f"{path} is not an Android boot image v3") if b[KOFF + 56:KOFF + 60] != b"ARMd": die("no arm64 kernel Image at offset 4096") for va, old, new, what in KPATCH: o = KOFF + va - TEXT cur = b[o:o + 4].hex() if cur == new: print(f" already patched: {what}") continue if cur != old: die(f"kernel bytes at {va:#x} are {cur}, expected {old}. " "This is a different firmware build - the offsets do not apply. Nothing was written.") b[o:o + 4] = bytes.fromhex(new) print(f" patched: {what}") return b def patch_vendor_boot(path, mac, ramoops): v = bytearray(open(path, "rb").read()) if v[:8] != b"VNDRBOOT" or struct.unpack("<I", v[8:12])[0] != 3: die(f"{path} is not a vendor_boot v3 image") page = struct.unpack("<I", v[12:16])[0] dsize = struct.unpack("<I", v[2100:2104])[0] if v[DTB_OFF:DTB_OFF + 4] != b"\xd0\x0d\xfe\xed": die("device tree not found at offset 6144") end_pad = (DTB_OFF + dsize + page - 1) // page * page with tempfile.TemporaryDirectory() as t: d = os.path.join(t, "vb.dtb") open(d, "wb").write(v[DTB_OFF:DTB_OFF + dsize]) comp = run("fdtget", d, ETH, "compatible") if not comp.startswith("allwinner,sunxi-gma"): die(f"unexpected gmac0 node: {comp}") # gigabit port on, 100M port off run("fdtput", "-t", "s", d, ETH, "compatible", "allwinner,sunxi-gmac") run("fdtput", "-t", "s", d, ETH, "status", "okay") run("fdtput", "-t", "s", d, ETH1, "status", "disabled") run("fdtput", "-t", "i", d, ETH, "use_ephy25m", "0") run("fdtput", "-t", "x", d, ETH, "tx-delay", "5") run("fdtput", "-t", "x", d, ETH, "rx-delay", "6") for p in ("phy-rst", "gmac-power0", "gmac-power1", "gmac-power2"): subprocess.run(["fdtput", "-d", d, ETH, p], capture_output=True) # pins: right function name + full drive strength (10 mA = level 0 in this kernel) run("fdtput", "-t", "s", d, PIN, "function", "emac0") run("fdtput", "-t", "x", d, PIN, "drive-strength", "0x28") # front display driver holds PI11/PI12 (RGMII TXCK/TXCTL) run("fdtput", "-t", "s", d, FD628, "status", "fail") if ramoops: subprocess.run(["fdtput", "-c", d, RAMOOPS], capture_output=True) run("fdtput", "-t", "s", d, RAMOOPS, "compatible", "ramoops") run("fdtput", "-t", "x", d, RAMOOPS, "reg", "1", "0x3ab00000", "0", "0x100000") run("fdtput", "-t", "x", d, RAMOOPS, "console-size", "0x80000") run("fdtput", "-t", "x", d, RAMOOPS, "record-size", "0x20000") run("fdtput", "-t", "x", d, RAMOOPS, "pmsg-size", "0x0") run("fdtput", "-t", "x", d, RAMOOPS, "ecc-size", "0") p = os.path.join(t, "vb2.dtb") run("dtc", "-q", "-I", "dtb", "-O", "dtb", "-o", p, d) nd = open(p, "rb").read() if DTB_OFF + len(nd) > end_pad: die("new device tree does not fit in the padding") v[DTB_OFF:end_pad] = nd + b"\0" * (end_pad - DTB_OFF - len(nd)) v[2100:2104] = struct.pack("<I", len(nd)) # boot line: fixed MAC address (driver needs one, u-boot passes an empty one) cl = bytes(v[28:28 + 2048]).rstrip(b"\0") if b"mac_addr=" not in cl: cl += b" mac_addr=" + mac.encode() if len(cl) >= 2048: die("boot line too long") v[28:28 + 2048] = cl.ljust(2048, b"\0") return v def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("boot"); ap.add_argument("vendor_boot") ap.add_argument("--mac", help="MAC to use (default: random locally administered 02:..)") ap.add_argument("--no-ramoops", action="store_true", help="don't add the crash-log memory region") a = ap.parse_args() mac = a.mac or "02:" + ":".join(f"{random.randint(0, 255):02x}" for _ in range(5)) if len(mac) != 17: die("MAC must look like 02:11:22:33:44:55") print("boot image:") b = patch_boot(a.boot) print("vendor_boot image:") v = patch_vendor_boot(a.vendor_boot, mac, not a.no_ramoops) ob = a.boot.rsplit(".img", 1)[0] + ".patched.img" ov = a.vendor_boot.rsplit(".img", 1)[0] + ".patched.img" open(ob, "wb").write(b); open(ov, "wb").write(v) print(f"\nMAC address: {mac}") for f in (ob, ov): print(f"wrote {f} md5 {hashlib.md5(open(f, 'rb').read()).hexdigest()}") if __name__ == "__main__": main()Tested: run on the original SlimBOX atv_12_16 images, the output kernel is byte-identical to the one running on my box. The device tree matches too, apart from two status strings written the standard way. A box with this fix has been running at ~790 Mbit/s with zero errors through reboots. Questions welcome. If you have a different H618 box with an RTL8211F, the same six problems are probably there.
Echo_Tinker
Validating
-
Joined
-
Last visited