diff --git a/include/host_stream/mcp2515.h b/include/host_stream/mcp2515.h index 8e50acf..4e9939a 100644 --- a/include/host_stream/mcp2515.h +++ b/include/host_stream/mcp2515.h @@ -476,7 +476,7 @@ typedef struct mcp2515_dev typedef struct spi_description { char *spidev_path; - uint8_t fd; + int fd; /* spidev file descriptor; -1 = not open */ uint8_t mode; uint8_t bits; uint32_t speed; diff --git a/src/host_stream/event_recorder.c b/src/host_stream/event_recorder.c index 7f9aeb9..dcd28aa 100644 --- a/src/host_stream/event_recorder.c +++ b/src/host_stream/event_recorder.c @@ -266,9 +266,9 @@ static void http_post_file(const char *filepath) printf("[EVT] upload: %s sent OK\n", basename); } -/* ═══════════════════════════════════════════════════════════════════════════ +/* * Helpers - * ═══════════════════════════════════════════════════════════════════════════ */ + * */ #define TZ_OFFSET_SEC (8 * 3600) diff --git a/src/host_stream/mcp2515.c b/src/host_stream/mcp2515.c index d2981ac..ae30f9d 100644 --- a/src/host_stream/mcp2515.c +++ b/src/host_stream/mcp2515.c @@ -337,11 +337,14 @@ static void spi_autoprobe(mcp2515_dev *mcp2515_device) int spi_initial(mcp2515_dev *mcp2515_device) { int ret = 0; - int bind_ok = 0; - bind_ok = ensure_spidev_binding(mcp2515_device->spi_dev->spidev_path); - if (bind_ok < 0) { - return -8; + /* Close existing fd before reopening to prevent fd leak. + * ensure_spidev_binding() is intentionally skipped here: the kernel + * already exposes /dev/spidev1.0 at boot, so rebinding is unnecessary + * and causes ESHUTDOWN errors on the old fd during CAN error recovery. */ + if (mcp2515_device->spi_dev->fd >= 0) { + close(mcp2515_device->spi_dev->fd); + mcp2515_device->spi_dev->fd = -1; } int fd = open(mcp2515_device->spi_dev->spidev_path, O_RDWR); @@ -349,7 +352,7 @@ int spi_initial(mcp2515_dev *mcp2515_device) printf("[SPI] failed to open %s (errno=%d)\n", mcp2515_device->spi_dev->spidev_path, errno); return -1; } - mcp2515_device->spi_dev->fd = (uint8_t)fd; + mcp2515_device->spi_dev->fd = fd; ret = ioctl(fd, SPI_IOC_WR_MODE, &(mcp2515_device->spi_dev->mode)); if (ret == -1) { printf("[SPI] WR_MODE failed (errno=%d)\n", errno); return -2; } @@ -446,8 +449,9 @@ mcp2515_dev* new_mcp2515_dev(char *spidev_path) mcp2515_dev* mcp2515_device = (mcp2515_dev*)malloc(sizeof(mcp2515_dev)); mcp2515_device->spi_dev = (spi_description*)malloc(sizeof(spi_description)); mcp2515_device->spi_dev->spidev_path = spidev_path; - mcp2515_device->spi_dev->mode = 0; - mcp2515_device->spi_dev->bits = 8; + mcp2515_device->spi_dev->fd = -1; /* not open yet */ + mcp2515_device->spi_dev->mode = 0; + mcp2515_device->spi_dev->bits = 8; mcp2515_device->spi_dev->speed = 1000000; return mcp2515_device; } diff --git a/web_serve.py b/web_serve.py index 0ae4b55..9d64acd 100644 --- a/web_serve.py +++ b/web_serve.py @@ -415,55 +415,6 @@ def _verify_firmware_running(tn): ok = any("FW PID:" in ln for ln in lines) return ok, lines -def _spi_setup_on_device(tn, spi_node="spi1.0"): - """Try to switch SPI node from display driver to spidev and report status.""" - steps = [ - ("SPI preflight: listing SPI devices...", - "ls -l /sys/bus/spi/devices 2>/dev/null || true", 10), - ("SPI preflight: current modalias...", - f"cat /sys/bus/spi/devices/{spi_node}/modalias 2>/dev/null || echo 'modalias missing'", 10), - ("SPI preflight: current driver link...", - f"readlink /sys/bus/spi/devices/{spi_node}/driver 2>/dev/null || echo 'driver link missing'", 10), - ("SPI setup: unbind current driver...", - f"DRV=$(basename $(readlink /sys/bus/spi/devices/{spi_node}/driver 2>/dev/null) 2>/dev/null || true); " - f"echo DRIVER=$DRV; " - f"[ -n \"$DRV\" ] && echo {spi_node} > /sys/bus/spi/drivers/$DRV/unbind 2>/dev/null || true; " - "echo 'unbind done'", 12), - ("SPI setup: set driver_override=spidev (if supported)...", - f"if [ -e /sys/bus/spi/devices/{spi_node}/driver_override ]; then " - f"echo spidev > /sys/bus/spi/devices/{spi_node}/driver_override && echo 'override OK'; " - "else echo 'driver_override missing'; fi", 10), - ("SPI setup: bind spidev...", - f"echo {spi_node} > /sys/bus/spi/drivers/spidev/bind 2>/dev/null || true; echo 'bind done'", 10), - ("SPI verify: modalias + driver + /dev/spidev*", - f"echo 'modalias:'; cat /sys/bus/spi/devices/{spi_node}/modalias 2>/dev/null || true; " - f"echo 'driver:'; readlink /sys/bus/spi/devices/{spi_node}/driver 2>/dev/null || true; " - "echo '/dev:'; ls -l /dev/spidev* 2>/dev/null || echo 'no /dev/spidev'", 12), - ] - - out = [] - for label, cmd, timeout in steps: - out.append(("log", label)) - out.append(("prompt", f"$ {cmd}")) - for ln in _telnet_run(tn, cmd, timeout=timeout): - kind = "ok" - low = ln.lower() - if "missing" in low or "no /dev/spidev" in low: - kind = "warn" - out.append((kind, ln)) - - # Final health hint - final = _telnet_run( - tn, - f"M=$(cat /sys/bus/spi/devices/{spi_node}/modalias 2>/dev/null || true); " - "echo FINAL_MODALIAS=$M; " - "echo $M | grep -q dh2228fv && echo 'SPI_BINDING_BLOCKED' || echo 'SPI_BINDING_OK'", - timeout=10, - ) - for ln in final: - out.append(("warn" if "BLOCKED" in ln else "ok", ln)) - return out - # ── API: deploy via Telnet (SSE) ────────────────────────────────────────────── @app.route("/api/deploy/run") def api_deploy_run(): @@ -473,7 +424,6 @@ def api_deploy_run(): port = int(request.args.get("port", cfg["port"])) out_rtsp = request.args.get("out_rtsp", "1") == "1" out_hdmi = request.args.get("out_hdmi", "0") == "1" - spi_fix = request.args.get("spi_fix", "1") == "1" base = f"http://{h_ip}:{port}" bd = BIN_DIR_DEVICE fw = FW_PATH_DEVICE @@ -539,12 +489,7 @@ def api_deploy_run(): tn = _telnet_connect(kl_ip) _drain_shell(tn) yield sse(f"Connected to {kl_ip}", "ok") - - if spi_fix: - yield sse("Running SPI setup preflight (spi1.0 -> spidev)...") - for kind, text in _spi_setup_on_device(tn, "spi1.0"): - yield sse(text, kind) - + for label, cmd, bg, timeout in steps: yield sse(label) yield sse(f"$ {cmd}", "prompt") @@ -651,131 +596,6 @@ def api_deploy_startsh(): headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) # ── API: SPI setup via Telnet (SSE) ───────────────────────────────────────── -@app.route("/api/spi_setup/run") -def api_spi_setup_run(): - cfg = load_config() - kl_ip = request.args.get("ip", cfg["kl630_ip"]) - spi_node = request.args.get("node", "spi1.0") - - def generate(): - yield sse(f"Connecting to {kl_ip}:23 via Telnet...") - try: - tn = _telnet_connect(kl_ip) - _drain_shell(tn) - yield sse("Connected.", "ok") - for kind, text in _spi_setup_on_device(tn, spi_node): - yield sse(text, kind) - tn.close() - yield sse("SPI setup flow complete.", "ok") - except Exception as e: - yield sse(f"Telnet error: {e}", "error") - yield sse_done() - - return Response(generate(), mimetype="text/event-stream", - headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) - -# ── API: BT UART one-time baud setup via Telnet (SSE) ──────────────────────── -@app.route("/api/bt_setup/run") -def api_bt_setup_run(): - cfg = load_config() - kl_ip = request.args.get("ip", cfg["kl630_ip"]) - bd = BIN_DIR_DEVICE - fw = FW_PATH_DEVICE - restart_cmd = (f"cd {bd} && rm -f /dev/shm/* && " - f"nohup sh ./ini/demo_rtsp.sh > /tmp/fw.log 2>&1 &") - - def generate(): - yield sse("⚠ 請先關閉手機 nRF Connect 並確保 DX-BT24 未連線,再繼續!", "warn") - yield sse(f"Connecting to {kl_ip}:23 via Telnet...") - try: - tn = _telnet_connect(kl_ip) - _drain_shell(tn) - yield sse("Connected.", "ok") - - # Step 1: kill firmware so UART is free - yield sse("Step 1: stopping firmware...") - _telnet_run(tn, "killall -9 kp_firmware_host_stream 2>/dev/null; " - "killall -9 rtsps 2>/dev/null; sleep 1; echo stopped", timeout=10) - - # Step 2: probe current baud by reading UART response - yield sse("Step 2: probing module baud rate...") - probe_115200 = ( - "stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; " - "cat /dev/ttyS1 > /tmp/_bt_resp.bin & CPID=$!; " - "printf 'AT+BAUD\\r\\n' > /dev/ttyS1; sleep 1; " - "kill $CPID 2>/dev/null; " - "BYTES=$(wc -c < /tmp/_bt_resp.bin 2>/dev/null || echo 0); " - "echo \"115200_bytes=$BYTES\"; " - "[ \"$BYTES\" -gt 0 ] && cat /tmp/_bt_resp.bin || true" - ) - resp_115200 = _telnet_run(tn, probe_115200, timeout=8) - for ln in resp_115200: - yield sse(ln, "ok") - - got_115200 = any("115200_bytes=" in ln and not ln.endswith("=0") for ln in resp_115200) - - if got_115200: - yield sse("Module responded at 115200 — already configured!", "ok") - else: - yield sse("No response at 115200 — trying 9600...", "warn") - probe_9600 = ( - "stty -F /dev/ttyS1 9600 raw cs8 -parenb -cstopb -echo; " - "cat /dev/ttyS1 > /tmp/_bt_resp.bin & CPID=$!; " - "printf 'AT+BAUD\\r\\n' > /dev/ttyS1; sleep 1; " - "kill $CPID 2>/dev/null; " - "BYTES=$(wc -c < /tmp/_bt_resp.bin 2>/dev/null || echo 0); " - "echo \"9600_bytes=$BYTES\"; " - "[ \"$BYTES\" -gt 0 ] && cat /tmp/_bt_resp.bin || true" - ) - resp_9600 = _telnet_run(tn, probe_9600, timeout=8) - for ln in resp_9600: - yield sse(ln, "ok") - - got_9600 = any("9600_bytes=" in ln and not ln.endswith("=0") for ln in resp_9600) - - if got_9600: - yield sse("Module at 9600 — sending AT+BAUD7 to upgrade...", "ok") - upgrade = ( - "stty -F /dev/ttyS1 9600 raw cs8 -parenb -cstopb -echo; " - "printf 'AT+BAUD7\\r\\n' > /dev/ttyS1; sleep 0.3; " - "stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; " - "printf 'AT+RESET\\r\\n' > /dev/ttyS1; sleep 1.5; " - "echo 'upgrade_sent'" - ) - for ln in _telnet_run(tn, upgrade, timeout=8): - yield sse(ln, "ok") - yield sse("AT+BAUD7 + AT+RESET sent. Module rebooting...", "ok") - else: - yield sse("No response at 9600 either.", "warn") - yield sse("Can't probe module — it may be in BLE transparent mode or disconnected.", "warn") - yield sse("Make sure phone is DISCONNECTED from DX-BT24, then run this again.", "error") - - # Step 3: send test string at 115200 to verify - yield sse("Step 3: sending test ping at 115200...") - test_cmd = ( - "stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; " - "printf '{\"class\":\"test\",\"level\":0}' > /dev/ttyS1; " - "echo 'ping_sent'" - ) - for ln in _telnet_run(tn, test_cmd, timeout=5): - yield sse(ln, "ok") - - # Step 4: restart firmware normally (bt_at_probe stays 0) - yield sse("Step 4: restarting firmware...") - _telnet_run(tn, "killall -9 kp_firmware_host_stream 2>/dev/null; " - "killall -9 rtsps 2>/dev/null; sleep 1; rm -f /dev/shm/*", timeout=10) - _telnet_run_bg(tn, restart_cmd) - yield sse("Firmware restarted.", "ok") - - tn.close() - yield sse('完成!現在連上手機 → 訂閱 Notify → 如果看到 {"class":"test","level":0} 或 {"class":"boot","level":0} 表示成功', "ok") - except Exception as e: - yield sse(f"Telnet error: {e}", "error") - yield sse_done() - - return Response(generate(), mimetype="text/event-stream", - headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) - # ── API: RTSP → MJPEG proxy (via opencv-python, no system ffmpeg needed) ────── _stream_active = False @@ -1789,14 +1609,6 @@ input:checked+.slider:before{transform:translateX(16px);background:#fff} Update start.sh - -