Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce NFC driver with RFAL middleware #4566

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

kopecdav
Copy link

@kopecdav kopecdav commented Jan 31, 2025

This PR Introduce NFC driver with RFAL middleware to control ST25R3916.

RFAL is a ST middleware which provides a low level control of the ST25R3916 + an extra layer to support several different NFC standards.

PR also introduce prodtests to READ, EMULATE and WRITE to NFC card.

@kopecdav kopecdav added the T3W1 label Jan 31, 2025
@kopecdav kopecdav requested review from TychoVrahe and cepetr January 31, 2025 12:42
@kopecdav kopecdav self-assigned this Jan 31, 2025
@kopecdav kopecdav requested a review from prusnak as a code owner January 31, 2025 12:42
@TychoVrahe TychoVrahe removed the request for review from prusnak January 31, 2025 15:06
@kopecdav kopecdav force-pushed the kopecdav/T3W1/NFC_driver branch from 2e6bf04 to 4c189eb Compare January 31, 2025 15:47
Copy link

github-actions bot commented Jan 31, 2025

core UI changes device test click test persistence test
T2T1 Model T test(screens) main(screens) test(screens) main(screens) test(screens) main(screens)
T3B1 Safe 3 test(screens) main(screens) test(screens) main(screens) test(screens) main(screens)
T3T1 Safe 5 test(screens) main(screens) test(screens) main(screens) test(screens) main(screens)
All main(screens)

@kopecdav kopecdav force-pushed the kopecdav/T3W1/NFC_driver branch from 4c189eb to 606d8c9 Compare February 3, 2025 08:56
core/embed/io/nfc/inc/io/nfc.h Outdated Show resolved Hide resolved
core/embed/io/nfc/inc/io/nfc.h Outdated Show resolved Hide resolved
core/embed/io/nfc/inc/io/nfc.h Outdated Show resolved Hide resolved
core/embed/io/nfc/inc/io/nfc.h Outdated Show resolved Hide resolved
core/embed/io/nfc/inc/io/nfc.h Outdated Show resolved Hide resolved
core/embed/io/nfc/st25r3916b/nfc.c Outdated Show resolved Hide resolved
core/embed/io/nfc/st25r3916b/nfc.c Outdated Show resolved Hide resolved
core/embed/io/nfc/st25r3916b/nfc.c Outdated Show resolved Hide resolved
core/embed/io/nfc/st25r3916b/ndef.c Outdated Show resolved Hide resolved
core/embed/io/nfc/inc/io/nfc.h Outdated Show resolved Hide resolved
core/embed/io/nfc/st25r3916b/nfc.c Outdated Show resolved Hide resolved

#include <sys/irq.h>
#include <sys/systick.h>
#include <trezor_bsp.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no strict rule on how to order #include statements, but in new code, we follow this convention:

#include <trezor_xxx.h>.  <-- comes first
// empty line
#include <module/header.h> <-- comes next
// empty line
#include "local_header.h" <--- comes last

There is no strict rule on how to order #include statements, but in new code, we follow this convention:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope to fix correctly in d0ecd9f

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe #include <io/nfc.h> belongs to the second category. Otherwise it looks good.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

*/
0x00, 0x00}; /* RD */

static ReturnCode nfc_transcieve_blocking(uint8_t *txBuf, uint16_t txBufSize,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In new code, we prefer camelCase over snake_case. camelCase is also used in other parts of the file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait do we? i am confused now, i believe snake_case is almost exclusively used in our C code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I flipped it:-)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the return type to the nfc_status_t but in rest of the code I have everything in snake_case i think. Unfortunatelly the RFAL is written in camelCase so the code looks bit wierd :/. But I dont think we can do anything about it.

core/embed/io/nfc/st25r3916b/ndef.h Outdated Show resolved Hide resolved
core/embed/projects/prodtest/cmd/prodtest_nfc.c Outdated Show resolved Hide resolved
return NFC_ERROR;
}

char *uid_str = hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider replacing hex2Str with cstr_encode_hex from <rtl/strutils.>

Copy link
Author

@kopecdav kopecdav Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didnt know we already have it 😮 , fixed d0ecd9f but not tested yet, please leave open

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you couldn't have known. It's only been there for a few days.

core/embed/io/nfc/st25r3916b/nfc.c Outdated Show resolved Hide resolved
core/embed/io/nfc/st25r3916b/nfc.c Outdated Show resolved Hide resolved

uint32_t nfc_create_timer(uint16_t time) { return (systick_ms() + time); }

