#!/bin/bash
# NRC7292 HaLow driver setup for Raspberry Pi 5, kernel 6.12
# Usage: bash nrc7292_setup.sh
#
# Applies all patches needed to build nrc7292_sw_pkg v1.5.2 on kernel 6.12,
# installs firmware, device tree overlay, and fixes start.py paths.

set -e
BOLD='\033[1m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
log()  { echo -e "\n${BOLD}=== $1 ===${NC}"; }
ok()   { echo -e "${GREEN}  OK${NC}: $1"; }
skip() { echo -e "  SKIP: $1"; }
warn() { echo -e "${YELLOW}  WARN${NC}: $1"; }
die()  { echo -e "${RED}  FAIL${NC}: $1"; exit 1; }

PKGDIR="$HOME/nrc7292_sw_pkg"
SRCDIR="$PKGDIR/package/src/nrc"
NRCPKG="$PKGDIR/package/evk/sw_pkg/nrc_pkg"
SCRIPTDIR="$NRCPKG/script"
FWDIR="$NRCPKG/sw/firmware"

# ─────────────────────────────────────────────────────────────────
log "Step 1/11: Enable persistent journal"
# Storage=auto (default) only persists if /var/log/journal exists at journald startup.
# Hard crashes (kernel oops/hang) can leave that directory unflushed on the SD card.
# Storage=persistent forces on-disk logging unconditionally, survives hard reboots.
# SyncIntervalSec=5s: flush to disk every 5s instead of 5min so crash logs aren't lost.
sudo mkdir -p /var/log/journal
sudo chown root:systemd-journal /var/log/journal
sudo chmod 2755 /var/log/journal
sudo sed -i '/^\[Journal\]/,/^\[/ s/^#\?Storage=.*/Storage=persistent/' /etc/systemd/journald.conf
grep -q '^Storage=persistent' /etc/systemd/journald.conf || \
    sudo sed -i '/^\[Journal\]/a Storage=persistent' /etc/systemd/journald.conf
sudo sed -i '/^\[Journal\]/,/^\[/ s/^#\?SyncIntervalSec=.*/SyncIntervalSec=5s/' /etc/systemd/journald.conf
grep -q '^SyncIntervalSec=' /etc/systemd/journald.conf || \
    sudo sed -i '/^\[Journal\]/a SyncIntervalSec=5s' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald
ok "Persistent journal: Storage=persistent, SyncIntervalSec=5s"

# ─────────────────────────────────────────────────────────────────
log "Step 2/11: Install build dependencies"
sudo apt-get update -qq
sudo apt-get install -y git build-essential raspberrypi-kernel-headers device-tree-compiler
ok "Dependencies installed"

# ─────────────────────────────────────────────────────────────────
log "Step 3/11: Clone NRC7292 package"
if [ ! -d "$PKGDIR" ]; then
    git clone https://github.com/newracom/nrc7292_sw_pkg.git "$PKGDIR"
    ok "Cloned to $PKGDIR"
else
    skip "$PKGDIR already exists"
fi

# Reset source to clean state before patching (safe to re-run)
cd "$PKGDIR" && git checkout package/src/nrc/
cd "$SRCDIR"

# ─────────────────────────────────────────────────────────────────
log "Step 4/11: Apply kernel 6.12 patches"

# ── nrc-build-config.h ──────────────────────────────────────────
# Disable WDS (removed in kernel 6.12)
if grep -q '^#define CONFIG_WIRELESS_WDS$' nrc-build-config.h; then
    sed -i 's/^#define CONFIG_WIRELESS_WDS$/\/\/ #define CONFIG_WIRELESS_WDS/' nrc-build-config.h
    ok "nrc-build-config.h: disabled CONFIG_WIRELESS_WDS"
else
    skip "nrc-build-config.h: CONFIG_WIRELESS_WDS"
fi

