Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 73 additions & 43 deletions rmk-config/src/chip.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ChipConfig, KeyboardTomlConfig};
use crate::{ChipConfig, KeyboardTomlConfig, SplitBoardConfig};

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum ChipSeries {
Expand Down Expand Up @@ -59,68 +59,98 @@ impl ChipModel {
}
}

/// Build a `ChipModel` from a raw chip name string.
pub fn parse_chip_model(chip: &str) -> Result<ChipModel, String> {
let lower = chip.to_lowercase();
if lower.starts_with("stm32") {
Ok(ChipModel {
series: ChipSeries::Stm32,
chip: chip.to_string(),
board: None,
})
} else if lower.starts_with("nrf52") {
Ok(ChipModel {
series: ChipSeries::Nrf52,
chip: chip.to_string(),
board: None,
})
} else if lower.starts_with("rp2040") {
Ok(ChipModel {
series: ChipSeries::Rp2040,
chip: chip.to_string(),
board: None,
})
} else if lower.starts_with("esp32") {
Ok(ChipModel {
series: ChipSeries::Esp32,
chip: chip.to_string(),
board: None,
})
} else {
Err(format!("Unsupported chip: {}", chip))
}
}

/// Build a `ChipModel` from a supported board name.
fn chip_model_from_board(board: &str) -> Result<ChipModel, String> {
match board {
"nice!nano" | "nice!nano_v1" | "nicenano" | "nice!nano_v2" | "nice!nano v2" | "XIAO BLE"
| "nrfmicro" | "bluemicro840" | "puchi_ble" => Ok(ChipModel {
series: ChipSeries::Nrf52,
chip: "nrf52840".to_string(),
board: Some(board.to_string()),
}),
"Pi Pico W" | "Pico W" | "pi_pico_w" | "pico_w" => Ok(ChipModel {
series: ChipSeries::Rp2040,
chip: "rp2040".to_string(),
board: Some(board.to_string()),
}),
_ => Err(format!("Unsupported board: {}", board)),
}
}

impl KeyboardTomlConfig {
pub(crate) fn get_chip_model(&self) -> Result<ChipModel, String> {
let keyboard = self.keyboard.as_ref().unwrap();
if keyboard.board.is_none() == keyboard.chip.is_none() {
return Err("Either \"board\" or \"chip\" should be set in keyboard.toml, but not both".to_string());
}

// Check board type
if let Some(board) = keyboard.board.clone() {
match board.as_str() {
"nice!nano" | "nice!nano_v1" | "nicenano" | "nice!nano_v2" | "nice!nano v2" | "XIAO BLE"
| "nrfmicro" | "bluemicro840" | "puchi_ble" => Ok(ChipModel {
series: ChipSeries::Nrf52,
chip: "nrf52840".to_string(),
board: Some(board),
}),
"Pi Pico W" | "Pico W" | "pi_pico_w" | "pico_w" => Ok(ChipModel {
series: ChipSeries::Rp2040,
chip: "rp2040".to_string(),
board: Some(board),
}),
_ => Err(format!("Unsupported board: {}", board)),
}
chip_model_from_board(&board)
} else if let Some(chip) = keyboard.chip.clone() {
if chip.to_lowercase().starts_with("stm32") {
Ok(ChipModel {
series: ChipSeries::Stm32,
chip,
board: None,
})
} else if chip.to_lowercase().starts_with("nrf52") {
Ok(ChipModel {
series: ChipSeries::Nrf52,
chip,
board: None,
})
} else if chip.to_lowercase().starts_with("rp2040") {
Ok(ChipModel {
series: ChipSeries::Rp2040,
chip,
board: None,
})
} else if chip.to_lowercase().starts_with("esp32") {
Ok(ChipModel {
series: ChipSeries::Esp32,
chip,
board: None,
})
} else {
Err(format!("Unsupported chip: {}", chip))
}
parse_chip_model(&chip)
} else {
Err("Neither board nor chip is specified".to_string())
}
}

pub(crate) fn get_chip_config(&self) -> ChipConfig {
let chip_name = &self.get_chip_model().unwrap().chip;
self.get_chip_config_for(chip_name)
}

pub(crate) fn get_chip_config_for(&self, chip_name: &str) -> ChipConfig {
self.chip
.as_ref()
.and_then(|chip_configs| chip_configs.get(chip_name))
.cloned()
.unwrap_or_default()
}

/// Resolve the chip model for a split board.
///
/// If the board defines its own `chip`, use it; otherwise fall back to the
/// top-level keyboard chip model.
pub fn resolve_split_board_chip(
&self,
board_config: &SplitBoardConfig,
fallback: &ChipModel,
) -> ChipModel {
board_config
.chip
.as_ref()
.map(|chip| parse_chip_model(chip).expect("Invalid split board chip"))
.unwrap_or_else(|| fallback.clone())
}
}
121 changes: 120 additions & 1 deletion rmk-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ pub const DEFAULT_PASSKEY_ENTRY_TIMEOUT_SECS: u32 = 120;
pub const MIN_PASSKEY_ENTRY_TIMEOUT_SECS: u32 = 30;

/// Config for chip-specific settings
#[derive(Clone, Default, Debug, Deserialize)]
#[derive(Clone, Default, Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ChipConfig {
/// DCDC regulator 0 enabled (for nrf52840)
Expand Down Expand Up @@ -764,6 +764,9 @@ pub struct SplitBoardConfig {
pub row_offset: usize,
/// Col offset of the split board
pub col_offset: usize,
/// Chip model for this split board.
/// If not set, the top-level keyboard chip is used.
pub chip: Option<String>,
/// Ble address
pub ble_addr: Option<[u8; 6]>,
/// Serial config, the vector length should be 1 for peripheral
Expand Down Expand Up @@ -1229,4 +1232,120 @@ subs = 2
assert_eq!(config.event.layer_change.pubs, 2);
assert_eq!(config.event.layer_change.subs, 2);
}

