Files
dogtopus dfd753703a FeliCa Emulation: Handle certain Polling commands in firmware (#4204)
* FeliCa: Handle non-hardware Polling commands

NFC TagInfo and possibly other readers rely on Polling commands with Request Code of 1 (default System Code request) or non-FFFF System Code to detect card type. Since the NFC controller doesn't seem to handle them in hardware and simply bubbles them up, and then the Flipper firmware will just ignore them and refuse to respond afterwards, this causes the reading operation to fail.

This commit adds a simple handler for such Polling commands so that readers behaving like NFC TagInfo could read the emulated card without failing.

* Only handle cases when System Code is not FFFF

The NFC controller should handle Polling commands with the System Code set to FFFF, so it's not necessary for the firmware to handle it.

* Remove system code logging

* More cleanups

* Remove the claim that we need a poller change

We already have enough information to determine whether or not the card supports NDEF since SYS_OP register value is included in all current Lite-S card dumps.

* Respond to 12FC polling command when needed

* Handle Polling with NDEF and Lite-S Service Code

This allows the reader to specifically select the service by naming the Service Code.

* Introduce API for manual handling of Polling commands

Introduce nfc_felica_listener_timer_anticol_start() and nfc_felica_listener_timer_anticol_stop(). These are for now just wrappers around the block_tx timer that can be used to delay the response until the desired Time Slot. Thanks to the loose timing constraints of FeliCa collision resolution protocol, no compensation seems to be necessary. Also enabled the block_tx timer for FeliCa listener, but with both compensation and fdt set to 0 to keep the original behavior of not using the timer during normal data exchange.

This API is now being used for handling Polling commands that are not handled by the NFC controller on the hardware side.

* Document target_time_slot

* Implement changes suggested by @RebornedBrain

* api: f18 version sync

* nfc: added stubs for `nfc_felica_listener_timer_anticol` for unit tests

---------

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: hedger <hedger@nanode.su>
2025-09-24 14:08:40 +04:00

404 lines
14 KiB
C

/**
* @file nfc.h
* @brief Transport layer Nfc library.
*
* The Nfc layer is responsible for setting the operating mode (poller or listener)
* and technology (ISO14443-3A/B, ISO15693, ...), data exchange between higher
* protocol-specific levels and underlying NFC hardware, as well as timings handling.
*
* In applications using the NFC protocol system there is no need to neiter explicitly
* create an Nfc instance nor call any of its functions, as it is all handled
* automatically under the hood.
*
* If the NFC protocol system is not suitable for the application's intended purpose
* or there is need of having direct access to the NFC transport layer, then an Nfc
* instance must be allocated and the below functions shall be used to implement
* the required logic.
*/
#pragma once
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Nfc opaque type definition.
*/
typedef struct Nfc Nfc;
/**
* @brief Enumeration of possible Nfc event types.
*
* Not all technologies implement all events (this is due to hardware limitations).
*/
typedef enum {
NfcEventTypeUserAbort, /**< User code explicitly aborted the current operation. */
NfcEventTypeFieldOn, /**< Reader's field was detected by the NFC hardware. */
NfcEventTypeFieldOff, /**< Reader's field was lost. */
NfcEventTypeTxStart, /**< Data transmission has started. */
NfcEventTypeTxEnd, /**< Data transmission has ended. */
NfcEventTypeRxStart, /**< Data reception has started. */
NfcEventTypeRxEnd, /**< Data reception has ended. */
NfcEventTypeListenerActivated, /**< The listener has been activated by the reader. */
NfcEventTypePollerReady, /**< The card has been activated by the poller. */
} NfcEventType;
/**
* @brief Nfc event data structure.
*/
typedef struct {
BitBuffer* buffer; /**< Pointer to the received data buffer. */
} NfcEventData;
/**
* @brief Nfc event structure.
*
* Upon emission of an event, an instance of this struct will be passed to the callback.
*/
typedef struct {
NfcEventType type; /**< Type of the emitted event. */
NfcEventData data; /**< Event-specific data. */
} NfcEvent;
/**
* @brief Enumeration of possible Nfc commands.
*
* The event callback must return one of these to determine the next action.
*/
typedef enum {
NfcCommandContinue, /**< Continue operation normally. */
NfcCommandReset, /**< Reset the current state. */
NfcCommandStop, /**< Stop the current operation. */
NfcCommandSleep, /**< Switch Nfc hardware to low-power mode. */
} NfcCommand;
/**
* @brief Nfc event callback type.
*
* A function of this type must be passed as the callback parameter upon start of a an Nfc instance.
*
* @param [in] event Nfc event, passed by value, complete with protocol type and data.
* @param [in,out] context pointer to the user-specific context (set when starting an Nfc instance).
* @returns command which the event producer must execute.
*/
typedef NfcCommand (*NfcEventCallback)(NfcEvent event, void* context);
/**
* @brief Enumeration of possible operating modes.
*
* Not all technologies implement the listener operating mode.
*/
typedef enum {
NfcModePoller, /**< Configure the Nfc instance as a poller. */
NfcModeListener, /**< Configure the Nfc instance as a listener. */
NfcModeNum, /**< Operating mode count. Internal use. */
} NfcMode;
/**
* @brief Enumeration of available technologies.
*/
typedef enum {
NfcTechIso14443a, /**< Configure the Nfc instance to use the ISO14443-3A technology. */
NfcTechIso14443b, /**< Configure the Nfc instance to use the ISO14443-3B technology. */
NfcTechIso15693, /**< Configure the Nfc instance to use the ISO15693 technology. */
NfcTechFelica, /**< Configure the Nfc instance to use the FeliCa technology. */
NfcTechNum, /**< Technologies count. Internal use. */
} NfcTech;
/**
* @brief Enumeration of possible Nfc error codes.
*/
typedef enum {
NfcErrorNone, /**< No error has occurred. */
NfcErrorInternal, /**< An unknown error has occured on the lower level. */
NfcErrorTimeout, /**< Operation is taking too long (e.g. card does not respond). */
NfcErrorIncompleteFrame, /**< An incomplete data frame has been received. */
NfcErrorDataFormat, /**< Data has not been parsed due to wrong/unknown format. */
} NfcError;
/**
* @brief Allocate an Nfc instance.
*
* Will exclusively take over the NFC HAL until deleted.
*
* @returns pointer to the allocated Nfc instance.
*/
Nfc* nfc_alloc(void);
/**
* @brief Delete an Nfc instance.
*
* Will release the NFC HAL lock, making it available for use by others.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void nfc_free(Nfc* instance);
/**
* @brief Configure the Nfc instance to work in a particular mode.
*
* Not all technologies implement the listener operating mode.
*
* @param[in,out] instance pointer to the instance to be configured.
* @param[in] mode required operating mode.
* @param[in] tech required technology configuration.
*/
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech);
/**
* @brief Set poller frame delay time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] fdt_poll_fc frame delay time, in carrier cycles.
*/
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc);
/**
* @brief Set listener frame delay time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] fdt_listen_fc frame delay time, in carrier cycles.
*/
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc);
/**
* @brief Set mask receive time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] mask_rx_time_fc mask receive time, in carrier cycles.
*/
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc);
/**
* @brief Set frame delay time.
*
* Frame delay time is the minimum time between two consecutive poll frames.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] fdt_poll_poll_us frame delay time, in microseconds.
*/
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us);
/**
* @brief Set guard time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] guard_time_us guard time, in microseconds.
*/
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us);
/**
* @brief Start the Nfc instance.
*
* The instance must be configured to work with a specific technology
* in a specific operating mode with a nfc_config() call before starting.
*
* Once started, the user code will be receiving events through the provided
* callback which must handle them according to the logic required.
*
* @param[in,out] instance pointer to the instance to be started.
* @param[in] callback pointer to a user-defined callback function which will receive events.
* @param[in] context pointer to a user-specific context (will be passed to the callback).
*/
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context);
/**
* @brief Stop Nfc instance.
*
* The instance can only be stopped if it is running.
*
* @param[in,out] instance pointer to the instance to be stopped.
*/
void nfc_stop(Nfc* instance);
/**
* @brief Transmit and receive a data frame in poller mode.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer, with a timeout defined by the fwt parameter.
*
* The data being transmitted and received may be either bit- or byte-oriented.
* It shall not contain any technology-specific sequences as start or stop bits
* and/or other special symbols, as this is handled on the underlying HAL level.
*
* Must ONLY be used inside the callback function.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt);
/**
* @brief Transmit a data frame in listener mode.
*
* Used to transmit a response to the reader request in listener mode.
*
* The data being transmitted may be either bit- or byte-oriented.
* It shall not contain any technology-specific sequences as start or stop bits
* and/or other special symbols, as this is handled on the underlying HAL level.
*
* Must ONLY be used inside the callback function.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer);
/*
* Technology-specific functions.
*
* In a perfect world, this would not be necessary.
* However, the current implementation employs NFC hardware that partially implements
* certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features.
*/
/******************* ISO14443-3A specific API *******************/
/**
* @brief Enumeration of possible ISO14443-3A short frame types.
*/
typedef enum {
NfcIso14443aShortFrameSensReq,
NfcIso14443aShortFrameAllReqa,
} NfcIso14443aShortFrame;
/**
* @brief Transmit an ISO14443-3A short frame and receive the response in poller mode.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] frame type of short frame to be sent.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_poller_trx_short_frame(
Nfc* instance,
NfcIso14443aShortFrame frame,
BitBuffer* rx_buffer,
uint32_t fwt);
/**
* @brief Transmit an ISO14443-3A SDD frame and receive the response in poller mode.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_poller_trx_sdd_frame(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
/**
* @brief Transmit an ISO14443-3A data frame with custom parity bits and receive the response in poller mode.
*
* Same as nfc_poller_trx(), but uses the parity bits provided by the user code
* instead of calculating them automatically.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_poller_trx_custom_parity(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
/**
* @brief Transmit an ISO14443-3A frame with custom parity bits in listener mode.
*
* Same as nfc_listener_tx(), but uses the parity bits provided by the user code
* instead of calculating them automatically.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer);
/**
* @brief Set ISO14443-3A collision resolution parameters in listener mode.
*
* Configures the NFC hardware for automatic collision resolution.
*
* @param[in,out] instance pointer to the instance to be configured.
* @param[in] uid pointer to a byte array containing the UID.
* @param[in] uid_len UID length in bytes (must be supported by the protocol).
* @param[in] atqa ATQA byte value.
* @param[in] sak SAK byte value.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_listener_set_col_res_data(
Nfc* instance,
uint8_t* uid,
uint8_t uid_len,
uint8_t* atqa,
uint8_t sak);
/**
* @brief Set FeliCa collision resolution parameters in listener mode.
*
* Configures the NFC hardware for automatic collision resolution.
*
* @param[in,out] instance pointer to the instance to be configured.
* @param[in] idm pointer to a byte array containing the IDm.
* @param[in] idm_len IDm length in bytes.
* @param[in] pmm pointer to a byte array containing the PMm.
* @param[in] pmm_len PMm length in bytes.
* @param[in] sys_code System code from SYS_C block
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_felica_listener_set_sensf_res_data(
Nfc* instance,
const uint8_t* idm,
const uint8_t idm_len,
const uint8_t* pmm,
const uint8_t pmm_len,
const uint16_t sys_code);
/**
* @brief Send ISO15693 Start of Frame pattern in listener mode
*
* @param[in,out] instance pointer to the instance to be configured.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance);
/**
* @brief Start the timer used for manual FeliCa collision resolution in listener mode.
*
* This blocks TX until the desired Time Slot, and should be called as soon as the listener
* determines that a collision resolution needs to be handled manually.
*
* @param[in, out] instance instance pointer to the instance to be configured.
* @param[in] target_time_slot Target Time Slot number. Should be a value within the range of 0-15 (double-inclusive).
*/
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot);
/**
* @brief Cancel the timer used for manual FeliCa collision resolution in listener mode.
*
* @param[in, out] instance instance pointer to the instance to be configured.
*/
void nfc_felica_listener_timer_anticol_stop(Nfc* instance);
#ifdef __cplusplus
}
#endif