# Fix CONFIG_USE_CHANNEL_CONTEXT — was being disabled by WDS || condition
if grep -q 'defined(CONFIG_USE_CHANNEL_CONTEXT)' nrc-build-config.h; then
    sed -i 's/#if defined(CONFIG_WIRELESS_WDS) || defined(CONFIG_USE_CHANNEL_CONTEXT)/#if defined(CONFIG_WIRELESS_WDS)/' nrc-build-config.h
    ok "nrc-build-config.h: fixed CONFIG_USE_CHANNEL_CONTEXT condition"
else
    skip "nrc-build-config.h: channel context condition"
fi

# ── nrc-hif-cspi.c ──────────────────────────────────────────────
# asm/unaligned.h moved to linux/unaligned.h in kernel 6.12
if grep -q '<asm/unaligned.h>' nrc-hif-cspi.c; then
    sed -i 's|#include <asm/unaligned.h>|#include <linux/unaligned.h>|' nrc-hif-cspi.c
    ok "nrc-hif-cspi.c: fixed unaligned.h path"
else
    skip "nrc-hif-cspi.c: unaligned.h"
fi

# nrc_mac_stop call needs bool offchannel param
if grep -q 'nrc_mac_stop(nw->hw)' nrc-hif-cspi.c; then
    sed -i 's/nrc_mac_stop(nw->hw)/nrc_mac_stop(nw->hw, false)/' nrc-hif-cspi.c
    ok "nrc-hif-cspi.c: fixed nrc_mac_stop call"
else
    skip "nrc-hif-cspi.c: nrc_mac_stop call"
fi

# Static functions
for item in "void:spi_wakeup" "int:nrc_cspi_gpio_alloc" "void:nrc_cspi_gpio_free"; do
    rt="${item%%:*}"; fn="${item##*:}"
    if grep -q "^${rt} ${fn}(" nrc-hif-cspi.c; then
        sed -i "s/^${rt} ${fn}(/static ${rt} ${fn}(/" nrc-hif-cspi.c
        ok "nrc-hif-cspi.c: ${fn} -> static"
    fi
done

# ── hif.c ────────────────────────────────────────────────────────
python3 - << 'PYEOF'
import re

with open('hif.c', 'r') as f:
    content = f.read()

# Functions to make static (any return type, optional space before paren)
static_fns   = ['is_mgmt', 'is_urgent_frame', 'nrc_xmit_wim']
# Functions to make static __maybe_unused
unused_fns   = ['is_tcp_ack', 'nrc_hif_reset_rx']

for fn in static_fns:
    pattern = re.compile(r'^(?!static\s)(\w[\w\s\*]*\s)(' + fn + r'\s*\()', re.MULTILINE)
    new, n = pattern.subn(r'static \1\2', content)
    if n:
        content = new
        print(f'  OK: hif.c: {fn} -> static')

for fn in unused_fns:
    pattern = re.compile(r'^(?!static\s)(\w[\w\s\*]*\s)(' + fn + r'\s*\()', re.MULTILINE)
    new, n = pattern.subn(r'static __maybe_unused \1\2', content)
    if n:
        content = new
        print(f'  OK: hif.c: {fn} -> static __maybe_unused')
    else:
        # Already has static but maybe missing __maybe_unused
        pattern2 = re.compile(r'^static\s+(?!__maybe_unused)(\w[\w\s\*]*\s)(' + fn + r'\s*\()', re.MULTILINE)
        new, n = pattern2.subn(r'static __maybe_unused \1\2', content)
        if n:
            content = new
            print(f'  OK: hif.c: {fn} -> added __maybe_unused')

with open('hif.c', 'w') as f:
    f.write(content)
PYEOF

# ── nrc-mac80211.h ───────────────────────────────────────────────
if grep -q 'void nrc_mac_stop(struct ieee80211_hw \*hw);' nrc-mac80211.h; then
    sed -i 's/void nrc_mac_stop(struct ieee80211_hw \*hw);/void nrc_mac_stop(struct ieee80211_hw *hw, bool offchannel);/' nrc-mac80211.h
    ok "nrc-mac80211.h: updated nrc_mac_stop declaration"
else
    skip "nrc-mac80211.h: nrc_mac_stop declaration"
fi

