mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-29 09:14:33 +00:00
* docs(mochabin): add EP06 mPCIe GPIO investigation runbook (ref #345) scripts/probe-mpcie-gpios.sh: empirical CP0 GPIO sweep that drives each unrequested line HIGH for a few seconds and watches dmesg/lsusb for new USB device. Skips [used] lines, uses libgpiod gpioset --mode=time so the line auto-restores to input on release. Modes: --baseline (snapshot only, safe), --line gpiochipN K (single line), no arg (full sweep gpiochip1+gpiochip2). docs/hardware/mochabin-mpcie-ep06-runbook.md: hypothesis, procedure, and DTS rfkill-gpio template patterned after cn9132-clearfog.dts lines 69-122. Apply once a candidate GPIO is identified. --baseline smoke-tested on gk2 — sweep deferred until spare EP06 arrives (no mPCIe device in J5 = no signal regardless of which line drives W_DISABLE#). * docs(mochabin): drop EP06 spare assumption from runbook (ref #345) No spare EP06 on order; the existing EP06-E will be used for the sweep when re-seated in J5. Replaces "deferred until spare" framing with a multimeter-scoping fallback for the inconclusive case. * fix(mpcie): v0.2.0 probe-mpcie-gpios.sh — drop blanket sweep, danger-list, dry-run default (ref #345) The v0.1.0 "drive every unused-input CP0 GPIO HIGH for 3s one-at-a-time" sweep took gk2 hard-down within seconds (host became unreachable; ARP stopped responding). gpioinfo's [used] tag only reflects lines the kernel *requested* — several unrequested CP0 GPIOs are physically wired to critical board functions (eth switch reset, PCIe2 PERST#, pca9554 IRQ) and driving them HIGH wedged the board. v0.2.0: - No blanket-sweep mode. Removed. - Default mode = dry-run candidate listing only. No GPIO is driven. - --commit --line CHIP N required to actually drive a single line. - Hardcoded DANGER_LINES (gpiochip1:1, gpiochip1:9, gpiochip1:27) skipped unconditionally, even with --commit. - gpiochip2 entirely held off without --allow-chip2 (no per-line schematic mapping for that bank). - Hard refusals are vocal (REFUSED: ... reason) before exit. Runbook updated to walk through the new dry-run → --commit flow. HISTORY documents the incident + the v0.2.0 mitigations. Lesson saved in memory feedback_gpio_blanket_sweep_crashed_board.md. --------- Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
This commit is contained in:
parent
fe834d4fe8
commit
ff990ba112
|
|
@ -1,6 +1,56 @@
|
|||
# HISTORY — SecuBox-DEB Migration Log
|
||||
*Tracking completed milestones with dates*
|
||||
|
||||
---
|
||||
## 2026-05-22
|
||||
|
||||
### MOCHAbin mPCIe slot J5 — EP06 GPIO investigation runbook (Issue #345)
|
||||
|
||||
**Context:** PR #255's DTS patch wired the UTMI PHY to `cp0_usb3_1`, so the
|
||||
mPCIe slot's USB pipe is alive (4 USB buses on gk2 vs 2 before). But
|
||||
plugging an EP06-E still produces no enumeration: `lsusb` blank for the
|
||||
slot, `lspci -vvv` reports `DLActive-` on the PCIe side. Suspect: the
|
||||
slot's W_DISABLE# / PWR_EN / WAKE# control lines are not declared in
|
||||
the DTS, so they come up in whatever default state the SoC pad config
|
||||
leaves them — likely holding the modem powered-down. No MOCHAbin
|
||||
schematic in the repo to pin down which CP0 GPIO is wired to J5.
|
||||
|
||||
**Done:**
|
||||
|
||||
- **#345** — added `scripts/probe-mpcie-gpios.sh`: empirical sweep that
|
||||
drives each *unrequested* CP0 GPIO line HIGH for a few seconds and
|
||||
watches `dmesg` / `lsusb` for a new USB device. Skips any line marked
|
||||
`[used]` by gpioinfo, uses libgpiod (`gpioset --mode=time` so the
|
||||
line auto-restores to input on release). Three modes: `--baseline`
|
||||
(snapshot only, no GPIO writes — safe to run anytime), `--line
|
||||
gpiochipN K` (single line, useful once a candidate is found), or no
|
||||
arg (full sweep across `gpiochip1` + `gpiochip2` = the two CP0
|
||||
banks). Output to `/var/log/secubox/mpcie-probe-<ts>.log`.
|
||||
- Companion `docs/hardware/mochabin-mpcie-ep06-runbook.md` documenting
|
||||
the hypothesis, the procedure step-by-step, and the DTS template
|
||||
(`rfkill-gpio` block patterned after `cn9132-clearfog.dts` lines
|
||||
69–122) to apply once a candidate GPIO is identified.
|
||||
|
||||
**Validation:** `--baseline` smoke-tested on gk2 — produces correct
|
||||
gpioinfo dump, identifies the `[used]` lines (cp0_gpio0 line 0 "reset",
|
||||
line 12 "PHY reset", line 30 "shutdown", plus the SFP+ pca9554
|
||||
expander chip 3). No spare modem expected; if probing finds no
|
||||
candidate the next escalation is multimeter scoping of J5 pins
|
||||
2/20/24/39/41/52.
|
||||
|
||||
**Incident — v0.1.0 blanket sweep crashed gk2:** running the
|
||||
"all unused inputs" sweep with no modem in J5 took the board hard-down
|
||||
within seconds (host became unreachable; ARP stopped responding).
|
||||
`gpioinfo`'s `[used]` tag only reflects lines the kernel *requested*,
|
||||
not lines that are physically wired — several unrequested CP0 GPIOs
|
||||
drive critical board functions (eth switch reset, PCIe2 PERST#,
|
||||
pca9554 IRQ). v0.2.0 of the script removes the blanket-sweep mode
|
||||
entirely: defaults to dry-run candidate listing, requires explicit
|
||||
`--commit --line CHIP N` to drive any single line, hardcodes a
|
||||
`DANGER_LINES` skip-list, and holds `gpiochip2` off behind
|
||||
`--allow-chip2`. Memorialised in memory
|
||||
`feedback_gpio_blanket_sweep_crashed_board.md`.
|
||||
|
||||
---
|
||||
## 2026-05-21
|
||||
|
||||
|
|
|
|||
169
docs/hardware/mochabin-mpcie-ep06-runbook.md
Normal file
169
docs/hardware/mochabin-mpcie-ep06-runbook.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# MOCHAbin mPCIe Slot J5 — EP06 Investigation Runbook
|
||||
|
||||
**Status:** parked. Runbook ready to execute when an mPCIe modem is
|
||||
reseated in J5 (the original EP06-E on hand; no spare on order).
|
||||
**Related issues:** #255 (DTS UTMI PHY landed) · #345 (this runbook).
|
||||
|
||||
## Symptom
|
||||
|
||||
After the DTS patch in #255 wired the UTMI PHY to `cp0_usb3_1`, the
|
||||
mPCIe slot J5 USB pipe is fully alive — kernel reports the controller
|
||||
binds cleanly and `lsusb -t` shows 4 USB buses (vs 2 before the patch).
|
||||
|
||||
But plugging a Quectel EP06-E modem into J5 still doesn't produce any
|
||||
`new high-speed USB device` event in dmesg, no `2c7c:0306` in `lsusb`,
|
||||
and `lspci -vvv` reports the PCIe side as `DLActive-` (no link
|
||||
partner). The modem is electrically dark on both interfaces of the
|
||||
slot.
|
||||
|
||||
## Hypothesis
|
||||
|
||||
mPCIe defines several control pins per slot that the host must drive
|
||||
for the device to wake:
|
||||
|
||||
| Pin | Direction | Meaning | Default |
|
||||
| ------------- | ----------- | ----------------------- | -------------------------------- |
|
||||
| PERST# | host→device | reset (active low) | must transition low→high at boot |
|
||||
| W_DISABLE# | host→device | radio kill (active low) | must be HIGH for radio |
|
||||
| WAKE# | device→host | wake event (active low) | input only |
|
||||
| PWR_EN / 3.3V | rail | power | must be ON |
|
||||
|
||||
In `armada-7040-mochabin.dts`:
|
||||
|
||||
- `cp0_pcie2` has `reset-gpios = <&cp0_gpio1 9 GPIO_ACTIVE_LOW>` —
|
||||
PERST# is declared.
|
||||
- `cp0_usb3_1` has UTMI PHY but **no W_DISABLE# / PWR_EN / WAKE#
|
||||
GPIOs** declared.
|
||||
|
||||
The schematic for the MOCHAbin J5 slot is not in this repo, and
|
||||
Globalscale only publishes a partial. So we don't know which CP0 GPIO
|
||||
(if any) is wired to W_DISABLE#. The other option is that it's
|
||||
hard-tied (HIGH or LOW) on the PCB.
|
||||
|
||||
Reference: `cn9132-clearfog.dts` declares `rfkill-gpio` nodes for each
|
||||
of its mPCIe / M.2 slots (lines 69–122). That's the pattern to copy.
|
||||
|
||||
## Investigation procedure
|
||||
|
||||
`scripts/probe-mpcie-gpios.sh` (v0.2.0) is a one-line-at-a-time probe.
|
||||
v0.1.0 of this script ran a blanket sweep across all "unused input"
|
||||
CP0 GPIOs and took gk2 hard-down on 2026-05-22 — `gpioinfo`'s
|
||||
`[used]` tag only reflects what the kernel *requested*, and several
|
||||
unrequested lines are physically wired to critical functions (eth
|
||||
switch reset, PCIe2 PERST#, pca9554 IRQ). v0.2.0 therefore:
|
||||
|
||||
- skips a hardcoded `DANGER_LINES` list (those three + future
|
||||
additions);
|
||||
- refuses to touch `gpiochip2` (CP0 GPIO1) entirely without
|
||||
`--allow-chip2` (no per-line schematic mapping for that bank yet);
|
||||
- defaults to dry-run — listing candidates without driving anything;
|
||||
- requires `--commit` + `--line CHIP N` to actually drive a line HIGH.
|
||||
|
||||
There is no longer a blanket-sweep mode.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Run **on the MOCHAbin**, as root.
|
||||
- A known-good mPCIe device seated in J5 (preferably the EP06 modem;
|
||||
failing that, any mPCIe-USB device).
|
||||
- Screw tightened — the slot has spring-contact retention only.
|
||||
|
||||
### Step 1 — baseline (safe, no GPIO writes)
|
||||
|
||||
```bash
|
||||
# from the dev host
|
||||
scp scripts/probe-mpcie-gpios.sh root@<mochabin>:/tmp/
|
||||
ssh root@<mochabin> 'bash /tmp/probe-mpcie-gpios.sh --baseline'
|
||||
```
|
||||
|
||||
Output goes to `/var/log/secubox/mpcie-probe-<timestamp>.log`. Check
|
||||
the gpioinfo table to confirm the lines you expect are `[used]` (PHY
|
||||
reset on cp0_gpio0 line 12, etc.) — the script will refuse to touch
|
||||
those.
|
||||
|
||||
### Step 2 — list candidates (dry-run, default mode)
|
||||
|
||||
```bash
|
||||
ssh root@<mochabin> 'bash /tmp/probe-mpcie-gpios.sh'
|
||||
```
|
||||
|
||||
Prints the candidate list on `gpiochip1` (CP0 GPIO0). Each entry is
|
||||
of the form `gpiochip1:N` — an unrequested input that is NOT in
|
||||
`DANGER_LINES`. No GPIO is driven in this mode.
|
||||
|
||||
If you want candidates on `gpiochip2` too (entire bank held off by
|
||||
default), add `--allow-chip2` — only do this if you can physically
|
||||
power-cycle the board if something locks up.
|
||||
|
||||
### Step 3 — commit a single line
|
||||
|
||||
Pick one candidate from step 2, then drive it for `SETTLE_SEC=3` s
|
||||
(longer with the env var if the EP06 needs more time to boot its
|
||||
radio):
|
||||
|
||||
```bash
|
||||
ssh root@<mochabin> \
|
||||
'SETTLE_SEC=10 bash /tmp/probe-mpcie-gpios.sh --line gpiochip1 14 --commit'
|
||||
```
|
||||
|
||||
The script:
|
||||
|
||||
1. Snapshots `lsusb` + last `dmesg` line.
|
||||
2. Drives the line output-HIGH for `SETTLE_SEC` seconds.
|
||||
3. Re-snapshots.
|
||||
4. Releases the line (libgpiod restores input direction on close).
|
||||
5. If `lsusb` differs or new "new high-speed USB device" /
|
||||
"Quectel" / `2c7c` appears in dmesg, logs `*** CHANGE DETECTED ***`.
|
||||
|
||||
A successful probe gives you the GPIO number to wire into the DTS.
|
||||
Iterate over candidates one at a time; if a probe locks up the board,
|
||||
add the line to `DANGER_LINES` in the script before retrying.
|
||||
|
||||
## Translating findings to DTS
|
||||
|
||||
Once a line is confirmed, add an `rfkill-gpio` block to
|
||||
`armada-7040-mochabin.dts` next to the existing `&cp0_usb3_1` /
|
||||
`&cp0_pcie2` declarations. Template (replace `<chip>` and `<line>`):
|
||||
|
||||
```dts
|
||||
/ {
|
||||
/* J5 W_DISABLE# (mPCIe modem radio enable) */
|
||||
rfkill-mpcie-modem {
|
||||
compatible = "rfkill-gpio";
|
||||
label = "mpcie modem (J5)";
|
||||
radio-type = "wwan";
|
||||
/* rfkill-gpio inverts internally; ACTIVE_HIGH = pin HIGH when
|
||||
* radio is OFF, LOW when ON. Check polarity against the
|
||||
* schematic before merging. */
|
||||
shutdown-gpios = <&<chip> <line> GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
If the modem still doesn't enumerate after asserting W_DISABLE# HIGH,
|
||||
the next suspect is a missing 3.3V rail enable. Look for an unused
|
||||
GPIO that toggles `/sys/class/regulator/regulator-N/state` from
|
||||
`disabled` → `enabled` when driven HIGH.
|
||||
|
||||
## Disambiguating "dead modem" vs "slot not powering it"
|
||||
|
||||
There's no spare EP06 coming, so we can't simply swap units. If the
|
||||
GPIO sweep finds no candidate line, the remaining signal that the
|
||||
modem is alive at all is:
|
||||
|
||||
- **3.3V on J5 pin 2 / 24 / 39 / 41 / 52** — multimeter on the
|
||||
connector with the modem unseated tells you if the slot's power rail
|
||||
is live. If 3.3V is missing, the modem can't possibly enumerate
|
||||
regardless of W_DISABLE#.
|
||||
- **Modem LED** — the EP06-E lights an onboard status LED when it has
|
||||
power. Visible through the chassis cutout near J5.
|
||||
|
||||
Multimeter scoping is outside the runbook's automated scope but is the
|
||||
next escalation if the sweep is inconclusive.
|
||||
|
||||
## Outcome we want
|
||||
|
||||
A single small DTS patch (rfkill-gpio block, maybe a regulator) that
|
||||
makes EP06 enumerate at boot without any post-boot intervention. That
|
||||
unblocks #236 (secubox-rbs-sensor v0.2) and the v0.4 SMS-via-EP06
|
||||
notification channel for secubox-sentinelle-gsm.
|
||||
336
scripts/probe-mpcie-gpios.sh
Executable file
336
scripts/probe-mpcie-gpios.sh
Executable file
|
|
@ -0,0 +1,336 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
# Source-Disclosed License — All rights reserved except as expressly granted.
|
||||
# See LICENCE-CMSD-1.0.md for terms.
|
||||
#
|
||||
# SecuBox-Deb :: probe-mpcie-gpios.sh
|
||||
#
|
||||
# Hardware investigation runbook for issue #345 — find which CP0 GPIO drives
|
||||
# W_DISABLE# / PERST# / PWR_EN on the MOCHAbin mPCIe slot J5.
|
||||
#
|
||||
# Background: the kernel DTS patch from #255 wired the UTMI PHY to
|
||||
# cp0_usb3_1 (the mPCIe USB controller) — so the SoC USB pipe is alive
|
||||
# on the slot, but plugging an EP06 modem still doesn't enumerate. The
|
||||
# slot's control lines (W_DISABLE#, PWR_EN, possibly WAKE#) are NOT
|
||||
# declared in the current DTS, so they come up in whatever default state
|
||||
# the SoC pad config leaves them in — which may be holding the modem
|
||||
# powered-down. This runbook drives carefully-selected CP0 GPIO lines
|
||||
# HIGH one at a time, watching for a new USB enumeration event.
|
||||
#
|
||||
# Prerequisites: run ON THE BOARD (gk2), as root, with a known-good
|
||||
# mPCIe device (EP06 modem or any mPCIe-USB device) physically seated
|
||||
# in J5 and the screw tightened.
|
||||
#
|
||||
# Safety (v0.2.0): gpioinfo's "[used]" tag only reflects lines the
|
||||
# kernel *requested* — some unrequested lines on the MOCHAbin are
|
||||
# physically wired to critical functions (eth switch reset, USB hub
|
||||
# power-enable, pca9554 IRQ, etc.) and driving them HIGH crashed
|
||||
# gk2 on the v0.1.0 blanket sweep (incident 2026-05-22). v0.2.0
|
||||
# therefore:
|
||||
# 1. Skips known-dangerous lines unconditionally (DANGER_LINES).
|
||||
# 2. Defaults to --dry-run (lists candidates without driving them).
|
||||
# 3. Requires --commit AND --line for the actual probe — no
|
||||
# blanket sweep mode anymore.
|
||||
# 4. Refuses to touch gpiochip2 (CP0 GPIO1) entirely without an
|
||||
# explicit --allow-chip2 flag (we have no per-line schematic
|
||||
# mapping for that bank yet).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly MODULE="probe-mpcie-gpios"
|
||||
readonly VERSION="0.2.0"
|
||||
|
||||
# Lines known (from DTS inspection) to be wired to critical board
|
||||
# functions even when gpioinfo reports them as "unused input". Format:
|
||||
# "chip:line". Driving any of these would risk a board crash.
|
||||
readonly -a DANGER_LINES=(
|
||||
# gpiochip1 = cp0_gpio0
|
||||
"gpiochip1:1" # mpp1 — cp0_switch_pins gpio function (Topaz reset)
|
||||
"gpiochip1:9" # mpp9 — cp0_pcie_reset_pins (PCIe2 PERST#); driving it can crash the slot
|
||||
"gpiochip1:27" # mpp27 — pca9554_int (SFP+ I/O expander IRQ); IRQ storm risk
|
||||
# gpiochip2 = cp0_gpio1 — entire bank held off by default
|
||||
# (no per-line schematic; require --allow-chip2 to probe)
|
||||
)
|
||||
readonly LOG_DIR="/var/log/secubox"
|
||||
readonly LOG_FILE="${LOG_DIR}/mpcie-probe-$(date +%Y%m%d-%H%M%S).log"
|
||||
readonly SETTLE_SEC="${SETTLE_SEC:-3}"
|
||||
# Which gpiochips to probe. gpiochip1 = cp0_gpio0, gpiochip2 = cp0_gpio1.
|
||||
# These are the two big CP0 banks on the Armada-7040. AP807 (gpiochip0)
|
||||
# is system-controller / never wired to slot J5, so we skip it. SFP+
|
||||
# expander (gpiochip3) and CP210x (gpiochip4) are obviously unrelated.
|
||||
readonly CHIPS="${CHIPS:-gpiochip1 gpiochip2}"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
${MODULE} v${VERSION} — probe one CP0 GPIO line to find mPCIe J5 W_DISABLE#
|
||||
|
||||
usage:
|
||||
$0 snapshot + list dry-run candidates (default)
|
||||
$0 --baseline snapshot only — no candidate listing
|
||||
$0 --line gpiochipN K dry-run a single line (refuses if danger-listed)
|
||||
$0 --line gpiochipN K --commit actually drive the line HIGH for SETTLE_SEC
|
||||
$0 --allow-chip2 ... required to touch any gpiochip2 line
|
||||
$0 --help
|
||||
|
||||
env vars:
|
||||
SETTLE_SEC=N seconds to hold the line HIGH (default 3)
|
||||
|
||||
output:
|
||||
${LOG_DIR}/mpcie-probe-<timestamp>.log
|
||||
|
||||
WARNING — read this before --commit:
|
||||
|
||||
v0.1.0 of this script ran a blanket sweep across all "unused input"
|
||||
CP0 GPIOs. That took gk2 hard-down on 2026-05-22 — some unrequested
|
||||
lines are physically wired to critical functions (eth switch reset,
|
||||
PCIe2 PERST#, pca9554 IRQ, etc.) even though the kernel never
|
||||
requests them. v0.2.0 therefore refuses to run a blanket sweep at
|
||||
all, and the default mode just lists candidates without driving
|
||||
anything.
|
||||
|
||||
When you do --commit a single line, make sure you can physically
|
||||
reach the MOCHAbin to power-cycle it if the board locks up.
|
||||
|
||||
prerequisites: run as root on the MOCHAbin, with an mPCIe device
|
||||
(preferably the EP06 modem) physically seated in J5 and the retention
|
||||
screw tightened.
|
||||
|
||||
A successful probe shows a new "usb 2-1.X: new high-speed USB device"
|
||||
line in the post-dmesg diff AND a new device in lsusb.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# True if "chip:line" matches an entry in DANGER_LINES (case-sensitive).
|
||||
is_danger_line() {
|
||||
local key="$1"
|
||||
local entry
|
||||
for entry in "${DANGER_LINES[@]}"; do
|
||||
[[ "$entry" == "$key" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "error: must run as root (need gpiochip + dmesg access)" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_libgpiod() {
|
||||
for cmd in gpiodetect gpioinfo gpioset; do
|
||||
if ! command -v "$cmd" >/dev/null; then
|
||||
echo "error: $cmd not found — install libgpiod (apt install gpiod)" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
setup_log() {
|
||||
mkdir -p "${LOG_DIR}"
|
||||
exec > >(tee -a "${LOG_FILE}") 2>&1
|
||||
echo "=== ${MODULE} v${VERSION} — $(date -Is) ==="
|
||||
echo "log: ${LOG_FILE}"
|
||||
}
|
||||
|
||||
snapshot_baseline() {
|
||||
echo
|
||||
echo "=== baseline ==="
|
||||
echo "--- kernel ---"
|
||||
uname -a
|
||||
echo "--- gpiodetect ---"
|
||||
gpiodetect
|
||||
echo "--- gpioinfo (probed chips: ${CHIPS}) ---"
|
||||
for chip in ${CHIPS}; do
|
||||
gpioinfo "$chip"
|
||||
done
|
||||
echo "--- lsusb -t ---"
|
||||
lsusb -t
|
||||
echo "--- lsusb ---"
|
||||
lsusb
|
||||
echo "--- lspci -tv ---"
|
||||
lspci -tv 2>/dev/null || echo "(lspci not installed)"
|
||||
echo "--- recent dmesg (last 60s) ---"
|
||||
dmesg -T --since '60 sec ago' 2>/dev/null | tail -30 || dmesg | tail -30
|
||||
}
|
||||
|
||||
# gpioinfo line format:
|
||||
# " line 14: unnamed unused input active-high "
|
||||
# " line 0: unnamed \"reset\" output active-low [used]"
|
||||
# A "candidate" is: unnamed + unused + input + NOT in DANGER_LINES.
|
||||
line_is_candidate() {
|
||||
local chip="$1"
|
||||
local info="$2"
|
||||
local n
|
||||
if [[ "$info" =~ line\ +([0-9]+): ]]; then
|
||||
n="${BASH_REMATCH[1]}"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
[[ "$info" != *"[used]"* ]] || return 1
|
||||
[[ "$info" == *"input"* ]] || return 1
|
||||
[[ "$info" == *"unused"* ]] || return 1
|
||||
is_danger_line "${chip}:${n}" && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
list_candidates() {
|
||||
local chip="$1"
|
||||
echo
|
||||
echo "=== candidates on ${chip} ==="
|
||||
while IFS= read -r line; do
|
||||
if line_is_candidate "${chip}" "${line}"; then
|
||||
# Extract line number for the summary
|
||||
[[ "$line" =~ line\ +([0-9]+): ]] && echo " ${chip}:${BASH_REMATCH[1]}"
|
||||
fi
|
||||
done < <(gpioinfo "${chip}")
|
||||
}
|
||||
|
||||
probe_line_commit() {
|
||||
local chip="$1"
|
||||
local n="$2"
|
||||
|
||||
# Hard gate — never drive a danger-listed line, even with --commit.
|
||||
if is_danger_line "${chip}:${n}"; then
|
||||
echo "REFUSED: ${chip}:${n} is in DANGER_LINES — driving it" \
|
||||
"risks crashing the board (see header comment)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# gpiochip2 entirely off-limits without --allow-chip2.
|
||||
if [[ "${chip}" == "gpiochip2" && "${ALLOW_CHIP2:-0}" != "1" ]]; then
|
||||
echo "REFUSED: gpiochip2 (cp0_gpio1) requires --allow-chip2 — no" \
|
||||
"per-line schematic mapping for that bank yet." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "── probe ${chip} line ${n} (COMMIT, HIGH for ${SETTLE_SEC}s) ──"
|
||||
|
||||
local pre_lsusb pre_dmesg_tail
|
||||
pre_lsusb=$(lsusb | sort)
|
||||
pre_dmesg_tail=$(dmesg | tail -1)
|
||||
|
||||
# Drive HIGH for SETTLE_SEC seconds. gpioset releases the line
|
||||
# (libgpiod restores it to input on close).
|
||||
timeout "$((SETTLE_SEC + 2))" gpioset --mode=time --sec="${SETTLE_SEC}" \
|
||||
"${chip}" "${n}=1" &
|
||||
local gpio_pid=$!
|
||||
sleep "$((SETTLE_SEC - 1))"
|
||||
|
||||
local post_lsusb post_dmesg
|
||||
post_lsusb=$(lsusb | sort)
|
||||
post_dmesg=$(dmesg | awk -v marker="${pre_dmesg_tail}" '
|
||||
BEGIN { capture=0 }
|
||||
index($0, marker) { capture=1; next }
|
||||
capture { print }
|
||||
')
|
||||
|
||||
wait "${gpio_pid}" 2>/dev/null || true
|
||||
|
||||
local lsusb_diff
|
||||
lsusb_diff=$(diff <(echo "${pre_lsusb}") <(echo "${post_lsusb}") || true)
|
||||
|
||||
if [[ -n "${lsusb_diff}" ]] || echo "${post_dmesg}" | grep -qiE 'new (high|full|low|super)-speed usb|quectel|2c7c'; then
|
||||
echo " *** CHANGE DETECTED ***"
|
||||
echo " --- lsusb diff ---"
|
||||
echo "${lsusb_diff:-(no diff)}"
|
||||
echo " --- new dmesg ---"
|
||||
printf ' %s\n' "${post_dmesg}"
|
||||
else
|
||||
echo " no change"
|
||||
fi
|
||||
}
|
||||
|
||||
probe_line_dryrun() {
|
||||
local chip="$1"
|
||||
local n="$2"
|
||||
echo
|
||||
echo "── DRY-RUN ${chip}:${n} ──"
|
||||
if is_danger_line "${chip}:${n}"; then
|
||||
echo " REFUSED: in DANGER_LINES"
|
||||
return
|
||||
fi
|
||||
if [[ "${chip}" == "gpiochip2" && "${ALLOW_CHIP2:-0}" != "1" ]]; then
|
||||
echo " REFUSED: gpiochip2 requires --allow-chip2"
|
||||
return
|
||||
fi
|
||||
# Verify the line is currently unrequested and an input.
|
||||
local info
|
||||
info=$(gpioinfo "${chip}" 2>/dev/null | awk -v n="${n}" '$2 == n":"')
|
||||
if [[ -z "${info}" ]]; then
|
||||
echo " ERROR: line ${n} not found on ${chip}"
|
||||
return
|
||||
fi
|
||||
if [[ "${info}" == *"[used]"* ]]; then
|
||||
echo " REFUSED: line is currently [used] by the kernel"
|
||||
return
|
||||
fi
|
||||
echo " OK: --commit would drive ${chip}:${n} HIGH for ${SETTLE_SEC}s"
|
||||
}
|
||||
|
||||
main() {
|
||||
require_root
|
||||
require_libgpiod
|
||||
|
||||
local mode="default"
|
||||
local chip=""
|
||||
local n=""
|
||||
local commit=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--help|-h)
|
||||
usage; exit 0 ;;
|
||||
--baseline)
|
||||
mode="baseline"; shift ;;
|
||||
--line)
|
||||
[[ $# -ge 3 ]] || { usage; exit 1; }
|
||||
mode="line"; chip="$2"; n="$3"; shift 3 ;;
|
||||
--commit)
|
||||
commit=1; shift ;;
|
||||
--allow-chip2)
|
||||
ALLOW_CHIP2=1; shift ;;
|
||||
*)
|
||||
usage; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
setup_log
|
||||
|
||||
case "${mode}" in
|
||||
baseline)
|
||||
snapshot_baseline
|
||||
;;
|
||||
line)
|
||||
snapshot_baseline
|
||||
if [[ "${commit}" == "1" ]]; then
|
||||
probe_line_commit "${chip}" "${n}"
|
||||
else
|
||||
probe_line_dryrun "${chip}" "${n}"
|
||||
echo
|
||||
echo "(dry-run — pass --commit to actually drive the line)"
|
||||
fi
|
||||
;;
|
||||
default)
|
||||
snapshot_baseline
|
||||
list_candidates gpiochip1
|
||||
if [[ "${ALLOW_CHIP2:-0}" == "1" ]]; then
|
||||
list_candidates gpiochip2
|
||||
else
|
||||
echo
|
||||
echo "=== gpiochip2 candidates suppressed ==="
|
||||
echo " pass --allow-chip2 to list (no per-line schematic yet)"
|
||||
fi
|
||||
echo
|
||||
echo "=== next step ==="
|
||||
echo " pick a candidate, then:"
|
||||
echo " $0 --line <chip> <line> # dry-run"
|
||||
echo " $0 --line <chip> <line> --commit # actually drive"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue
Block a user