esp_hal_mfrc522/
debug.rs

1use core::fmt::Write;
2
3use crate::{
4    MFRC522, MfrcDriver,
5    consts::{PCDErrorCode, PICCCommand, PICCType, Uid},
6};
7use heapless::String;
8
9#[allow(async_fn_in_trait)]
10pub trait MFRC522Debug {
11    async fn debug_dump_card(&mut self, uid: &Uid) -> Result<(), PCDErrorCode>;
12    async fn debug_dump_card_memory(&mut self, uid: &Uid) -> Result<(), PCDErrorCode>;
13    async fn debug_dump_card_details(&mut self, uid: &Uid) -> Result<(), PCDErrorCode>;
14}
15
16impl<D> MFRC522<D>
17where
18    D: MfrcDriver,
19{
20    pub async fn test(&mut self) {}
21}
22
23#[allow(unused)]
24impl<D> MFRC522<D>
25where
26    D: MfrcDriver,
27{
28    pub async fn debug_dump_card(&mut self, uid: &Uid) -> Result<(), PCDErrorCode> {
29        self.debug_dump_card_details(uid).await?;
30        log::debug!("");
31        self.debug_dump_card_memory(uid).await?;
32
33        Ok(())
34    }
35
36    pub async fn debug_dump_card_memory(&mut self, uid: &Uid) -> Result<(), PCDErrorCode> {
37        let picc_type = PICCType::from_sak(uid.sak);
38
39        match picc_type {
40            PICCType::PiccTypeMifare1K
41            | PICCType::PiccTypeMifare4K
42            | PICCType::PiccTypeMifareMini => {
43                let mifare_key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
44                let res = dump_mifare_classic(self, uid, &mifare_key, picc_type).await;
45                if let Err(e) = res {
46                    log::error!("Dump mifare classic failed: {e:?}");
47                }
48            }
49            PICCType::PiccTypeMifareUL => {
50                let res = dump_mifare_ultralight(self).await;
51                if let Err(e) = res {
52                    log::error!("Dump mifare ultralight failed: {e:?}");
53                }
54            }
55            PICCType::PiccTypeUnknown | PICCType::PiccTypeNotComplete => {
56                return Ok(());
57            }
58            _ => {
59                log::warn!("Dumping memory not implemented for: {:?}", picc_type);
60            }
61        }
62
63        self.picc_halta().await?;
64        Ok(())
65    }
66
67    pub async fn debug_dump_card_details(&mut self, uid: &Uid) -> Result<(), PCDErrorCode> {
68        let mut dbg_line_buff: String<32> = String::new();
69        for i in 0..uid.size {
70            _ = dbg_line_buff.write_fmt(format_args!(" {:02X}", uid.uid_bytes[i as usize]));
71        }
72
73        log::debug!("Card UID:{dbg_line_buff}");
74        log::debug!("Card SAK: 0x{:02X}", uid.sak);
75        log::debug!("PICC Type: {:?}", PICCType::from_sak(uid.sak));
76
77        Ok(())
78    }
79}
80
81async fn dump_mifare_classic<D: MfrcDriver>(
82    mfrc522: &mut MFRC522<D>,
83    uid: &Uid,
84    key: &[u8],
85    picc_type: PICCType,
86) -> Result<(), PCDErrorCode> {
87    let sectors_count = match picc_type {
88        PICCType::PiccTypeMifareMini => 5,
89        PICCType::PiccTypeMifare1K => 16,
90        PICCType::PiccTypeMifare4K => 40,
91        _ => unreachable!(),
92    };
93
94    log::debug!("Sector Block   0  1  2  3   4  5  6  7   8  9 10 11  12 13 14 15  AccessBits");
95    for i in (0..sectors_count).rev() {
96        let res = dump_mifare_classic_sector(mfrc522, uid, key, i).await;
97        if let Err(e) = res {
98            log::error!("Sector dump error ({i}): {e:?}");
99        }
100    }
101
102    mfrc522.picc_halta().await?;
103    mfrc522.pcd_stop_crypto1().await?;
104    Ok(())
105}
106
107async fn dump_mifare_classic_sector<D: MfrcDriver>(
108    mfrc522: &mut MFRC522<D>,
109    uid: &Uid,
110    key: &[u8],
111    sector: u8,
112) -> Result<(), PCDErrorCode> {
113    let mut groups = [0; 4];
114    let mut inverted_error = false;
115
116    let first_block;
117    let no_of_blocks;
118    if sector < 32 {
119        no_of_blocks = 4;
120        first_block = sector * no_of_blocks;
121    } else if sector < 40 {
122        no_of_blocks = 16;
123        first_block = 128 + (sector - 32) * no_of_blocks;
124    } else {
125        return Err(PCDErrorCode::Invalid);
126    }
127
128    let mut is_sector_trailer = true;
129    let mut buff = [0; 18];
130
131    let mut dbg_line_buff: String<128> = String::new();
132    for block_offset in (0..no_of_blocks).rev() {
133        dbg_line_buff.clear();
134
135        let block_addr = first_block + block_offset;
136        if is_sector_trailer {
137            mfrc522
138                .pcd_authenticate(PICCCommand::PICC_CMD_MF_AUTH_KEY_A, first_block, key, uid)
139                .await?;
140
141            _ = dbg_line_buff.write_fmt(format_args!("  {sector: >2}    "));
142        } else {
143            _ = dbg_line_buff.push_str("        ");
144        }
145
146        _ = dbg_line_buff.write_fmt(format_args!("{block_addr: >3}   "));
147
148        let mut byte_count = 18;
149        mfrc522
150            .mifare_read(block_addr, &mut buff, &mut byte_count)
151            .await?;
152
153        for (i, buffi) in buff.iter().enumerate().take(16) {
154            _ = dbg_line_buff.write_fmt(format_args!("{:02X} ", buffi));
155            if i % 4 == 3 {
156                _ = dbg_line_buff.push(' ');
157            }
158        }
159
160        if is_sector_trailer {
161            let c1 = buff[7] >> 4;
162            let c2 = buff[8] & 0xF;
163            let c3 = buff[8] >> 4;
164            let c1_ = buff[6] & 0xF;
165            let c2_ = buff[6] >> 4;
166            let c3_ = buff[7] & 0xF;
167
168            inverted_error = (c1 != (!c1_ & 0xF)) || (c2 != (!c2_ & 0xF)) || (c3 != (!c3_ & 0xF));
169            groups[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | (c3 & 1);
170            groups[1] = ((c1 & 2) << 1) | (c2 & 2) | ((c3 & 2) >> 1);
171            groups[2] = (c1 & 4) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2);
172            groups[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3);
173
174            is_sector_trailer = false;
175        }
176
177        let first_in_group;
178        let group;
179
180        if no_of_blocks == 4 {
181            group = block_offset;
182            first_in_group = true;
183        } else {
184            group = block_offset / 5;
185            first_in_group = (group == 3) || (group != (block_offset + 1) / 5);
186        }
187
188        if first_in_group {
189            let (g1, g2, g3) = (
190                (groups[group as usize] >> 2) & 1,
191                (groups[group as usize] >> 1) & 1,
192                groups[group as usize] & 1,
193            );
194
195            _ = core::fmt::write(&mut dbg_line_buff, format_args!("[ {g1} {g2} {g3} ]"));
196            if inverted_error {
197                _ = dbg_line_buff.push_str(" Inverted access bits did not match! ");
198            }
199        }
200
201        if group != 3 && (groups[group as usize] == 1 || groups[group as usize] == 6) {
202            let val = ((buff[3] as i32) << 24)
203                | ((buff[2] as i32) << 16)
204                | ((buff[1] as i32) << 8)
205                | (buff[0] as i32);
206
207            _ = dbg_line_buff.write_fmt(format_args!(" Value=0x{val:X} Adr=0x{:X}", buff[12]));
208        }
209
210        log::debug!("{}", dbg_line_buff);
211    }
212
213    Ok(())
214}
215
216async fn dump_mifare_ultralight<D: MfrcDriver>(
217    mfrc522: &mut MFRC522<D>,
218) -> Result<(), PCDErrorCode> {
219    let mut buff = [0; 18];
220    let mut i;
221
222    log::debug!("Page  0  1  2  3");
223    let mut dbg_line_buff: String<128> = String::new();
224    for page in (0..16).step_by(4) {
225        dbg_line_buff.clear();
226
227        let mut bytes_count = 18;
228        mfrc522
229            .mifare_read(page, &mut buff, &mut bytes_count)
230            .await?;
231
232        for offset in 0..4 {
233            i = page + offset;
234            _ = dbg_line_buff.write_fmt(format_args!(" {i: >2}  "));
235
236            for index in 0..4 {
237                i = 4 * offset + index;
238                _ = dbg_line_buff.write_fmt(format_args!(" {i:02X}  "));
239            }
240        }
241
242        log::debug!("{}", dbg_line_buff);
243    }
244
245    Ok(())
246}