diff --git a/crates/tyutool-cli/src/main.rs b/crates/tyutool-cli/src/main.rs index 751c151f..00ecdd1e 100644 --- a/crates/tyutool-cli/src/main.rs +++ b/crates/tyutool-cli/src/main.rs @@ -154,7 +154,8 @@ enum Commands { // runtime — instead `device_list_matches_registry` asserts the two agree, so a // chip added to the registry can't silently drift out of the CLI. const SUPPORTED_DEVICES: &[&str] = &[ - "bk7231n", "t2", "t3", "t1", "t5ai", "ln882h", "esp32", "esp32c3", "esp32c6", "esp32s3", + "bk7231n", "t2", "t3", "t1", "t5ai", "ln882h", "esp32", "esp32c3", "esp32c6", "esp32p4", + "esp32s3", ]; fn chip_value_parser() -> impl TypedValueParser { @@ -184,7 +185,7 @@ fn chip_value_parser() -> impl TypedValueParser { fn default_baud(device: &str) -> u32 { match device.to_ascii_lowercase().as_str() { "ln882h" => 115200, - "esp32" | "esp32c3" | "esp32c6" | "esp32s3" => 460800, + "esp32" | "esp32c3" | "esp32c6" | "esp32p4" | "esp32s3" => 460800, _ => 921600, } } @@ -684,6 +685,7 @@ mod tests { ("ESP32", "esp32"), ("ESP32C3", "esp32c3"), ("ESP32C6", "esp32c6"), + ("ESP32P4", "esp32p4"), ("ESP32S3", "esp32s3"), ]; for (input, canonical) in cases { @@ -712,6 +714,7 @@ mod tests { assert_eq!(default_baud("esp32"), 460800); assert_eq!(default_baud("esp32c3"), 460800); assert_eq!(default_baud("esp32c6"), 460800); + assert_eq!(default_baud("esp32p4"), 460800); assert_eq!(default_baud("esp32s3"), 460800); assert_eq!(default_baud("bk7231n"), 921600); assert_eq!(default_baud("t5ai"), 921600); diff --git a/crates/tyutool-core/src/plugins/esp/chips.rs b/crates/tyutool-core/src/plugins/esp/chips.rs index ea9f7965..107dbfb3 100644 --- a/crates/tyutool-core/src/plugins/esp/chips.rs +++ b/crates/tyutool-core/src/plugins/esp/chips.rs @@ -29,3 +29,8 @@ pub(crate) static ESP32S3_DEF: EspChipDef = EspChipDef { id: "ESP32S3", chip: Chip::Esp32s3, }; + +pub(crate) static ESP32P4_DEF: EspChipDef = EspChipDef { + id: "ESP32P4", + chip: Chip::Esp32p4, +}; diff --git a/crates/tyutool-core/src/plugins/esp32p4.rs b/crates/tyutool-core/src/plugins/esp32p4.rs new file mode 100644 index 00000000..c6e396c1 --- /dev/null +++ b/crates/tyutool-core/src/plugins/esp32p4.rs @@ -0,0 +1,28 @@ +//! ESP32-P4 flash plugin. + +use std::sync::atomic::AtomicBool; + +use crate::error::FlashError; +use crate::flash_event::FlashEvent; +use crate::job::FlashJob; +use crate::plugin::FlashPlugin; + +use super::esp::chips::ESP32P4_DEF; +use super::esp::common::run_esp; + +pub struct Esp32p4Plugin; + +impl FlashPlugin for Esp32p4Plugin { + fn id(&self) -> &'static str { + "ESP32P4" + } + + fn run( + &self, + job: &FlashJob, + cancel: &AtomicBool, + progress: &dyn Fn(FlashEvent), + ) -> Result<(), FlashError> { + run_esp(job, cancel, progress, &ESP32P4_DEF) + } +} diff --git a/crates/tyutool-core/src/plugins/mod.rs b/crates/tyutool-core/src/plugins/mod.rs index fc995a36..1cc0a898 100644 --- a/crates/tyutool-core/src/plugins/mod.rs +++ b/crates/tyutool-core/src/plugins/mod.rs @@ -4,6 +4,7 @@ pub mod esp; pub mod esp32; pub mod esp32c3; pub mod esp32c6; +pub mod esp32p4; pub mod esp32s3; pub mod ln882h; pub mod t1; @@ -15,6 +16,7 @@ pub use bk7231n::Bk7231nPlugin; pub use esp32::Esp32Plugin; pub use esp32c3::Esp32c3Plugin; pub use esp32c6::Esp32c6Plugin; +pub use esp32p4::Esp32p4Plugin; pub use esp32s3::Esp32s3Plugin; pub use ln882h::Ln882hPlugin; pub use t1::T1Plugin; diff --git a/crates/tyutool-core/src/registry.rs b/crates/tyutool-core/src/registry.rs index 67786d87..a0a349a2 100644 --- a/crates/tyutool-core/src/registry.rs +++ b/crates/tyutool-core/src/registry.rs @@ -6,8 +6,8 @@ use crate::flash_event::{FlashEvent, FlashResult, JobSummary}; use crate::job::{FlashJob, FlashMode}; use crate::plugin::FlashPlugin; use crate::plugins::{ - Bk7231nPlugin, Esp32Plugin, Esp32c3Plugin, Esp32c6Plugin, Esp32s3Plugin, Ln882hPlugin, - T1Plugin, T2Plugin, T3Plugin, T5AIPlugin, + Bk7231nPlugin, Esp32Plugin, Esp32c3Plugin, Esp32c6Plugin, Esp32p4Plugin, Esp32s3Plugin, + Ln882hPlugin, T1Plugin, T2Plugin, T3Plugin, T5AIPlugin, }; /// Canonicalize a user-supplied chip id: trim, upper-case, and rewrite legacy @@ -47,6 +47,8 @@ impl FlashPluginRegistry { log::debug!("Registered flash plugin: ESP32C3"); plugins.insert("ESP32C6".to_string(), Arc::new(Esp32c6Plugin)); log::debug!("Registered flash plugin: ESP32C6"); + plugins.insert("ESP32P4".to_string(), Arc::new(Esp32p4Plugin)); + log::debug!("Registered flash plugin: ESP32P4"); plugins.insert("ESP32S3".to_string(), Arc::new(Esp32s3Plugin)); log::debug!("Registered flash plugin: ESP32S3"); plugins.insert("LN882H".to_string(), Arc::new(Ln882hPlugin)); @@ -189,6 +191,8 @@ mod tests { assert!(r.get("ESP32C3").is_ok()); assert!(r.get("esp32c6").is_ok()); assert!(r.get("ESP32C6").is_ok()); + assert!(r.get("esp32p4").is_ok()); + assert!(r.get("ESP32P4").is_ok()); assert!(r.get("esp32s3").is_ok()); assert!(r.get("ESP32S3").is_ok()); assert!(r.get("ln882h").is_ok()); @@ -200,7 +204,7 @@ mod tests { fn list_chip_ids_only_real_plugins() { let r = FlashPluginRegistry::new(); let ids = r.list_chip_ids(); - assert_eq!(ids.len(), 10); + assert_eq!(ids.len(), 11); assert!(ids.contains(&"BK7231N".to_string())); assert!(ids.contains(&"T2".to_string())); assert!(ids.contains(&"T3".to_string())); @@ -209,6 +213,7 @@ mod tests { assert!(ids.contains(&"ESP32".to_string())); assert!(ids.contains(&"ESP32C3".to_string())); assert!(ids.contains(&"ESP32C6".to_string())); + assert!(ids.contains(&"ESP32P4".to_string())); assert!(ids.contains(&"ESP32S3".to_string())); assert!(ids.contains(&"LN882H".to_string())); } diff --git a/docs/cli.md b/docs/cli.md index df5a854a..be3b1c0c 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -214,6 +214,7 @@ Outputs JSON with raw USB metadata for all ports. Used for cross-OS debugging. | `esp32` | ESP32 | 460800 | | `esp32c3` | ESP32-C3 | 460800 | | `esp32c6` | ESP32-C6 | 460800 | +| `esp32p4` | ESP32-P4 | 460800 | | `esp32s3` | ESP32-S3 | 460800 | Device names are case-insensitive (`--device T5AI`, `--device t5AI`, and `--device t5ai` are all equivalent). diff --git a/src/features/batch-flash-auth/BatchFlashAuthPage.vue b/src/features/batch-flash-auth/BatchFlashAuthPage.vue index a42e96de..a57d0a9a 100644 --- a/src/features/batch-flash-auth/BatchFlashAuthPage.vue +++ b/src/features/batch-flash-auth/BatchFlashAuthPage.vue @@ -31,7 +31,7 @@ onMounted(async () => { }); onUnmounted(() => { - store.cleanup(); + if (!store.isBusy) store.cleanup(); }); diff --git a/src/features/firmware-flash/chip-manifests.test.ts b/src/features/firmware-flash/chip-manifests.test.ts index b13f874b..d2c6605b 100644 --- a/src/features/firmware-flash/chip-manifests.test.ts +++ b/src/features/firmware-flash/chip-manifests.test.ts @@ -75,11 +75,18 @@ describe("chipManifest", () => { expect(chipManifest("esp32").rustPluginId).toBe("ESP32"); expect(chipManifest("esp32c3").rustPluginId).toBe("ESP32C3"); expect(chipManifest("esp32c6").rustPluginId).toBe("ESP32C6"); + expect(chipManifest("esp32p4").rustPluginId).toBe("ESP32P4"); expect(chipManifest("esp32s3").rustPluginId).toBe("ESP32S3"); }); it("ESP chips have 460800 default baud", () => { - for (const id of ["esp32", "esp32c3", "esp32c6", "esp32s3"] as const) { + for (const id of [ + "esp32", + "esp32c3", + "esp32c6", + "esp32p4", + "esp32s3", + ] as const) { expect(chipManifest(id).defaultBaudRate).toBe(460800); } }); @@ -106,6 +113,7 @@ describe("rustPluginIdForChip", () => { expect(rustPluginIdForChip("bk7231n")).toBe("BK7231N"); expect(rustPluginIdForChip("esp32")).toBe("ESP32"); expect(rustPluginIdForChip("esp32c3")).toBe("ESP32C3"); + expect(rustPluginIdForChip("esp32p4")).toBe("ESP32P4"); expect(rustPluginIdForChip("esp32s3")).toBe("ESP32S3"); }); diff --git a/src/features/firmware-flash/chip-manifests.ts b/src/features/firmware-flash/chip-manifests.ts index b34a87b9..d98bc9b0 100644 --- a/src/features/firmware-flash/chip-manifests.ts +++ b/src/features/firmware-flash/chip-manifests.ts @@ -58,6 +58,17 @@ export const CHIP_MANIFEST: Record = { fullChip: { start: "0x00000000", end: "0x007FFFFF" }, }, }, + esp32p4: { + rustPluginId: "ESP32P4", + defaultBaudRate: 460800, + defaultAuthBaudRate: 115200, + defaultLogBaudRate: 115200, + flashSize: "0x01000000", // 16 MiB + eraseRequires4KAlignment: true, + erasePresets: { + fullChip: { start: "0x00000000", end: "0x00FFFFFF" }, + }, + }, esp32s3: { rustPluginId: "ESP32S3", defaultBaudRate: 460800, diff --git a/src/features/firmware-flash/constants.ts b/src/features/firmware-flash/constants.ts index 3da2b9b2..75aee216 100644 --- a/src/features/firmware-flash/constants.ts +++ b/src/features/firmware-flash/constants.ts @@ -4,6 +4,7 @@ export const CHIP_IDS = [ "esp32", "esp32c3", "esp32c6", + "esp32p4", "esp32s3", "ln882h", "t1", diff --git a/src/locales/en.json b/src/locales/en.json index a343a219..d13892f1 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -132,6 +132,7 @@ "esp32": "USB–UART hard-reset sequence (same as after a flash completes; line mapping depends on the board). Free the serial port first.", "esp32c3": "Reset follows the detected USB serial/JTAG path—same as post-flash reset. Free the serial port first.", "esp32c6": "Hard reset matches post-flash reset; interface type is auto-detected. Free the serial port first.", + "esp32p4": "Hard reset matches post-flash reset; interface type is auto-detected. Free the serial port first.", "esp32s3": "Some boards use a USB-JTAG/serial bridge; reset timing matches post-flash reset. Free the serial port first." }, "deviceOps": "Device & operation", @@ -147,6 +148,7 @@ "esp32": "ESP32", "esp32c3": "ESP32-C3", "esp32c6": "ESP32-C6", + "esp32p4": "ESP32-P4", "esp32s3": "ESP32-S3", "bk7231n": "BK7231N", "ln882h": "LN882H", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index b4c18ed8..4862a152 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -132,6 +132,7 @@ "esp32": "经 USB 串口的硬复位时序,与一次完整烧录结束后的复位相同;引脚组合取决于板级连线。请先释放串口占用。", "esp32c3": "复位序列随检测到的 USB 串口或 USB-JTAG 接口而定,与烧录结束后一致。请先释放串口占用。", "esp32c6": "复位序列与烧录结束后的硬复位一致;接口类型由系统自动识别。请先释放串口占用。", + "esp32p4": "复位序列与烧录结束后的硬复位一致;接口类型由系统自动识别。请先释放串口占用。", "esp32s3": "部分开发板为 USB-JTAG/串口复合,复位时序与烧录结束后一致。请先释放串口占用。" }, "deviceOps": "设备与操作", @@ -147,6 +148,7 @@ "esp32": "ESP32", "esp32c3": "ESP32-C3", "esp32c6": "ESP32-C6", + "esp32p4": "ESP32-P4", "esp32s3": "ESP32-S3", "bk7231n": "BK7231N", "ln882h": "LN882H",