Using NFC Library in 'raw' APDU mode : how to set FWI/FWTX?

Custom device using nrf5340, with NFC card emulation functionality.

To use the NFC, I understand that I should use the supplied nfc_t4t library (binary) and its api. I have this working ok for type 4 card emulation using the NDEF application functionality built in to the library..

I now need to create an application that will emulate other functions, and so I am trying to the use the library in 'raw APDU' mode. This is basically working, but I have run into a timeout issue : certain elements of my APDU processing can take some 10's of ms, and this is apparently causing my nfc reader to timeout its request. This causes the library to have some issues : I get logs like this:

[00:05:13.375,488] <err> nfc_platform: Tried to read data pointer: 4 bytes, read 0.
[00:05:13.375,488] <err> nfc_platform: Reading nfc ring buffer failed, resetting ring buffer.

 I think these are basically when I do the nfc_t4t_response_pdu_send() call to send the response PDU. Any ideas on what this means for my operation?

The reader never manages to get a valid response, this seems to be related to the time it takes for my code to process the APDU (around 35ms for a basic select operation, due to it being sent to a ISO7816 contact card for processing...). In the NFC spec I see this:

"When the Card Emulator needs more time than the defined FWT to process the received block, it uses an S(WTX) request for a waiting time extension. An S(WTX) request contains a 1-byte INF field as specified in Table 84"

How can I tell the library that I want it to send this S(WTX) frame? It seems it needs to be sent for each received command? The only param I see to set is the FWI (which sets the max FWT as I understand it). When I try to set this to the max (before doing nfc_t4t_setup()) :

                uint32_t fwi = NFC_T4T_FWI_MAX_VAL_EMV;
                if ((ret=nfc_t4t_parameter_set(NFC_T4T_PARAM_FWI, &fwi, sizeof(fwi)))!=0) {
                    log_warn("nfcmgr: t4t fwi set fails (%d)",ret);
                }
Then it gives me an error -22
Doing a get of the FWI_MAX says its set to 4.
Is there a way to get the library to tell the reader that I want to increase the waiting timeout for a specific received command?
thanks
Parents Reply Children
  • I'm very doubtfull about this, as my changes to platform.c/platform_internal_thread.c were made to add debugging to see if there was an issue about the ring buffer/scheduling or not...(and there are some issues).

    I will need to come back to the project and see what happens when I rebuild with this option.

    What happened to this investigation?

    What we see initially, is that it looks like the 3-byte Tx frame logged is the corrupted frame
  • disable CONFIG_NFC_THREAD_CALLBACK this should bypass platform.c, which should reveal if the error is in the library itself.

    This option

    CONFIG_NFC_THREAD_CALLBACK=n

    causes the code to crash immediately the NFC reader is presented. Do I need other config set or other code changes?

  • Ok, I see that this option essentially calls my code in the IRQ context directly using platform_internal_irq.c - not surprising it ends up in an assert().

    By creating a 'test' callback function which is IRQ safe, and just makes a fixed response to the DATA_IND event using nfc_t4t_response_pdu_send() , the reader operates as expected.

    However, if there is any delay (more than a couple of ms) in generating the response (eg I use the system queue to schedule the response outside of the callback) then the reader does not manage to see the response correctly.

    I think this demonstrates that the problem is not in the platform.c or platform_internal.c code but in the library (as already hinted at in your previous responses).

    My callback code for the build case with CONFIG_NFC_THREAD_CALLBACK=n:

    static uint8_t _nok_resp[] = { 0x6a, 0x82};
    static void _apdu_cmd_test_resp(void* p_ctx) {
        struct _nfc_ctx* ctx = (struct _nfc_ctx*)p_ctx;
        if (ctx==NULL) {
            return;
        }
        int ret;
        if ((ret=nfc_t4t_response_pdu_send(_nok_resp, sizeof(_nok_resp)))!=0) {
            log_warn("nfcmgr: t4t apdu test resp NOT sent after %dms error (%d)", (gettime_ms()-ctx->lastRead), ret);
        } else {
            log_warn("nfcmgr:t4t apdu test resp sent after %dms", (gettime_ms()-ctx->lastRead));
        }
    }

    static void _apdu_cmd_test_print(void* p_ctx) {
        log_warn("nfcmgr: test event %d", (int32_t)p_ctx);
    }

    // ADPU type operation test callback, called directly on IRQ, just always responds with 'no'
    static void _nfc_t4t_test_cb_from_irq(void *context, nfc_t4t_event_t event, const uint8_t *data, size_t data_length, uint32_t flags) {
        struct _nfc_ctx* ctx = (struct _nfc_ctx*)context;
        if (ctx==NULL) {
            return;
        }
        switch(event) {        
            case NFC_T4T_EVENT_FIELD_ON: {
                ctx->nfc_active = true;
                wsched_request(_apdu_cmd_test_print, (void*)NFC_T4T_EVENT_FIELD_ON);
                break;
            }
            case NFC_T4T_EVENT_FIELD_OFF: {
                ///< External Reader polling stopped
                ctx->nfc_active = false;
                wsched_request(_apdu_cmd_test_print, (void*)NFC_T4T_EVENT_FIELD_OFF);
                break;
            }
            case NFC_T4T_EVENT_DATA_IND: {
                ctx->lastRead = gettime_ms();
                int ret=nfc_t4t_response_pdu_send(_nok_resp, sizeof(_nok_resp));
                wsched_request(_apdu_cmd_test_print, (void*)ret);
    //            wsched_request(_apdu_cmd_test_print, (void*)NFC_T4T_EVENT_DATA_IND);
                // will generate a response
    //            wsched_request(_apdu_cmd_test_resp, ctx);
                break;
            }
            case NFC_T4T_EVENT_DATA_TRANSMITTED: {
                // just to log
                wsched_request(_apdu_cmd_test_print, (void*)NFC_T4T_EVENT_DATA_TRANSMITTED);
                break;
            }
            default: {
                wsched_request(_apdu_cmd_test_print, (void*)event);
                break;
            }
        }
    }
    Note if the case for DATA_IND uses the  system work queue to schedule the  function _apdu_cmd_test_resp()  then the problem occurs, if it directly does pdu_send() then it does not.
  • any word back on this?

    I have rewritten my code to process the callback from the IRQ directly (CONFIG_NFC_THREAD_CALLBACK=n) instead of using platform_internal_thread.c and always get the same result (works if response sent immediately, bad NFC if takes > 3-4ms).

  • Hi, I'd really like to make some progress on this as its blocking my POCs for digital wallet solutions (access control, authentication etc) using the NFC channel.

    Any ideas on the way to move forward?

Related