1#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct RawMapping {
11 pub pin: String,
13 pub af: u8,
15 pub peripheral: String,
17 pub signal: String,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum PackError {
24 Malformed(String),
26}
27
28impl core::fmt::Display for PackError {
29 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
30 match self {
31 PackError::Malformed(what) => write!(f, "malformed pack data: {what}"),
32 }
33 }
34}
35
36impl std::error::Error for PackError {}
37
38#[derive(Debug, Clone, Copy)]
41pub enum Patch {
42 Remove {
44 pin: &'static str,
45 af: u8,
46 peripheral: &'static str,
47 signal: &'static str,
48 reason: &'static str,
49 },
50 Add {
52 pin: &'static str,
53 af: u8,
54 peripheral: &'static str,
55 signal: &'static str,
56 reason: &'static str,
57 },
58}
59
60pub const PATCHES: &[Patch] = &[];
78
79pub fn parse_gpio_modes(xml: &str) -> Result<Vec<RawMapping>, PackError> {
81 let doc = roxmltree::Document::parse(xml)
82 .map_err(|e| PackError::Malformed(format!("gpio modes xml: {e}")))?;
83
84 let mut mappings = Vec::new();
85 for gpio_pin in elements_named(&doc, "GPIO_Pin") {
86 let pin = required_attr(&gpio_pin, "Name")?;
87 let Some(pin) = normalize_pin_name(pin) else {
90 continue;
91 };
92
93 for pin_signal in gpio_pin
94 .children()
95 .filter(|n| n.is_element() && n.tag_name().name() == "PinSignal")
96 {
97 let signal_name = required_attr(&pin_signal, "Name")?;
98 let Some(af) = gpio_af_value(&pin_signal) else {
99 continue;
101 };
102 let af = af
103 .strip_prefix("GPIO_AF")
104 .and_then(|s| s.split('_').next())
105 .and_then(|s| s.parse::<u8>().ok())
106 .filter(|af| *af <= 15)
107 .ok_or_else(|| PackError::Malformed(format!("bad GPIO_AF value {af:?}")))?;
108
109 let (peripheral, signal) = signal_name
113 .split_once('_')
114 .unwrap_or((signal_name, signal_name));
115
116 mappings.push(RawMapping {
117 pin: pin.to_string(),
118 af,
119 peripheral: peripheral.to_string(),
120 signal: signal.to_string(),
121 });
122 }
123 }
124 Ok(mappings)
125}
126
127pub fn parse_package_pins(xml: &str) -> Result<Vec<String>, PackError> {
130 let doc = roxmltree::Document::parse(xml)
131 .map_err(|e| PackError::Malformed(format!("mcu xml: {e}")))?;
132
133 Ok(elements_named(&doc, "Pin")
134 .filter_map(|pin| {
135 let name = pin.attribute("Name")?;
136 normalize_pin_name(name).map(str::to_string)
138 })
139 .collect())
140}
141
142pub fn apply_patches(mappings: &mut Vec<RawMapping>, patches: &[Patch]) {
144 for patch in patches {
145 match *patch {
146 Patch::Remove {
147 pin,
148 af,
149 peripheral,
150 signal,
151 reason: _,
152 } => mappings.retain(|m| {
153 !(m.pin == pin && m.af == af && m.peripheral == peripheral && m.signal == signal)
154 }),
155 Patch::Add {
156 pin,
157 af,
158 peripheral,
159 signal,
160 reason: _,
161 } => mappings.push(RawMapping {
162 pin: pin.to_string(),
163 af,
164 peripheral: peripheral.to_string(),
165 signal: signal.to_string(),
166 }),
167 }
168 }
169}
170
171pub fn generate_table(
174 mappings: &[RawMapping],
175 package_pins: &[String],
176 const_name: &str,
177) -> String {
178 let mut rows: Vec<(char, u8, &RawMapping)> = mappings
179 .iter()
180 .filter(|m| package_pins.contains(&m.pin))
181 .filter_map(|m| {
182 let (port, number) = pin_sort_key(&m.pin)?;
183 Some((port, number, m))
184 })
185 .collect();
186 rows.sort_by_key(|(port, number, m)| {
187 (*port, *number, m.af, m.peripheral.clone(), m.signal.clone())
188 });
189 rows.dedup_by(|a, b| a.2 == b.2);
190
191 let mut out = format!(
192 "// @generated by build.rs from packdata/ — do not edit.\n\
193 pub(crate) const {const_name}: &[AfMapping] = &[\n",
194 );
195 for (port, number, m) in rows {
196 out.push_str(&format!(
197 " map(Port::{port}, {number}, {af}, \"{peripheral}\", \"{signal}\"),\n",
198 af = m.af,
199 peripheral = m.peripheral,
200 signal = m.signal,
201 ));
202 }
203 out.push_str("];\n");
204 out
205}
206
207fn normalize_pin_name(name: &str) -> Option<&str> {
209 let base = name.split(['-', '/', ' ']).next().unwrap_or(name);
210 pin_sort_key(base).map(|_| base)
211}
212
213fn pin_sort_key(pin: &str) -> Option<(char, u8)> {
215 let rest = pin.strip_prefix('P')?;
216 let port = rest.chars().next().filter(char::is_ascii_uppercase)?;
217 let number: u8 = rest[1..].parse().ok().filter(|n| *n <= 15)?;
218 Some((port, number))
219}
220
221fn elements_named<'a>(
224 doc: &'a roxmltree::Document<'a>,
225 name: &'static str,
226) -> impl Iterator<Item = roxmltree::Node<'a, 'a>> {
227 doc.descendants()
228 .filter(move |n| n.is_element() && n.tag_name().name() == name)
229}
230
231fn required_attr<'a>(
232 node: &roxmltree::Node<'a, '_>,
233 attr: &'static str,
234) -> Result<&'a str, PackError> {
235 node.attribute(attr).ok_or_else(|| {
236 PackError::Malformed(format!(
237 "<{}> missing {attr} attribute",
238 node.tag_name().name()
239 ))
240 })
241}
242
243fn gpio_af_value<'a>(pin_signal: &roxmltree::Node<'a, '_>) -> Option<&'a str> {
245 pin_signal
246 .children()
247 .find(|n| {
248 n.is_element()
249 && n.tag_name().name() == "SpecificParameter"
250 && n.attribute("Name") == Some("GPIO_AF")
251 })?
252 .children()
253 .find(|n| n.is_element() && n.tag_name().name() == "PossibleValue")?
254 .text()
255}