1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
//! Pn532 Requests

/// Pn532 Request consisting of a [`Command`] and extra command data
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Request<const N: usize> {
    pub command: Command,
    pub data: [u8; N],
}

/// Pn532 Request consisting of a [`Command`] and a reference to extra command data
pub struct BorrowedRequest<'a> {
    pub command: Command,
    pub data: &'a [u8],
}

impl<'a> BorrowedRequest<'a> {
    #[inline]
    pub const fn new(command: Command, data: &'a [u8]) -> Self {
        Self {command, data}
    }
}

impl<'a, const N: usize> Into<BorrowedRequest<'a>> for &'a Request<N> {
    fn into(self) -> BorrowedRequest<'a> {
        BorrowedRequest::new(self.command, &self.data)
    }
}

impl<const N: usize> Request<N> {
    #[inline]
    pub const fn new(command: Command, data: [u8; N]) -> Self {
        Request { command, data }
    }
}

impl Request<0> {
    pub const GET_FIRMWARE_VERSION: Request<0> = Request::new(Command::GetFirmwareVersion, []);
    pub const INLIST_ONE_ISO_A_TARGET: Request<2> =
        Request::new(Command::InListPassiveTarget, [1, CardType::IsoTypeA as u8]);

    pub const SELECT_TAG_1: Request<1> = Request::new(Command::InSelect, [1]);
    pub const SELECT_TAG_2: Request<1> = Request::new(Command::InSelect, [2]);
    pub const DESELECT_TAG_1: Request<1> = Request::new(Command::InDeselect, [1]);
    pub const DESELECT_TAG_2: Request<1> = Request::new(Command::InDeselect, [2]);
    pub const RELEASE_TAG_1: Request<1> = Request::new(Command::InRelease, [1]);
    pub const RELEASE_TAG_2: Request<1> = Request::new(Command::InRelease, [2]);

    pub const fn sam_configuration(mode: SAMMode, use_irq_pin: bool) -> Request<3> {
        // TODO use_irq_pin seems to not have any effect
        let (mode, timeout) = match mode {
            SAMMode::Normal => (1, 0),
            SAMMode::VirtualCard { timeout } => (2, timeout),
            SAMMode::WiredCard => (3, 0),
            SAMMode::DualCard => (4, 0),
        };
        Request::new(
            Command::SAMConfiguration,
            [mode, timeout, use_irq_pin as u8],
        )
    }

    pub const fn rf_regulation_test(tx_speed: TxSpeed, tx_framing: TxFraming) -> Request<1> {
        Request::new(
            Command::RFRegulationTest,
            [tx_speed as u8 | tx_framing as u8],
        )
    }

    pub const fn ntag_read(page: u8) -> Request<3> {
        Request::new(
            Command::InDataExchange,
            [0x01, NTAGCommand::Read as u8, page],
        )
    }
    pub const fn ntag_write(page: u8, bytes: &[u8; 4]) -> Request<7> {
        Request::new(
            Command::InDataExchange,
            [
                0x01,
                NTAGCommand::Write as u8,
                page,
                bytes[0],
                bytes[1],
                bytes[2],
                bytes[3],
            ],
        )
    }
    pub const fn ntag_pwd_auth(bytes: &[u8; 4]) -> Request<5> {
        Request::new(
            Command::InCommunicateThru,
            [
                NTAGCommand::PwdAuth as u8,
                bytes[0],
                bytes[1],
                bytes[2],
                bytes[3],
            ],
        )
    }
}