# ── nrc-mac80211.c ───────────────────────────────────────────────
# stop() callback now requires bool offchannel param
if grep -q '^void nrc_mac_stop(struct ieee80211_hw \*hw)$' nrc-mac80211.c; then
    sed -i 's/^void nrc_mac_stop(struct ieee80211_hw \*hw)$/void nrc_mac_stop(struct ieee80211_hw *hw, bool offchannel)/' nrc-mac80211.c
    ok "nrc-mac80211.c: updated nrc_mac_stop signature"
else
    skip "nrc-mac80211.c: nrc_mac_stop signature"
fi

# .tx is at offset 0 of ieee80211_ops; mac80211 6.12.47 WARN_ONs and returns NULL if absent.
# Add it if missing — a previous .txt-based installer run may have removed it.
# (6.12.75+ forbids .tx when wake_tx_queue is set; this installer targets 6.12.47 only.)
if grep -q '\.tx = nrc_mac_tx' nrc-mac80211.c; then
    skip "nrc-mac80211.c: .tx already present"
else
    sed -i 's/static const struct ieee80211_ops nrc_mac80211_ops = {/static const struct ieee80211_ops nrc_mac80211_ops = {\n\t.tx = nrc_mac_tx,/' nrc-mac80211.c
    ok "nrc-mac80211.c: added .tx = nrc_mac_tx (required at offset 0 in 6.12.47)"
fi

# chanctx callbacks need link_conf param in 6.12
if grep -q 'nrc_post_channel_switch(struct ieee80211_hw \*hw, struct ieee80211_vif \*vif)' nrc-mac80211.c; then
    sed -i 's/nrc_post_channel_switch(struct ieee80211_hw \*hw, struct ieee80211_vif \*vif)/nrc_post_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf)/' nrc-mac80211.c
    ok "nrc-mac80211.c: fixed nrc_post_channel_switch signature"
else
    skip "nrc-mac80211.c: nrc_post_channel_switch"
fi

if grep -q 'nrc_mac_assign_vif_chanctx(struct ieee80211_hw \*hw, struct ieee80211_vif \*vif, struct ieee80211_chanctx_conf \*ctx)' nrc-mac80211.c; then
    sed -i 's/nrc_mac_assign_vif_chanctx(struct ieee80211_hw \*hw, struct ieee80211_vif \*vif, struct ieee80211_chanctx_conf \*ctx)/nrc_mac_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx)/' nrc-mac80211.c
    ok "nrc-mac80211.c: fixed assign_vif_chanctx signature"
else
    skip "nrc-mac80211.c: assign_vif_chanctx"
fi

if grep -q 'nrc_mac_unassign_vif_chanctx(struct ieee80211_hw \*hw, struct ieee80211_vif \*vif, struct ieee80211_chanctx_conf \*ctx)' nrc-mac80211.c; then
    sed -i 's/nrc_mac_unassign_vif_chanctx(struct ieee80211_hw \*hw, struct ieee80211_vif \*vif, struct ieee80211_chanctx_conf \*ctx)/nrc_mac_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx)/' nrc-mac80211.c
    ok "nrc-mac80211.c: fixed unassign_vif_chanctx signature"
else
    skip "nrc-mac80211.c: unassign_vif_chanctx"
fi

# Static functions — use actual return types from source
python3 - << 'PYEOF'
import re

with open('nrc-mac80211.c', 'r') as f:
    content = f.read()

fns = [
    # (search_pattern, replacement_prefix)
    (r'^unsigned int nrc_ac_credit\(',   'static unsigned int nrc_ac_credit('),
    (r'^const char \*iftype_string\(',   'static const char *iftype_string('),
    (r'^void nrc_mac_set_wakeup\(',      'static void nrc_mac_set_wakeup('),
    (r'^int nrc_mac_resume\(',           'static int nrc_mac_resume('),
    (r'^int nrc_mac_suspend\(',          'static int nrc_mac_suspend('),
    (r'^void remotecmd_callback\(',      'static void remotecmd_callback('),
    (r'^void remotecmd_schedule_off\(',  'static void remotecmd_schedule_off('),
    (r'^void scan_complete\(',           'static void scan_complete('),
]