#[test]
fn test_split_board_chip_defaults_to_top_level_chip() {
let user_toml = r#"
[keyboard]
name = "Test"
vendor_id = 0x1234
product_id = 0x5678
chip = "nrf52840"

[layout]
rows = 4
cols = 3
layers = 1
keymap = [[["A", "B", "C"], ["D", "E", "F"], ["G", "H", "I"], ["J", "K", "L"]]]

[split]
connection = "ble"

[split.central]
rows = 2
cols = 2
row_offset = 0
col_offset = 0

[split.central.matrix]
matrix_type = "normal"
row_pins = ["P0_00", "P0_01"]
col_pins = ["P0_02", "P0_03"]

[[split.peripheral]]
rows = 2
cols = 1
row_offset = 2
col_offset = 2

[split.peripheral.matrix]
matrix_type = "normal"
row_pins = ["P0_04", "P0_05"]
col_pins = ["P0_06"]
"#;
let config: KeyboardTomlConfig = Config::builder()
.add_source(File::from_str(user_toml, FileFormat::Toml))
.build()
.unwrap()
.try_deserialize()
.unwrap();

let top_level_chip = config.get_chip_model().unwrap();
let peripheral = &config.split.as_ref().unwrap().peripheral[0];
let resolved = config.resolve_split_board_chip(peripheral, &top_level_chip);
assert_eq!(resolved, top_level_chip);
}

#[test]
fn test_split_board_chip_override() {
let user_toml = r#"
[keyboard]
name = "Test"
vendor_id = 0x1234
product_id = 0x5678
chip = "nrf52840"

[layout]
rows = 4
cols = 3
layers = 1
keymap = [[["A", "B", "C"], ["D", "E", "F"], ["G", "H", "I"], ["J", "K", "L"]]]

[ble]
enabled = true

[split]
connection = "ble"

[split.central]
rows = 2
cols = 2
row_offset = 0
col_offset = 0

[split.central.matrix]
matrix_type = "normal"
row_pins = ["P0_00", "P0_01"]
col_pins = ["P0_02", "P0_03"]

[[split.peripheral]]
chip = "rp2040"
rows = 2
cols = 1
row_offset = 2
col_offset = 2

[split.peripheral.matrix]
matrix_type = "normal"
row_pins = ["PIN_4", "PIN_5"]
col_pins = ["PIN_6"]
"#;
let config: KeyboardTomlConfig = Config::builder()
.add_source(File::from_str(user_toml, FileFormat::Toml))
.build()
.unwrap()
.try_deserialize()
.unwrap();

let top_level_chip = config.get_chip_model().unwrap();
let peripheral = &config.split.as_ref().unwrap().peripheral[0];
let resolved = config.resolve_split_board_chip(peripheral, &top_level_chip);
assert_eq!(resolved.series, chip::ChipSeries::Rp2040);
assert_eq!(resolved.chip, "rp2040");

let hardware = config.hardware().unwrap();
let (peripheral_chip, peripheral_chip_config) = hardware.chip_for_split_board(peripheral);
assert_eq!(peripheral_chip.series, chip::ChipSeries::Rp2040);
assert_eq!(peripheral_chip_config, ChipConfig::default());
}
}
27 changes: 26 additions & 1 deletion rmk-config/src/resolved/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
//! Leaf types are re-exported directly from the TOML configuration types
//! Only types with genuine structural transformation are defined here.

use std::collections::HashMap;

// Re-export leaf types from TOML config (now properly named and `pub`)
pub use crate::board::{BoardConfig, UniBodyConfig};
pub use crate::chip::{ChipModel, ChipSeries};
pub use crate::chip::{ChipModel, ChipSeries, parse_chip_model};
pub use crate::communication::{CommunicationConfig, UsbInfo};
pub use crate::{
BleConfig, ChipConfig, CommunicationProtocol, DependencyConfig, DisplayConfig, DisplayDriver, EncoderConfig,
Expand All @@ -26,6 +28,8 @@ pub struct Storage {
pub struct Hardware {
pub chip: ChipModel,
pub chip_config: ChipConfig,
/// User-supplied `[chip.<name>]` overrides for all chips used in the build.
pub chip_configs: HashMap<String, ChipConfig>,
pub communication: CommunicationConfig,
pub board: BoardConfig,
pub storage: Option<Storage>,
Expand All @@ -35,6 +39,26 @@ pub struct Hardware {
pub dependency: DependencyConfig,
}

impl Hardware {
/// Resolve the chip model and chip-specific config for a split board.
///
/// If the board defines its own `chip`, that chip is used; otherwise the
/// top-level keyboard chip is used.
pub fn chip_for_split_board(&self, board_config: &SplitBoardConfig) -> (ChipModel, ChipConfig) {
let chip_model = board_config
.chip
.as_ref()
.map(|chip| parse_chip_model(chip).expect("Invalid split board chip"))
.unwrap_or_else(|| self.chip.clone());
let chip_config = self
.chip_configs
.get(&chip_model.chip)
.cloned()
.unwrap_or_default();
(chip_model, chip_config)
}
}

impl crate::KeyboardTomlConfig {
/// Resolve hardware configuration from TOML config.
pub fn hardware(&self) -> Result<Hardware, String> {
Expand All @@ -60,6 +84,7 @@ impl crate::KeyboardTomlConfig {
Ok(Hardware {
chip,
chip_config,
chip_configs: self.chip.clone().unwrap_or_default(),
communication,
board,
storage,
Expand Down
Loading
Loading