Skip to content

Commit c15bf25

Browse files
committed
feat: add bootloader command scanner script
Scripts to scan and display bootloader command responses for protocol exploration and documentation. Usage: scripts/scan-commands.sh [OPTIONS] Options: -a, --all Scan all commands 0x00-0x7F -k, --known Scan only known/documented commands (default) -r, --range M-N Scan command range, e.g., 0x00-0x20 -c, --cmd CMD Run single command with optional data -v, --verbose Show full packet details -q, --quiet Show only summary Examples: scan-commands.sh -a # full scan scan-commands.sh -r 0x30-0x40 # range scan scan-commands.sh -c "0x3B 0x00" # area 0 info Signed-off-by: Vincent Jardin <[email protected]>
1 parent 75065d7 commit c15bf25

File tree

1 file changed

+339
-0
lines changed

1 file changed

+339
-0
lines changed

scripts/scan-commands.sh

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
#!/bin/bash
2+
#
3+
# Copyright (C) 2026 Vincent Jardin <[email protected]> Free Mobile
4+
#
5+
# SPDX-License-Identifier: AGPL-3.0-or-later
6+
#
7+
# Bootloader command scanner for Renesas RA MCUs
8+
#
9+
# Iterates through all known bootloader commands and displays their responses.
10+
# Useful for protocol exploration and documentation.
11+
#
12+
# Requirements:
13+
# - J16 shorted for boot mode
14+
# - Board connected via USB
15+
#
16+
17+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
18+
RADFU="${RADFU:-$(cd "$SCRIPT_DIR/.." && pwd)/build/radfu}"
19+
20+
# Colors
21+
RED='\033[0;31m'
22+
GREEN='\033[0;32m'
23+
YELLOW='\033[1;33m'
24+
BLUE='\033[0;34m'
25+
CYAN='\033[0;36m'
26+
NC='\033[0m'
27+
28+
usage() {
29+
cat <<EOF
30+
Usage: $0 [OPTIONS]
31+
32+
Scan bootloader commands and display responses.
33+
34+
Options:
35+
-a, --all Scan all commands 0x00-0x7F (slow)
36+
-k, --known Scan only known/documented commands (default)
37+
-r, --range M-N Scan command range (hex), e.g., 0x00-0x20
38+
-c, --cmd CMD Run single command with optional data
39+
-v, --verbose Show full packet details for each command
40+
-q, --quiet Show only summary (no packet details)
41+
-h, --help Show this help
42+
43+
Examples:
44+
$0 # Scan known commands
45+
$0 -a # Scan all 0x00-0x7F
46+
$0 -r 0x30-0x40 # Scan specific range
47+
$0 -c 0x3A # Single command (signature)
48+
$0 -c "0x3B 0x00" # Command with data (area 0)
49+
50+
Known Commands:
51+
No data: 0x00 (INQ), 0x2C (DLM), 0x3A (SIG), 0x4F (BND)
52+
With data: 0x12 (ERA), 0x13 (WRI), 0x15 (REA), 0x18 (CRC),
53+
0x28-0x2B (KEY), 0x30 (AUTH), 0x34 (BAU),
54+
0x3B (ARE), 0x4E (BND_SET), 0x50-0x52 (INI/PRM),
55+
0x71 (DLM_TRANSIT)
56+
EOF
57+
exit 0
58+
}
59+
60+
check_device() {
61+
if [ ! -x "$RADFU" ]; then
62+
echo -e "${RED}Error:${NC} radfu not found at $RADFU"
63+
echo "Build with: meson compile -C build"
64+
exit 1
65+
fi
66+
67+
if ! "$RADFU" info -q >/dev/null 2>&1; then
68+
echo -e "${RED}Error:${NC} Cannot connect to device"
69+
echo "Check: J16 shorted for boot mode, USB connected"
70+
exit 1
71+
fi
72+
}
73+
74+
run_cmd() {
75+
local cmd="$1"
76+
shift
77+
local data="$*"
78+
local output
79+
local status
80+
81+
if [ -n "$data" ]; then
82+
# shellcheck disable=SC2086
83+
output=$("$RADFU" raw "$cmd" $data 2>&1)
84+
else
85+
output=$("$RADFU" raw "$cmd" 2>&1)
86+
fi
87+
status=$?
88+
89+
echo "$output"
90+
return $status
91+
}
92+
93+
print_cmd_header() {
94+
local cmd="$1"
95+
local name="$2"
96+
echo -e "\n${CYAN}----------------------------------------${NC}"
97+
echo -e "${CYAN}Command: $cmd ($name)${NC}"
98+
echo -e "${CYAN}----------------------------------------${NC}"
99+
}
100+
101+
scan_single() {
102+
local cmd="$1"
103+
shift
104+
local data="$*"
105+
local output
106+
local res
107+
108+
if [ -n "$data" ]; then
109+
# shellcheck disable=SC2086
110+
output=$(run_cmd "$cmd" $data)
111+
else
112+
output=$(run_cmd "$cmd")
113+
fi
114+
115+
if [ "$QUIET" -eq 1 ]; then
116+
res=$(echo "$output" | grep -E "RES:|STS:" | head -2)
117+
printf "%-6s: %s\n" "$cmd" "$res"
118+
else
119+
echo "$output"
120+
fi
121+
}
122+
123+
scan_known() {
124+
echo -e "${BLUE}=== Scanning Known Commands ===${NC}"
125+
126+
# Commands without data
127+
print_cmd_header "0x00" "INQ - Inquiry"
128+
scan_single 0x00
129+
130+
print_cmd_header "0x2C" "DLM - DLM State Request"
131+
scan_single 0x2C
132+
133+
print_cmd_header "0x3A" "SIG - Signature Request"
134+
scan_single 0x3A
135+
136+
print_cmd_header "0x4F" "BND - Boundary Request"
137+
scan_single 0x4F
138+
139+
# Area information for all areas
140+
print_cmd_header "0x3B" "ARE - Area Information"
141+
echo -e "${YELLOW}Area 0:${NC}"
142+
scan_single 0x3B 0x00
143+
echo -e "${YELLOW}Area 1:${NC}"
144+
scan_single 0x3B 0x01
145+
echo -e "${YELLOW}Area 2:${NC}"
146+
scan_single 0x3B 0x02
147+
echo -e "${YELLOW}Area 3:${NC}"
148+
scan_single 0x3B 0x03
149+
150+
# Parameter request
151+
print_cmd_header "0x52" "PRM - Parameter Request"
152+
echo -e "${YELLOW}Parameter 0x01 (Initialize command):${NC}"
153+
scan_single 0x52 0x01
154+
155+
# Commands that need data (show hint)
156+
echo -e "\n${BLUE}=== Commands Requiring Data (hints) ===${NC}"
157+
158+
for cmd in 0x12 0x13 0x15 0x18; do
159+
printf "%-6s: " "$cmd"
160+
run_cmd "$cmd" 2>&1 | grep -E "Hint:" | head -1
161+
done
162+
163+
for cmd in 0x28 0x29 0x2A 0x2B 0x30 0x34 0x4E 0x50 0x51 0x71; do
164+
printf "%-6s: " "$cmd"
165+
run_cmd "$cmd" 2>&1 | grep -E "Hint:" | head -1
166+
done
167+
}
168+
169+
scan_range() {
170+
local start="$1"
171+
local end="$2"
172+
local start_dec=$((start))
173+
local end_dec=$((end))
174+
175+
echo -e "${BLUE}=== Scanning Range $(printf '0x%02X' $start_dec)-$(printf '0x%02X' $end_dec) ===${NC}"
176+
177+
for ((i=start_dec; i<=end_dec; i++)); do
178+
local cmd
179+
local output
180+
local res_line
181+
local sts_line
182+
183+
cmd=$(printf "0x%02X" $i)
184+
output=$(run_cmd "$cmd" 2>&1)
185+
res_line=$(echo "$output" | grep "RES:" | head -1)
186+
sts_line=$(echo "$output" | grep "STS:" | head -1)
187+
188+
if echo "$res_line" | grep -q "| OK)"; then
189+
local name
190+
name="${res_line#*(}"
191+
name="${name% | OK)*}"
192+
echo -e "${GREEN}$cmd${NC}: OK - $name"
193+
if [ "$VERBOSE" -eq 1 ]; then
194+
echo "$output" | grep -E "^\s+(Raw:|DATA|->)" | head -10
195+
echo
196+
fi
197+
elif echo "$sts_line" | grep -q "ERR_PCKT"; then
198+
echo -e "${YELLOW}$cmd${NC}: Recognized (needs data)"
199+
if [ "$VERBOSE" -eq 1 ]; then
200+
echo "$output" | grep -E "Hint:" | head -1
201+
echo
202+
fi
203+
elif echo "$sts_line" | grep -q "ERR_NSCM"; then
204+
echo -e "${RED}$cmd${NC}: Unrecognized"
205+
else
206+
echo -e "$cmd: $res_line"
207+
fi
208+
done
209+
}
210+
211+
scan_all() {
212+
echo -e "${BLUE}=== Full Command Scan (0x00-0x7F) ===${NC}"
213+
echo "This may take a while..."
214+
echo
215+
216+
local ok_cmds=""
217+
local data_cmds=""
218+
local unrec_cmds=""
219+
220+
for ((i=0; i<=127; i++)); do
221+
local cmd
222+
local output
223+
local res_line
224+
local sts_line
225+
226+
cmd=$(printf "0x%02X" $i)
227+
output=$(run_cmd "$cmd" 2>&1)
228+
res_line=$(echo "$output" | grep "RES:" | head -1)
229+
sts_line=$(echo "$output" | grep "STS:" | head -1)
230+
231+
if echo "$res_line" | grep -q "| OK)"; then
232+
ok_cmds="$ok_cmds $cmd"
233+
if [ "$VERBOSE" -eq 1 ]; then
234+
local name
235+
name="${res_line#*(}"
236+
name="${name% | OK)*}"
237+
echo -e "${GREEN}$cmd${NC}: OK - $name"
238+
echo "$output" | grep -E "^\s+(Raw:|DATA|->)" | head -10
239+
echo
240+
else
241+
echo -ne "${GREEN}.${NC}"
242+
fi
243+
elif echo "$sts_line" | grep -q "ERR_PCKT"; then
244+
data_cmds="$data_cmds $cmd"
245+
if [ "$VERBOSE" -eq 1 ]; then
246+
echo -e "${YELLOW}$cmd${NC}: Recognized (needs data)"
247+
echo "$output" | grep -E "Hint:" | head -1
248+
echo
249+
else
250+
echo -ne "${YELLOW}.${NC}"
251+
fi
252+
else
253+
unrec_cmds="$unrec_cmds $cmd"
254+
if [ "$VERBOSE" -ne 1 ]; then
255+
echo -ne "${RED}.${NC}"
256+
fi
257+
fi
258+
259+
# Progress indicator every 16 commands (non-verbose only)
260+
if [ "$VERBOSE" -ne 1 ] && [ $((i % 16)) -eq 15 ]; then
261+
printf " %s\n" "$(printf '0x%02X' $i)"
262+
fi
263+
done
264+
265+
echo -e "\n\n${BLUE}=== Summary ===${NC}"
266+
echo -e "${GREEN}OK (no data needed):${NC}$ok_cmds"
267+
echo -e "${YELLOW}Recognized (needs data):${NC}$data_cmds"
268+
echo -e "${RED}Unrecognized:${NC} $(echo "$unrec_cmds" | wc -w) commands"
269+
}
270+
271+
# Defaults
272+
MODE="known"
273+
QUIET=0
274+
VERBOSE=0
275+
RANGE_START=""
276+
RANGE_END=""
277+
SINGLE_CMD=""
278+
279+
# Parse arguments
280+
while [ $# -gt 0 ]; do
281+
case "$1" in
282+
-a|--all)
283+
MODE="all"
284+
shift
285+
;;
286+
-k|--known)
287+
MODE="known"
288+
shift
289+
;;
290+
-r|--range)
291+
MODE="range"
292+
IFS='-' read -r RANGE_START RANGE_END <<< "$2"
293+
shift 2
294+
;;
295+
-c|--cmd)
296+
MODE="single"
297+
SINGLE_CMD="$2"
298+
shift 2
299+
;;
300+
-v|--verbose)
301+
VERBOSE=1
302+
shift
303+
;;
304+
-q|--quiet)
305+
QUIET=1
306+
shift
307+
;;
308+
-h|--help)
309+
usage
310+
;;
311+
*)
312+
echo "Unknown option: $1"
313+
usage
314+
;;
315+
esac
316+
done
317+
318+
echo -e "${BLUE}Renesas RA Bootloader Command Scanner${NC}"
319+
echo
320+
321+
check_device
322+
323+
case "$MODE" in
324+
known)
325+
scan_known
326+
;;
327+
all)
328+
scan_all
329+
;;
330+
range)
331+
scan_range "$RANGE_START" "$RANGE_END"
332+
;;
333+
single)
334+
# shellcheck disable=SC2086
335+
scan_single $SINGLE_CMD
336+
;;
337+
esac
338+
339+
echo -e "\n${GREEN}Done.${NC}"

0 commit comments

Comments
 (0)