changed = []
for pattern, replacement_start in fns:
    new, count = re.subn(pattern, replacement_start, content, flags=re.MULTILINE)
    if count:
        content = new
        fn = replacement_start.split('(')[0].split()[-1]
        changed.append(fn)

with open('nrc-mac80211.c', 'w') as f:
    f.write(content)

for fn in changed:
    print(f'  OK: nrc-mac80211.c: {fn} -> static')
PYEOF

# chanctx callbacks: line-by-line approach — add link_conf param before chanctx_conf arg
python3 - << 'PYEOF'
target_fns = ['nrc_mac_assign_vif_chanctx', 'nrc_mac_unassign_vif_chanctx']

with open('nrc-mac80211.c', 'r') as f:
    lines = f.readlines()

# Check if already patched
already = any('link_conf' in l for l in lines
              if any(fn in l for fn in target_fns))

in_fn = None
new_lines = []
patched = []

for line in lines:
    # Detect entry into a target function definition (has 'static' and the fn name)
    for fn in target_fns:
        if fn in line and ('static int' in line or 'static void' in line):
            in_fn = fn
            break

    # Inside a target function signature: find the chanctx_conf line and prepend link_conf
    if in_fn and 'ieee80211_chanctx_conf' in line and 'link_conf' not in line:
        indent = line[:len(line) - len(line.lstrip())]
        new_lines.append(indent + 'struct ieee80211_bss_conf *link_conf,\n')
        patched.append(in_fn)
        in_fn = None

    new_lines.append(line)

    # Once we hit the opening brace the signature is done
    if in_fn and line.strip() == '{':
        in_fn = None

if patched:
    with open('nrc-mac80211.c', 'w') as f:
        f.writelines(new_lines)
    for fn in patched:
        print(f'  OK: nrc-mac80211.c: {fn} -> added link_conf param')
elif already:
    print('  SKIP: nrc-mac80211.c: chanctx link_conf already present')
else:
    print('  WARN: nrc-mac80211.c: chanctx functions not found — check source manually')
PYEOF

# g_bd_valid fix: set to true after successful board data load
# Without this, nrc_mac_start returns -1 and interface never comes up
python3 - << 'PYEOF'
with open('nrc-mac80211.c', 'r') as f:
    content = f.read()

if 'g_bd_valid = true' in content:
    print('  SKIP: nrc-mac80211.c: g_bd_valid already set')
else:
    # Try known success message string
    needle = 'succeed in loading board data on target'
    if needle in content:
        content = content.replace(
            f'nrc_dbg(NRC_DBG_STATE,"{needle}");',
            f'nrc_dbg(NRC_DBG_STATE,"{needle}");\n\t\t\tg_bd_valid = true;'
        )
        print('  OK: nrc-mac80211.c: g_bd_valid = true added in success path')
    else:
        # Fallback: find g_bd_valid = false and add true assignment nearby
        import re
        # Set g_bd_valid=true right before nrc_mac_start checks it
        # Alternative: initialize to true at declaration
        content, n = re.subn(
            r'(static\s+bool\s+g_bd_valid\s*=\s*)false',
            r'\1true',
            content
        )
        if n:
            print('  OK: nrc-mac80211.c: g_bd_valid initialized to true (bypass check)')
        else:
            print('  WARN: nrc-mac80211.c: g_bd_valid not found — interface may not come up')
    with open('nrc-mac80211.c', 'w') as f:
        f.write(content)
PYEOF

# ── nrc-mac80211.c: nrc_mac_config NULL guard ─────────────────────
# ieee80211_do_open calls drv_config (nrc_mac_config) during interface bring-up
# via ieee80211_hw_conf_init. At that point hw->conf.chandef.chan is NULL because
# no channel has been configured yet. The function unconditionally does
# memcpy(&ch, hw->conf.chandef.chan, ...) which dereferences NULL → kernel oops,
# ip segfault, system hangs on subsequent iw dev (RTNL lock held by dead process).
# Fix: return 0 (no-op) early if chan is NULL.
python3 - << 'PYEOF'
with open('nrc-mac80211.c', 'r') as f:
    lines = f.readlines()