bool nfc_timer_is_expired(uint32_t timer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see ticks_expired() in sys/systick.h

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in d0ecd9f

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you don't need these functions at all. Why not use systick_xxxz in rfal_platform.h` directly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, fixed in bd5de3a

@kopecdav kopecdav force-pushed the kopecdav/T3W1/NFC_driver branch from 09828c9 to d0ecd9f Compare February 6, 2025 19:17

#pragma once

#include "trezor_types.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should include this header using <...> instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

#include "rfal_platform.h"

#define LM_SEL_RES \
0x20U /*!<NFC-A SEL_RES configured for Type 4A Tag Platform */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our clang-format doesn’t handle this type of comment well. How about placing it on its own line before the definition, like this:

// NFC-A SEL_RES configured for Type 4A Tag Platform
#define LM_SEL_RES 0x20

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

#define LM_SC_BYTE2 \
0xFCU /*!<NFC-F System Code byte 2 */
#define LM_PAD0 \
0x00U /*!<NFC-F PAD0 */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though there's nothing inherently wrong with multiline comments, we don’t use them often and usually prefer single-line comments (except for the license header). These comments also contain Doxygen inline annotation syntax /*!<...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

cli_trace(cli, "NFC Type AP2P: UID: %s", dev_info.uid);
break;
case NFC_DEV_TYPE_UNKNOWN:
cli_error(cli, CLI_ERROR, "NFC Type UNKNOWN");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of this error, both cli_error() and cli_error() are called.

Copy link
Author

@kopecdav kopecdav Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean cli_error() and cli cli_ok() right? changed break -> cleanup
fixed in bd5de3a


nfc_deinit();

cli_ok(cli, "");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nfc_deinit() is not always called. How about this:

  ...
  cli_ok(cli, "");
  
cleanup:
  nfc_deinit();
}

then, instead of return, you can use goto cleanup

The similar issue exists in prodtest_nfc_emulate_card and prodtest_nfc_write_card

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

if (*buffer == 0xFF) {
// TLV 3 byte length format
buffer++;
message->message_total_len = (int16_t)(buffer[0] << 8 | buffer[1]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the message is corrupted, message_total_len might be initialized to a nonsense value. I think we should check whether message_total_len fits within the buffer passed to the function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

}

if (rec->type_length > 0) {
memcpy(&(rec->type), buffer + bp, rec->type_length);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hese parentheses are not necessary (personally, I find them a bit unusual and slightly confusing):

&(rec->type) ->&rec->type

A similar pattern appears in the code above: &(message->records[message->records_cnt]).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

buffer++;
}

remaining_len = message->message_total_len;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general issue with these parsing functions is that they do not properly validate the input data. If the data is corrupted (for any reason), the function may access memory beyond the buffer. But I understand that the code is still somewhat experimental, so this is not a critical issue for now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above.

}

uint16_t ndef_create_uri(const char *uri, uint8_t *buffer) {
*buffer = 0x3; // TLV header
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be much better not to rely on the caller it provide a sufficient buffer. Instead, we should take a more defensive approach and modify the function to have buffer_size argument:

size_t ndef_create_uri(const char* uri, uint8_t* buffer, size_t buffer_size);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

}

// Look at first byte, parse header
memcpy(&(rec->header), buffer, 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we always need memcpy here. In some cases, a simple assignment would be enough. However, if you insist on using memcpy, it would be safer to use sizeof instead of a constant, like this:

memcpy(&rec->header, &buffer[bp], sizeof(rec->header);
bp += sizeof(rec->header);

Personally, I prefer just:

rec->header = buffer[bp++];

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

header was a non-scalar type variable since i split the struct into bitfields so direct assignment was not possible. I changed the header into union so i can access it as a single byte.

#endif

/* Includes ------------------------------------------------------------------*/
#include <trezor_bsp.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include trezor_types instead

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

@@ -0,0 +1,44 @@
#pragma once
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include licence

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

0x00U /*!<NFC-F PAD0 */

/* P2P communication data */
static uint8_t NFCID3[] = {0x01, 0xFE, 0x03, 0x04, 0x05,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure static const is even better

st25r3916b_driver_t *drv = &g_st25r3916b_driver;

if(!drv->initialized) {
return NFC_NOT_INITIALIZED;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should return nfc_event_t, but this is different type. Either return NFC_NO_EVENT, or maybe better function signature would be bool nfc_get_event(nfc_event_t *event), which also allows adding data to events if needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I changed the function structure to nfc_status_t nfc_get_event(nfc_event_t *event) so it returns error if the driver was not initialized as well.

*/
0x00, 0x00}; /* RD */

static ReturnCode nfc_transcieve_blocking(uint8_t *txBuf, uint16_t txBufSize,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait do we? i am confused now, i believe snake_case is almost exclusively used in our C code


typedef enum {
NFC_NO_EVENT,
NFC_STATE_ACTIVATED,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the polling mechanism is meant to get events, so changes of states, not the state itself. So in this case, we should return NFC_EVENT_ACTIVATED and NFC_EVENT_DEACTIVATED pair. We could have a separate nfc_get_state function that would return current state, but mainly we need the event polling.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, understood. I renamed the event enum to NFC_EVENT_ACTIVATED, but for now that is the only event provided by nfc_get_event() function. The nfc_get_event() function might be further extended in the future to monitor more events when we have a proper usecase.

@@ -0,0 +1,14 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include license

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a


#pragma once

#include <trezor_bsp.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trezor_types shoudl be enough

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in bd5de3a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: 🏃‍♀️ In progress
Development

Successfully merging this pull request may close these issues.

3 participants