/// Commands supported by the Pn532
///
/// These commands are fully described in the section 7 of the User Manual:
/// <https://www.nxp.com/docs/en/user-guide/141520.pdf>
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u8)]
pub enum Command {
    /// This command is used for self-diagnosis. Processing time of this command varies depending
    /// on the content of the processing.
    ///
    /// For more information, see 7.2.1 Diagnose
    Diagnose = 0x00,
    /// This command is used to get the version of the embedded firmware from PN532.
    ///
    /// For more information, see 7.2.2 GetFirmwareVersion
    GetFirmwareVersion = 0x02,
    /// This command allows the host controller to know at a given moment the complete situation of
    /// the PN532.
    ///
    /// For more information, see 7.2.3 GetGeneralStatus
    GetGeneralStatus = 0x04,
    /// This command is used to read the content of one or several internal registers of the PN532.
    ///
    /// For more information, see 7.2.4 ReadRegister
    ReadRegister = 0x06,
    /// This command is used to overwrite the content of one or several internal registers of the
    /// PN532.
    ///
    /// For more information, see 7.2.5 WriteRegister
    WriteRegister = 0x08,
    /// Tells the PN532 to read the value for each GPIO port and return the information to the host
    /// controller.
    ///
    /// For more information, see 7.2.6 ReadGPIO
    ReadGPIO = 0x0C,
    /// Tells the PN532 to apply the value for each port specified by the host controller.
    ///
    /// For more information, see 7.2.7 WriteGPIO
    WriteGPIO = 0x0E,
    /// Selects the baud rate on the serial link between the host controller and the PN532.
    ///
    /// For more information, see 7.2.8 SetSerialBaudRate
    SetSerialBaudRate = 0x10,
    /// This command is used to set internal parameters of the PN532, and then to configure its
    /// behavior regarding different cases.
    ///
    /// For more information, see 7.2.9 SetParameters
    SetParameters = 0x12,
    /// This command is used to select the data flow path by configuring the internal serial data
    /// switch.
    ///
    /// For more information, see 7.2.10 SAMConfiguration
    SAMConfiguration = 0x14,
    /// This command can be used to put the PN532 into Power Down mode in order to save power
    /// consumption.
    ///
    /// For more information, see 7.2.11 PowerDown
    PowerDown = 0x16,
    /// This command is used to configure the different settings of the PN532.
    ///
    /// For more information, see 7.3.1 RFConfiguration
    RFConfiguration = 0x32,
    /// This command is used for radio regulation test.
    ///
    /// For more information, see 7.3.2 RFRegulationTest
    RFRegulationTest = 0x58,
    /// This command is used by a host controller to activate a target using either active or
    /// passive communication mode during communication over DEP protocol.
    ///
    /// For more information, see 7.3.3 InJumpForDEP
    InJumpForDEP = 0x56,
    /// This command is used by a host controller to activate a target using either active or
    /// passive communication mode during communication over PSL or DEP protocols.
    ///
    /// For more information, see 7.3.4 InJumpForPSL
    InJumpForPSL = 0x46,
    /// This command tells PN532 to detect as many targets as possible in passive mode.
    ///
    /// For more information, see 7.3.5 InListPassiveTarget
    InListPassiveTarget = 0x4A,
    /// This command is used by a host controller to launch an activation of a target in case of
    /// passive mode.
    ///
    /// For more information, see 7.3.6 InATR
    InATR = 0x50,
    /// This command is used by a host controller to change the defined bit rates either with a TPE
    /// target or with a ISO/IEC14443-4 target.
    ///
    /// For more information, see 7.3.7 InPSL
    InPSL = 0x4E,
    /// This command is used to support protocol data exchanges between the PN532 as initiator and
    /// a target.
    ///
    /// For more information, see 7.3.8 InDataExchange
    InDataExchange = 0x40,
    /// This command is used to support basic data exchanges between the PN532 and a target.
    ///
    /// For more information, see 7.3.9 InCommunicateThru
    InCommunicateThru = 0x42,
    /// Command to deselect specified targets(s).
    ///
    /// For more information, see 7.3.10 InDeselect
    InDeselect = 0x44,
    /// Command to release the specified target(s).
    ///
    /// For more information, see 7.3.11 InRelease
    InRelease = 0x52,
    /// Command to select the specified target.
    ///
    /// For more information, see 7.3.12 InSelect
    InSelect = 0x54,
    /// This command is used to poll card(s) / target(s) of specified Type present in the RF field.
    ///
    /// For more information, see 7.3.13 InAutoPoll
    InAutoPoll = 0x60,
    /// The host controller uses this command to configure the PN532 as target.
    ///
    /// For more information, see 7.3.14 TgInitAsTarget
    TgInitAsTarget = 0x8C,
    /// This command is used to give General Bytes to the PN532.
    ///
    /// For more information, see 7.3.15 TgSetGeneralBytes
    TgSetGeneralBytes = 0x92,
    /// This command allows the host controller to get back the data received by the PN532 from its
    /// initiator.
    ///
    /// For more information, see 7.3.16 TgGetData
    TgGetData = 0x86,
    /// This command allows the host controller to spully PN532 with teh data that it wants to send
    /// back to teh initiator.
    ///
    /// For more information, see 7.3.17 TgSetData
    TgSetData = 0x8E,
    /// This command is used if the overall amount of data to be sent cannot be transmitted in one
    /// frame.
    ///
    /// For more information, see 7.3.18 TgSetMetaData
    TgSetMetaData = 0x94,
    /// This command is used to get a packet of data from an initiator and to send it back to the
    /// host controller.
    ///
    /// For more information, see 7.3.19 TgGetInitiatorCommand
    TgGetInitiatorCommand = 0x88,
    /// This command is used to send a response packet of data to an initiator.
    ///
    /// For more information, see 7.3.20 TgResponseToInitiator
    TgResponseToInitiator = 0x90,
    /// This command is used by the host controller to know what the current state of the PN532 is.
    ///
    /// For more information, see 7.3.21 TgGetTargetStatus
    TgGetTargetStatus = 0x8A,
}