if any('chandef.chan is NULL during ieee80211_hw_conf_init' in l for l in lines):
    print('  SKIP: nrc-mac80211.c: chandef.chan NULL guard already present')
else:
    insert_after = None
    func_line = None
    for i, line in enumerate(lines):
        if 'static int nrc_mac_config(' in line:
            func_line = i
        if func_line is not None and i > func_line and i < func_line + 30:
            if '#endif /* defined(CONFIG_SUPPORT_BD) */' in line:
                insert_after = i
                break
    if insert_after is None:
        print('  WARN: nrc-mac80211.c: nrc_mac_config NULL guard patch point not found')
    else:
        guard = [
            '\t/* chandef.chan is NULL during ieee80211_hw_conf_init on first ip link set up.\n',
            '\t * The memcpy below dereferences it unconditionally -- return 0 until valid. */\n',
            '\tif (!hw->conf.chandef.chan)\n',
            '\t\treturn 0;\n',
        ]
        lines = lines[:insert_after + 1] + guard + lines[insert_after + 1:]
        with open('nrc-mac80211.c', 'w') as f:
            f.writelines(lines)
        print(f'  OK: nrc-mac80211.c: added chandef.chan NULL guard to nrc_mac_config')
PYEOF

# ── nrc-trx.c ────────────────────────────────────────────────────
python3 - << 'PYEOF'
import re

with open('nrc-trx.c', 'r') as f:
    content = f.read()

# Match any non-static function definition at start of line (any return type)
fns = ['nrc_is_valid_vif', 'ieee80211_is_data_data', 'insert_qos_ctrl_field_in_skb']
for fn in fns:
    pattern = re.compile(r'^(?!static\s)(\w[\w\s\*]*\s)(' + fn + r'\s*\()', re.MULTILINE)
    new, count = pattern.subn(r'static \1\2', content)
    if count:
        content = new
        print(f'  OK: nrc-trx.c: {fn} -> static')
    else:
        if 'static' in content and fn in content:
            print(f'  SKIP: nrc-trx.c: {fn} already static')
        elif fn not in content:
            print(f'  SKIP: nrc-trx.c: {fn} not found')

with open('nrc-trx.c', 'w') as f:
    f.write(content)
PYEOF

# ── nrc-init.c ───────────────────────────────────────────────────
python3 - << 'PYEOF'
import re
with open('nrc-init.c', 'r') as f:
    content = f.read()
pattern = re.compile(r'^(?!static\s)(\w[\w\s\*]*\s)(nrc_fw_start\s*\()', re.MULTILINE)
new, n = pattern.subn(r'static \1\2', content)
if n:
    with open('nrc-init.c', 'w') as f:
        f.write(new)
    print('  OK: nrc-init.c: nrc_fw_start -> static')
else:
    print('  SKIP: nrc-init.c: nrc_fw_start')
PYEOF

# ── wim.c ─────────────────────────────────────────────────────────
python3 - << 'PYEOF'
import re

with open('wim.c', 'r') as f:
    content = f.read()

fns = ['nrc_wim_set_rc_mode', 'nrc_wim_set_default_mcs',
       'nrc_wim_handle_fw_ready', 'nrc_wim_handle_fw_reload',
       'nrc_wim_handle_req_deauth']

for fn in fns:
    pattern = re.compile(r'^(?!static\s)(\w[\w\s\*]*\s)(' + fn + r'\s*\()', re.MULTILINE)
    new, n = pattern.subn(r'static \1\2', content)
    if n:
        content = new
        print(f'  OK: wim.c: {fn} -> static')

with open('wim.c', 'w') as f:
    f.write(content)
PYEOF

# ieee80211_csa_finish / ieee80211_chswitch_done gained a link_id param in 6.12
if grep -q 'ieee80211_csa_finish(vif)' wim.c; then
    sed -i 's/ieee80211_csa_finish(vif)/ieee80211_csa_finish(vif, 0)/' wim.c
    ok "wim.c: fixed ieee80211_csa_finish"
else
    skip "wim.c: ieee80211_csa_finish"
