M5Unit-NFC 0.0.3 git rev:59f5362
Loading...
Searching...
No Matches
nfcf.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UNIT_UNIFIED_NFC_NFC_F_NFCF_HPP
11#define M5_UNIT_UNIFIED_NFC_NFC_F_NFCF_HPP
12
13#include "nfc/nfc.hpp"
14#include <cstdint>
15#include <string>
16#include <array>
17
18namespace m5 {
19namespace nfc {
24namespace f {
25
30enum class Type : uint8_t {
31 Unknown,
36 // FeliCaLink, //!< Link
37};
38
40inline m5::nfc::NFCForumTag get_nfc_forum_tag_type(const Type t)
41{
42 return (t != Type::Unknown) ? NFCForumTag::Type3 : NFCForumTag::None;
43}
44
47using Format = uint8_t;
48constexpr Format format_nfcip1{0x0001};
49constexpr Format format_lite{0x0002};
50constexpr Format format_private{0x0004};
51constexpr Format format_ndef{0x0008};
52constexpr Format format_shared{0x0010};
53constexpr Format format_secure{0x0020};
54constexpr Format format_felica_plug{0x0040};
56
61enum class CommandCode : uint8_t {
62 Polling,
63 RequestService = 0x02,
64 RequestResponse = 0x04,
65 ReadWithoutEncryption = 0x06,
66 WriteWithoutEncryption = 0x08,
67 RequestSystemCode = 0x0C,
68};
69
74enum class ResponseCode : uint8_t {
75 Polling = 0x01,
76 RequestService = 0x03,
77 RequestResponse = 0x05,
78 ReadWithoutEncryption = 0x07,
79 WriteWithoutEncryption = 0x09,
80 RequestSystemCode = 0x0D,
81};
82
85constexpr uint16_t system_code_wildcard{0xFFFF};
86constexpr uint16_t system_code_ndef{0x12FC};
87constexpr uint16_t system_code_felica_secure_id{0x957A};
88constexpr uint16_t system_code_shared{0xFE00};
89constexpr uint16_t system_code_lite{0x88B4};
90constexpr uint16_t system_code_felica_plug{0xFEE1};
92
97enum class RequestCode : uint8_t {
98 None,
101};
102
107enum class TimeSlot : uint8_t { Slot1, Slot2, Slot4 = 0x03, Slot8 = 0x07, Slot16 = 0x0F };
108
110inline constexpr uint8_t timeslot_to_slot(const TimeSlot ts)
111{
112 return (ts == TimeSlot::Slot16) ? 16
113 : (ts == TimeSlot::Slot8) ? 8
114 : (ts == TimeSlot::Slot4) ? 4
115 : (ts == TimeSlot::Slot2) ? 2
116 : (ts == TimeSlot::Slot1) ? 1
117 : 0; // Illegal
118}
119
122// Random service
123constexpr uint16_t service_random_read_write_auth{0x0008};
124constexpr uint16_t service_random_read_write{0x0009};
125constexpr uint16_t service_random_read_auth{0x000A};
126constexpr uint16_t service_random_read{0x000B};
127// Cyclic service
128constexpr uint16_t service_cyclic_read_write_auth{0x000C};
129constexpr uint16_t service_cyclic_read_write{0x000D};
130constexpr uint16_t service_cyclic_read_auth{0x000E};
131constexpr uint16_t service_cyclic_read{0x000F};
132// Parse service
133constexpr uint16_t service_parse_direct_auth{0x0100};
134constexpr uint16_t service_parse_direct{0x0101};
135constexpr uint16_t service_parse_cacheback_auth{0x0102};
136constexpr uint16_t service_parse_cacheback{0x0103};
137constexpr uint16_t service_parse_decrement_auth{0x0104};
138constexpr uint16_t service_parse_decrement{0x0105};
139constexpr uint16_t service_parse_increment_auth{0x0106};
140constexpr uint16_t service_parse_increment{0x0107};
142
147struct block_t {
148 uint8_t pad{};
149 uint8_t header{};
150 uint16_t number{};
151
152 inline constexpr block_t() : block_t(0)
153 {
154 }
155
156 // Allow implicit type conversion
157 inline constexpr block_t(const uint16_t num, const uint8_t access = 0, const uint8_t order = 0)
158 : header{(uint8_t)(((num > 0xFF) ? 0x00 : 0x80) | ((access & 0x07) << 4) | (order & 0x0F))}, number{num}
159 {
160 }
161
162 static block_t from(const uint8_t v[3])
163 {
164 block_t b{0xFFFFu};
165 if (v) {
166 b.header = v[0];
167 b.number = v[1];
168 if (b.is_3byte()) {
169 b.number |= (uint16_t)v[2] << 8;
170 }
171 }
172 return b;
173 }
174
175 inline constexpr bool is_2byte() const
176 {
177 return (header & 0x80) != 0;
178 }
179 inline constexpr bool is_3byte() const
180 {
181 return (header & 0x80) == 0;
182 }
183 inline constexpr uint8_t access_mode() const
184 {
185 return (header >> 4) & 0x07;
186 }
187 inline constexpr uint8_t order() const
188 {
189 return (header & 0x0F);
190 }
191 inline constexpr uint16_t block() const
192 {
193 return number;
194 }
195
196 inline void block(const uint16_t num)
197 {
198 number = num;
199 header = (header & ~0x80) | (num > 0xFF ? 0x00 : 0x80);
200 }
201 inline void access_mode(const uint8_t a)
202 {
203 header = (header & ~(0x07 << 4)) | ((a & 0x07) << 4);
204 }
205 inline void order(const uint8_t o)
206 {
207 header = (header & ~0x0F) | (o & 0x0F);
208 }
209
210 inline operator uint16_t() const
211 {
212 return block();
213 }
214
215 uint8_t store(uint8_t buf[3]) const
216 {
217 uint8_t idx{};
218 buf[idx++] = header;
219 buf[idx++] = number & 0xFF;
220 if (is_3byte()) {
221 buf[idx++] = number >> 8;
222 }
223 return idx;
224 }
225};
226
231namespace standard {
236enum class Mode : uint8_t {
237 Mode0,
238 Mode1,
239 Mode2,
240 Mode3,
241
242};
243} // namespace standard
244
249namespace lite {
251
254constexpr block_t S_PAD0{0x00};
255constexpr block_t S_PAD1{0x01};
256constexpr block_t S_PAD2{0x02};
257constexpr block_t S_PAD3{0x03};
258constexpr block_t S_PAD4{0x04};
259constexpr block_t S_PAD5{0x05};
260constexpr block_t S_PAD6{0x06};
261constexpr block_t S_PAD7{0x07};
262constexpr block_t S_PAD8{0x08};
263constexpr block_t S_PAD9{0x09};
264constexpr block_t S_PAD10{0x0A};
265constexpr block_t S_PAD11{0x0B};
266constexpr block_t S_PAD12{0X0C};
267constexpr block_t S_PAD13{0x0D};
268constexpr block_t REG{0x0E};
269constexpr block_t RC{0x80};
270constexpr block_t MAC{0x81};
271constexpr block_t ID{0x082};
272constexpr block_t D_ID{0x83};
273constexpr block_t SER_C{0x84};
274constexpr block_t SYS_C{0x85};
275constexpr block_t CKV{0x86};
276constexpr block_t CK{0x87};
277constexpr block_t MC{0x88};
279
280} // namespace lite
281
286namespace lite_s {
287
292enum class Mode : uint8_t {
293 Mode00,
294 Mode01,
295 Mode10,
296 Mode11,
297};
298
301constexpr block_t S_PAD0{0x00}; // Same as Lite
302constexpr block_t S_PAD1{0x01}; // Same as Lite
303constexpr block_t S_PAD2{0x02}; // Same as Lite
304constexpr block_t S_PAD3{0x03}; // Same as Lite
305constexpr block_t S_PAD4{0x04}; // Same as Lite
306constexpr block_t S_PAD5{0x05}; // Same as Lite
307constexpr block_t S_PAD6{0x06}; // Same as Lite
308constexpr block_t S_PAD7{0x07}; // Same as Lite
309constexpr block_t S_PAD8{0x08}; // Same as Lite
310constexpr block_t S_PAD9{0x09}; // Same as Lite
311constexpr block_t S_PAD10{0x0A}; // Same as Lite
312constexpr block_t S_PAD11{0x0B}; // Same as Lite
313constexpr block_t S_PAD12{0X0C}; // Same as Lite
314constexpr block_t S_PAD13{0x0D}; // Same as Lite
315constexpr block_t REG{0x0E}; // Same as Lite
316constexpr block_t RC{0x80}; // Same as Lite
317constexpr block_t MAC{0x81}; // Same as Lite
318constexpr block_t ID{0x082}; // Same as Lite
319constexpr block_t D_ID{0x83}; // Same as Lite
320constexpr block_t SER_C{0x84}; // Same as Lite
321constexpr block_t SYS_C{0x85}; // Same as Lite
322constexpr block_t CKV{0x86}; // Same as Lite
323constexpr block_t CK{0x87}; // Same as Lite
324constexpr block_t MC{0x88}; // Same as Lite
325constexpr block_t WCNT{0x90};
326constexpr block_t MAC_A{0x91};
327constexpr block_t STATE{0x92};
328constexpr block_t CRC_CHECK{0xA0};
330
331} // namespace lite_s
332
334uint16_t get_maximum_block(const Type t);
336uint16_t get_number_of_user_blocks(const Type t);
338inline uint16_t get_user_area_size(const Type t)
339{
340 return 16 * get_number_of_user_blocks(t);
341}
343uint16_t get_first_user_block(const Type t);
345uint16_t get_last_user_block(const Type t);
347inline bool is_user_block(const Type t, const uint16_t block)
348{
349 return (block >= get_first_user_block(t)) && (block <= get_last_user_block(t));
350}
352uint8_t get_maximum_read_blocks(const Type t);
354uint8_t get_maximum_write_blocks(const Type t);
355
357bool is_read_only_lite(const block_t block);
359bool is_read_only_lite_s(const block_t block);
361bool can_read_lite(const block_t block);
363bool can_read_lite_s(const block_t block);
364
367constexpr uint16_t NODE_SYSTEM_KEY{0xFFFF};
368constexpr uint16_t KEY_VERIOSN_NONE{0xFFFF};
370
371constexpr uint8_t FELICA_ID_LENGTH{8};
372constexpr uint8_t FELICA_MAX_BLOCKS{8};
373constexpr uint16_t FELICA_MAX_PACKET_LENGTH_REQUEST_SERVICE{1 + 8 + 1 + 2 * 255};
374constexpr uint16_t FELICA_MAX_PACKET_LENGTH_REQUEST_RESPONSE{1 + 8 + 1};
375constexpr uint16_t FELICA_MAX_PACKET_LENGTH_REQUEST_SYSTEM_CODE{1 + 8 + 1 + 2 * 255};
376
381struct PICC {
382 union {
383 uint8_t m[16]{};
384 struct {
385 uint8_t idm[FELICA_ID_LENGTH];
386 uint8_t pmm[FELICA_ID_LENGTH];
387 };
388 };
389 uint16_t request_data{};
393 uint8_t _pad{}; // padding
394 uint16_t dfc_format{};
395 uint16_t emulation_sc{};
396
398 bool valid() const;
400 bool validEmulation() const;
401
403 inline uint16_t userAreaSize() const
404 {
405 return valid() ? get_user_area_size(type) : 0;
406 }
408 inline uint16_t firstUserBlock() const
409 {
410 return valid() ? get_first_user_block(type) : 0xFFFF;
411 }
413 inline uint16_t lastUserBlock() const
414 {
415 return valid() ? get_last_user_block(type) : 0xFFFF;
416 }
418 inline bool isUserBlock(const block_t block) const
419 {
420 return is_user_block(type, block);
421 }
422
424 inline uint8_t maximumReadBlocks() const
425 {
427 }
429 inline uint8_t maximumWriteBlocks() const
430 {
432 }
433
435 inline bool checkFormat(const Format f) const
436 {
437 return (format & f) != 0;
438 }
440 inline bool supportsNDEF() const
441 {
442 return checkFormat(format_ndef);
443 }
444
447 {
448 return get_nfc_forum_tag_type(type);
449 }
450
459 bool emulate(const Type t, const uint8_t idm[FELICA_ID_LENGTH], const uint8_t pmm[FELICA_ID_LENGTH],
460 const uint16_t sc = 0);
461
463 std::string idmAsString() const;
465 std::string pmmAsString() const;
467 std::string typeAsString() const;
468};
469
471bool operator==(const PICC& a, const PICC& b);
473inline bool operator!=(const PICC& a, const PICC& b)
474{
475 return !(a == b);
476}
477
480// constexpr uint32_t TIMEOUT_POLLING{3};
481constexpr uint32_t TIMEOUT_POLLING{5};
482constexpr uint32_t TIMEOUT_POLLING_PICC{2}; // 2 ms per PICC
483
488union REG {
489 uint8_t reg[16]{};
490 struct {
491 uint8_t reg_a[4]; // RegA (LE)
492 uint8_t reg_b[4]; // RegB (LE)
493 uint8_t reg_c[8]; // RegC (BE)
494 } __attribute__((packed));
495
496 REG()
497 {
498 }
499
501 inline uint32_t regA() const
502 {
503 return ((uint32_t)reg_a[3] << 24) | ((uint32_t)reg_a[2] << 16) | ((uint32_t)reg_a[1] << 8) |
504 ((uint32_t)reg_a[0]);
505 }
507 inline uint32_t regB() const
508 {
509 return ((uint32_t)reg_b[3] << 24) | ((uint32_t)reg_b[2] << 16) | ((uint32_t)reg_b[1] << 8) |
510 ((uint32_t)reg_b[0]);
511 }
513 inline uint64_t regC() const
514 {
515 return ((uint64_t)reg_c[0] << 56) | ((uint64_t)reg_c[1] << 48) | ((uint64_t)reg_c[2] << 40) |
516 ((uint64_t)reg_c[3] << 32) | ((uint64_t)reg_c[4] << 24) | ((uint64_t)reg_c[5] << 16) |
517 ((uint64_t)reg_c[6] << 8) | ((uint64_t)reg_c[7]);
518 }
520 void regA(const uint32_t v)
521 {
522 reg_a[0] = v & 0xFF;
523 reg_a[1] = v >> 8;
524 reg_a[2] = v >> 16;
525 reg_a[3] = v >> 24;
526 }
528 void regB(const uint32_t v)
529 {
530 reg_b[0] = v & 0xFF;
531 reg_b[1] = v >> 8;
532 reg_b[2] = v >> 16;
533 reg_b[3] = v >> 24;
534 }
536 void regC(const uint64_t v)
537 {
538 reg_c[7] = v & 0xFF;
539 reg_c[6] = v >> 8;
540 reg_c[5] = v >> 16;
541 reg_c[4] = v >> 24;
542 reg_c[3] = v >> 32;
543 reg_c[2] = v >> 40;
544 reg_c[1] = v >> 48;
545 reg_c[0] = v >> 56;
546 }
547
548 inline operator const uint8_t*() const
549 {
550 return reg;
551 }
552 inline operator uint8_t*()
553 {
554 return reg;
555 }
556} __attribute__((packed));
557
564inline bool can_write_reg(const REG& o, const REG& n)
565{
566 return (o.regA() >= n.regA()) && (o.regB() >= n.regB());
567}
568
571
578bool make_session_key(uint8_t sk[16], const uint8_t ck[16], const uint8_t rc[16]);
579
592bool generate_mac(uint8_t mac[8], const uint8_t* plain, uint32_t plain_len, const uint8_t* block_data,
593 uint32_t block_len, const uint8_t sk1[8], const uint8_t sk2[8], const uint8_t rc[16]);
594
602bool make_personalized_card_key_lite_s(uint8_t card_key[16], const uint8_t master_key[24], const uint8_t id_block[16]);
603
605
606} // namespace f
607} // namespace nfc
608} // namespace m5
609#endif
NFC-A definitions.
NFC-B definitions.
NFC-F definitions.
Mode
Mode for LiteS.
Definition nfcf.hpp:292
@ Mode11
External authentication complete, polling response not possible.
@ Mode10
External authentication complete, polling response possible.
@ Mode00
External authentication incomplete, polling response possible.
@ Mode01
External authentication incomplete, polling response not possible.
Top level namespace of M5stack.
NFC related definitions.
For FeliCa Standard.
NFC-V definitions.
NFC definitions.
NFCForumTag
NFC Forum Tag Type.
Definition nfc.hpp:39
uint16_t get_number_of_user_blocks(const Type t)
Gets the number of user blocks.
Definition nfca.cpp:327
uint8_t get_maximum_write_blocks(const Type t)
Maximum number of blocks that can be write simultaneously.
Definition nfcf.cpp:112
uint16_t get_maximum_block(const Type t)
Gets the maximum block.
Definition nfcf.cpp:80
uint8_t get_maximum_read_blocks(const Type t)
Maximum number of blocks that can be read simultaneously.
Definition nfcf.cpp:106
constexpr uint32_t TIMEOUT_POLLING
Definition nfcf.hpp:481
TimeSlot
Timeslot value for Polling.
Definition nfcf.hpp:107
constexpr uint16_t service_parse_cacheback_auth
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:135
constexpr uint16_t service_random_read_write
Random,Read/write,No authentication required (S, L, LS)
Definition nfcf.hpp:124
constexpr Format format_private
Has private area.
Definition nfcf.hpp:50
constexpr uint16_t system_code_shared
Shared area.
Definition nfcf.hpp:88
constexpr Format format_shared
Has shared area.
Definition nfcf.hpp:52
constexpr uint16_t service_parse_cacheback
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:136
constexpr uint16_t service_parse_decrement_auth
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:137
ResponseCode
NFC-F Response code.
Definition nfcf.hpp:74
constexpr uint16_t service_random_read_write_auth
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:123
constexpr uint16_t system_code_felica_plug
FeliCa Plug.
Definition nfcf.hpp:90
constexpr uint32_t TIMEOUT_POLLING_PICC
Definition nfcf.hpp:482
constexpr uint16_t service_parse_increment_auth
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:139
constexpr uint16_t service_random_read_auth
Random,Read only,No authentication required (S)
Definition nfcf.hpp:125
CommandCode
NFC-F Command code.
Definition nfcf.hpp:61
bool can_write_reg(const REG &o, const REG &n)
Definition nfcf.hpp:564
Mode
Mode for Standard.
Definition nfcf.hpp:236
@ Mode2
After mutual authentication is complete (Auth2)
@ Mode0
Power was supplied to the PICC.
@ Mode1
Certification for PICC has been completed (Auth1)
@ Mode3
After registering area services or executing system partitioning.
Type
Type of the PICC.
Definition nfcf.hpp:30
@ FeliCaLiteS
Lite-S.
@ FeliCaStandard
Standard.
constexpr Format format_felica_plug
FeliCa Plug.
Definition nfcf.hpp:54
constexpr uint16_t service_cyclic_read_write_auth
Cyclic,Read/write,Authentication required(S)
Definition nfcf.hpp:128
constexpr uint16_t service_parse_direct_auth
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:133
constexpr uint16_t KEY_VERIOSN_NONE
No key version exists.
Definition nfcf.hpp:368
constexpr uint8_t timeslot_to_slot(const TimeSlot ts)
TimeSlot to the number of the slot.
Definition nfcf.hpp:110
constexpr uint16_t service_parse_direct
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:134
constexpr Format format_secure
FeliCa Secure ID.
Definition nfcf.hpp:53
constexpr uint16_t service_random_read
Random,Read only,No authentication required (S,LS)
Definition nfcf.hpp:126
constexpr Format format_nfcip1
Support ISO/IEC18092.
Definition nfcf.hpp:48
RequestCode
Request code for Polling.
Definition nfcf.hpp:97
@ SystemCode
Request system code.
@ CommunicationPerformance
Request communication performance.
uint8_t Format
Support ISO/IEC18092.
Definition nfcf.hpp:47
constexpr uint16_t system_code_ndef
NDEF.
Definition nfcf.hpp:86
constexpr uint16_t service_cyclic_read_auth
Cyclic,Read only,Authentication required(S)
Definition nfcf.hpp:130
constexpr Format format_ndef
Support NDEF.
Definition nfcf.hpp:51
constexpr uint16_t service_cyclic_read_write
Cyclic,Read/write,No athentication required(S)
Definition nfcf.hpp:129
constexpr uint16_t system_code_felica_secure_id
FeliCa secure ID.
Definition nfcf.hpp:87
constexpr uint16_t service_cyclic_read
Cyclic,Read only,No authentication required(S)
Definition nfcf.hpp:131
constexpr uint16_t service_parse_decrement
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:138
constexpr uint16_t system_code_wildcard
Wildcard.
Definition nfcf.hpp:85
constexpr uint16_t NODE_SYSTEM_KEY
Retrieving the System Key Version.
Definition nfcf.hpp:367
constexpr uint16_t system_code_lite
Lite, Lite-S.
Definition nfcf.hpp:89
constexpr uint16_t service_parse_increment
Random,Read/write,Authentication required (S)
Definition nfcf.hpp:140
constexpr Format format_lite
Lite or Lite-S.
Definition nfcf.hpp:49
PICC information for NFC-F.
Definition nfcf.hpp:381
NFCForumTag nfcForumTagType() const
NFC ForumTag.
Definition nfcf.hpp:446
uint16_t userAreaSize() const
Total user area size.
Definition nfcf.hpp:403
std::string typeAsString() const
Gets the type string.
Definition nfcf.cpp:333
bool supportsNDEF() const
Supports NDEF?
Definition nfcf.hpp:440
uint16_t dfc_format
DFC format ID:0x82[8,9] BE if format include DFC.
Definition nfcf.hpp:394
bool validEmulation() const
Valid for emulation?
Definition nfcf.cpp:317
Format format
Format type group bits.
Definition nfcf.hpp:392
uint16_t lastUserBlock() const
Gets the last user block.
Definition nfcf.hpp:413
uint8_t idm[FELICA_ID_LENGTH]
Manufacture ID.
Definition nfcf.hpp:385
RequestCode request_code
Tyepe of the request_data.
Definition nfcf.hpp:390
std::string pmmAsString() const
Gets the PMm string.
Definition nfcf.cpp:328
bool checkFormat(const Format f) const
Check format.
Definition nfcf.hpp:435
uint8_t pmm[FELICA_ID_LENGTH]
Manufacture Parameter.
Definition nfcf.hpp:386
uint8_t maximumWriteBlocks() const
Maximum number of blocks that can be write simultaneously.
Definition nfcf.hpp:429
bool emulate(const Type t, const uint8_t idm[FELICA_ID_LENGTH], const uint8_t pmm[FELICA_ID_LENGTH], const uint16_t sc=0)
Emulation settings.
Definition nfcf.cpp:339
uint16_t request_data
Any request data if exists.
Definition nfcf.hpp:389
uint16_t firstUserBlock() const
Gets the first user block.
Definition nfcf.hpp:408
Type type
PICC Type.
Definition nfcf.hpp:391
uint8_t maximumReadBlocks() const
Maximum number of blocks that can be read simultaneously.
Definition nfcf.hpp:424
bool isUserBlock(const block_t block) const
Is user block?
Definition nfcf.hpp:418
std::string idmAsString() const
Gets the IDm string.
Definition nfcf.cpp:323
uint16_t emulation_sc
System code for emulation.
Definition nfcf.hpp:395
bool valid() const
Valid for detection?
Definition nfcf.cpp:310
Block list element.
Definition nfcf.hpp:147
uint16_t number
block number (using low byte if 2 byte mode)
Definition nfcf.hpp:150
uint8_t header
size:1 access:3 order:4
Definition nfcf.hpp:149
Subtract Register Block Data.
Definition nfcf.hpp:488
uint64_t regC() const
Gets the RegC.
Definition nfcf.hpp:513
void regC(const uint64_t v)
Set the RegC.
Definition nfcf.hpp:536
void regB(const uint32_t v)
Set the RegB.
Definition nfcf.hpp:528
void regA(const uint32_t v)
Set the RegA.
Definition nfcf.hpp:520
uint32_t regA() const
Gets the RegA.
Definition nfcf.hpp:501
uint32_t regB() const
Gets the RegB.
Definition nfcf.hpp:507