/// SAM mode parameter to be used in [`Command::SAMConfiguration`]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum SAMMode {
    /// The SAM is not used; this is the default mode
    Normal,
    /// The couple PN532+SAM is seen as only one contactless SAM card
    /// from the external world
    VirtualCard {
        /// In multiples of 50ms
        timeout: u8,
    },
    /// The host controller can access to the SAM with standard PCD commands
    /// (InListPassiveTarget, InDataExchange, ...)
    WiredCard,
    /// Both the PN532 and the SAM are visible from the external world
    /// as two separated targets
    DualCard,
}

/// Card type parameter to be used in [`Command::InListPassiveTarget`]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u8)]
pub enum CardType {
    /// 106 kbps type A (ISO/IEC14443 Type A)
    IsoTypeA = 0x00,
    /// 212 kbps (FeliCa polling)
    FeliCa212kbps = 0x01,
    /// 424 kbps (FeliCa polling)
    FeliCa424kbps = 0x02,
    /// 106 kbps type B (ISO/IEC14443-3B)
    IsoTypeB = 0x03,
    /// 106 kbps Innovision Jewel tag
    Jewel = 0x04,
}

/// Bitrate to be used in [`Command::RFRegulationTest`]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u8)]
pub enum TxSpeed {
    /// 106 kbps
    Tx106kbps = 0b0000_0000,
    /// 212 kbps
    Tx212kbps = 0b0001_0000,
    /// 424 kbps
    Tx424kbps = 0b0010_0000,
    /// 848 kbps
    Tx848kbps = 0b0011_0000,
}

/// Type of modulation to be used in [`Command::RFRegulationTest`]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u8)]
pub enum TxFraming {
    Mifare = 0b0000_0000,
    FeliCa = 0b0000_0010,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u8)]
pub enum NTAGCommand {
    GetVersion = 0x60,
    Read = 0x30,
    FastRead = 0x3A,
    Write = 0xA2,
    CompWrite = 0xA0,
    ReadCnt = 0x39,
    PwdAuth = 0x1B,
    ReadSig = 0x3C,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u8)]
pub enum MifareCommand {
    AuthenticationWithKeyA = 0x60,
    AuthenticationWithKeyB = 0x61,
    PersonalizeUIDUsage = 0x40,
    SetModType = 0x43,
    Read = 0x30,
    Write = 0xA0,
    Decrement = 0xC0,
    Increment = 0xC1,
    Restore = 0xC2,
    Transfer = 0xB0,
}