fi
if grep -q 'ieee80211_chswitch_done(vif, true)' wim.c; then
    sed -i 's/ieee80211_chswitch_done(vif, true)/ieee80211_chswitch_done(vif, true, 0)/' wim.c
    ok "wim.c: fixed ieee80211_chswitch_done"
else
    skip "wim.c: ieee80211_chswitch_done"
fi

# ── nrc-fw.c / nrc-dump.c / nrc-bd.c ─────────────────────────────
python3 - << 'PYEOF'
import re

def make_static(filename, fns, maybe_unused=False):
    with open(filename, 'r') as f:
        content = f.read()
    for fn in fns:
        prefix = 'static __maybe_unused ' if maybe_unused else 'static '
        pattern = re.compile(r'^(?!static\s)(\w[\w\s\*]*\s)(' + fn + r'\s*\()', re.MULTILINE)
        new, n = pattern.subn(prefix + r'\1\2', content)
        if n:
            content = new
            print(f'  OK: {filename}: {fn} -> {prefix.strip()}')
    with open(filename, 'w') as f:
        f.write(content)

make_static('nrc-fw.c',   ['nrc_fw_update_frag'])
make_static('nrc-dump.c', ['nrc_dump_init'], maybe_unused=True)
make_static('nrc-bd.c',   ['nrc_get_non_s1g_freq', 'nrc_set_supp_ch_list'])
PYEOF

# ── nrc-hif.h ─────────────────────────────────────────────────────
# nrc_hif_cspi_read_credit is used by nrc-debug.c via extern — needs a proper prototype
# Remove any wrong prototype we may have added in a previous run, then add correct one
sed -i '/nrc_hif_cspi_read_credit/d' nrc-hif.h
sed -i '/^#endif/i void nrc_hif_cspi_read_credit(struct nrc_hif_device *hdev, int q, int *p_front, int *p_rear, int *p_credit);' nrc-hif.h
ok "nrc-hif.h: added nrc_hif_cspi_read_credit prototype (void, 5 params)"

# ── nrc-netlink.c ─────────────────────────────────────────────────
# genl_ops -> genl_split_ops in kernel 6.1+ for pre_doit/post_doit
python3 - << 'PYEOF'
import re, sys

with open('nrc-netlink.c', 'r') as f:
    content = f.read()

if 'genl_split_ops' in content:
    print('  SKIP: nrc-netlink.c genl_split_ops')
    sys.exit(0)

changed = False
for fn in ['nrc_nl_pre_doit', 'nrc_nl_post_doit']:
    pattern = re.compile(
        r'(static\s+\S+\s+' + fn + r'\s*\()(const struct genl_ops \*)',
        re.MULTILINE
    )
    if pattern.search(content):
        content = pattern.sub(r'\1const struct genl_split_ops *', content)
        print(f'  OK: nrc-netlink.c: {fn} -> genl_split_ops')
        changed = True
    else:
        print(f'  SKIP: nrc-netlink.c: {fn} (not found or already patched)')

if changed:
    with open('nrc-netlink.c', 'w') as f:
        f.write(content)
PYEOF

# ─────────────────────────────────────────────────────────────────
log "Step 5/11: Build module"
make clean && make || die "Build failed — paste errors for diagnosis"
ok "Build successful"

# ─────────────────────────────────────────────────────────────────
log "Step 6/11: Install module"
sudo make modules_install
sudo depmod -a
ok "Module installed ($(uname -r))"

# ─────────────────────────────────────────────────────────────────
log "Step 7/11: Device tree overlay (SPI0, CS0, 20MHz)"
# compatible = "nrc80211" is required — the nrc driver's spi_device_id table uses NRC_DRIVER_NAME
# which is "nrc80211". A DT node with compatible = "newracom,nrc7292" generates modalias
# spi:nrc7292 and never binds to the nrc80211 driver.
# NOTE: adjust spi-max-frequency and GPIO pins if your board wiring differs
cat > /tmp/nrc-rpi.dts << 'DTSEOF'
/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2712";

    fragment@0 {
        target = <&spi0>;
        __overlay__ {
            #address-cells = <1>;
            #size-cells = <0>;
            status = "okay";

            nrc7292: nrc7292@0 {
                compatible = "nrc80211";
                reg = <0>;
                spi-max-frequency = <20000000>;
            };

            /* disable spidev on CS0 — conflicts with nrc7292 if dtparam=spi=on is set */
            spidev@0 {
                status = "disabled";
            };
        };
    };
};
DTSEOF

dtc -@ -I dts -O dtb -o /tmp/nrc-rpi.dtbo /tmp/nrc-rpi.dts
sudo cp /tmp/nrc-rpi.dtbo /boot/firmware/overlays/nrc-rpi.dtbo
ok "Overlay installed: /boot/firmware/overlays/nrc-rpi.dtbo"

# Udev rule: on SPI device add, set driver_override so when nrc registers its SPI
# driver Linux auto-binds spi0.0 (driver_override matches driver name nrc80211).
# DO NOT put RUN+=modprobe here — request_firmware inside probe needs udev to deliver
# the firmware file, but udev is blocked while running the RUN directive, causing a
# silent firmware deadlock. Module loading is handled by the systemd service (Step 11).
UDEV_RULES=/etc/udev/rules.d/99-nrc7292.rules
sudo tee "$UDEV_RULES" > /dev/null << 'UDEVEOF'
ACTION=="add", SUBSYSTEM=="spi", ATTR{modalias}=="spi:nrc7292", ATTR{driver_override}="nrc80211"
ACTION=="add", SUBSYSTEM=="spi", ATTR{modalias}=="spi:nrc80211", ATTR{driver_override}="nrc80211"
UDEVEOF
sudo udevadm control --reload-rules
ok "udev rule: spi:nrc7292/nrc80211 -> driver_override nrc80211 (bind only, no modprobe)"

# dtparam=spi=on creates a spidev on CS0 that conflicts with our overlay — remove it
if grep -q '^dtparam=spi=on' /boot/firmware/config.txt; then
    sudo sed -i '/^dtparam=spi=on/d' /boot/firmware/config.txt
    ok "config.txt: removed dtparam=spi=on (conflicts with nrc CS0)"
else
    skip "config.txt: dtparam=spi=on not present"
fi

if grep -q 'dtoverlay=nrc-rpi' /boot/firmware/config.txt; then
    skip "config.txt: dtoverlay=nrc-rpi already present"
else
    echo 'dtoverlay=nrc-rpi' | sudo tee -a /boot/firmware/config.txt
    ok "config.txt: added dtoverlay=nrc-rpi"
fi

# ─────────────────────────────────────────────────────────────────
log "Step 8/11: Install firmware and board data"
[ -f "$FWDIR/nrc7292_cspi.bin" ] || die "Firmware not found: $FWDIR/nrc7292_cspi.bin"
[ -f "$FWDIR/nrc7292_bd.dat" ]   || die "Board data not found: $FWDIR/nrc7292_bd.dat"
sudo cp "$FWDIR/nrc7292_cspi.bin" /lib/firmware/
sudo cp "$FWDIR/nrc7292_bd.dat"   /lib/firmware/bd.dat
ok "Firmware: /lib/firmware/nrc7292_cspi.bin"
ok "Board data: /lib/firmware/bd.dat"

# ─────────────────────────────────────────────────────────────────
log "Step 9/11: Configure modprobe + NetworkManager"
# spi_gpio_irq=-1 + spi_polling_interval=1: use polling (GPIO IRQ never fires on Pi 5)
echo "options nrc fw_name=nrc7292_cspi.bin spi_gpio_irq=-1 spi_polling_interval=1" \
    | sudo tee /etc/modprobe.d/nrc.conf
ok "/etc/modprobe.d/nrc.conf written"

# Prevent NetworkManager from managing wlan1.
# NM auto-probes new wireless interfaces; if it tries to bring up wlan1 before
# iw reg set is done it triggers nrc_mac_start with an invalid country code,
# fails, and leaves the interface stuck (ip link set up returns EPERM).
# The application layer (SquadTac) owns wlan1 entirely.
sudo mkdir -p /etc/NetworkManager/conf.d
echo -e '[keyfile]\nunmanaged-devices=interface-name:wlan1' \
    | sudo tee /etc/NetworkManager/conf.d/99-nrc7292.conf
ok "NetworkManager: wlan1 set unmanaged"

# ─────────────────────────────────────────────────────────────────
log "Step 10/11: Fix start.py paths (hardcoded to /home/pi/nrc_pkg/)"

# start.py expects everything under /home/pi/nrc_pkg — create symlink
sudo mkdir -p /home/pi
if [ ! -L /home/pi/nrc_pkg ]; then
    sudo ln -s "$NRCPKG" /home/pi/nrc_pkg
    ok "Symlink: /home/pi/nrc_pkg -> $NRCPKG"
else
    skip "Symlink /home/pi/nrc_pkg already exists"
fi

# start.py tries insmod /home/pi/nrc_pkg/sw/driver/nrc.ko which doesn't exist.
# We installed via make modules_install so modprobe works instead.
STARTPY="$SCRIPTDIR/start.py"
if grep -q 'insmod.*nrc_pkg/sw/driver/nrc.ko' "$STARTPY"; then
    # Replace the insmod line with modprobe
    python3 - << PYEOF2
with open('$STARTPY', 'r') as f:
    content = f.read()

import re
# Replace: cmd = "sudo insmod /home/pi/nrc_pkg/sw/driver/nrc.ko ..."
# With:    cmd = "sudo modprobe nrc"
content = re.sub(
    r'(cmd\s*=\s*["\'])sudo insmod[^"\']*nrc\.ko[^"\']*(["\'])',
    r'\g<1>sudo modprobe nrc\g<2>',
    content
)
with open('$STARTPY', 'w') as f:
    f.write(content)
print("  OK: start.py: insmod -> modprobe nrc")
PYEOF2
else
    skip "start.py: insmod already replaced or pattern not found"
fi

# ─────────────────────────────────────────────────────────────────
log "Step 11/11: Install systemd service for nrc7292 module load"
# WHY systemd and not udev RUN+=modprobe:
#   When the udev rule fires RUN+=modprobe at boot, nrc probe() calls request_firmware()
#   which blocks waiting for udev to deliver nrc7292_cspi.bin — but udev is already busy
#   running the modprobe RUN directive. Deadlock: firmware never downloads, g_bd_valid never
#   becomes true, ip link set wlan1 up returns EPERM forever.
#
# The fix: a oneshot systemd service runs modprobe nrc AFTER systemd-udev-settle.service
# completes. At that point udev is idle and can service the firmware request normally.
# Confirmed: on a clean boot the service fires, firmware downloads (start FW / end FW in
# dmesg), and wlan1 appears automatically.
sudo tee /etc/systemd/system/nrc7292.service > /dev/null << 'SVCEOF'
[Unit]
Description=NRC7292 HaLow driver
After=systemd-udev-settle.service

[Service]
Type=oneshot
ExecStart=/sbin/modprobe nrc
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
SVCEOF
sudo systemctl daemon-reload
sudo systemctl enable nrc7292.service
ok "systemd service installed and enabled: nrc7292.service (runs after udev-settle)"

# ─────────────────────────────────────────────────────────────────
echo ""
echo -e "${GREEN}${BOLD}╔══════════════════════════════════════╗"
echo -e "║   Setup complete — reboot required   ║"
echo -e "╚══════════════════════════════════════╝${NC}"
echo ""
echo "1. Reboot:  sudo reboot"
echo ""
echo "2. After reboot, verify wlan1 appeared automatically:"
echo "   iw dev"
echo "   dmesg | grep nrc | tail -20"
echo "   (look for: 'start FW', 'end FW', 'succeed in loading board data')"
echo ""
echo "3. Board data loads ~40s after boot (async). Wait before bringing up:"
echo "   sleep 45"
echo "   sudo iw reg set US"
echo "   sudo ip link set wlan1 up"
echo "   iw dev wlan1 info"
echo ""
echo "4. If ip link returns EPERM, check board data loaded:"
echo "   dmesg | grep -i 'board data'"
