TNEP for tag device

This library provides an implementation of the Tag NDEF Exchange Protocol (TNEP) for an NFC tag device.

It is used in the NFC: TNEP tag sample.

Initialization

Before using the TNEP library in an NFC tag application, you must configure the NDEF message buffers and define the TNEP services.

Configuring NDEF message buffers

The tag library communicates with the Type 4 Tag library using NDEF message buffers. The buffers are initialized all at the same time by nfc_tnep_tag_tx_msg_buffer_register().

To use the NDEF message buffers, you must configure the following elements:

  • Provide a pointer to two TX buffers - one for the NDEF message used for tag emulation and another one for encoding the new NDEF message for later usage.

  • Set a maximum buffer size in bytes, which is valid for both buffers.

The buffers must be linked with the tag layer in the application.

When the polling device writes a new NDEF message to the tag buffer, the application must inform the tag library by calling nfc_tnep_tag_rx_msg_indicate().

Defining TNEP services

TNEP provides mechanisms for creating services and exchanging data through these services. The client (application or upper-layer protocol) must define these services and use them for data transfer. At least one TNEP service must be defined for the initial NDEF message to be created.

To create a TNEP service, use the NFC_TNEP_TAG_SERVICE_DEF macro. The service data is stored in the persistent storage.

Initial TNEP NDEF message

When calling the nfc_tnep_tag_initial_msg_create() function, the service data is loaded and the Service Parameter record of each service is inserted into the Initial TNEP message. A Service Parameter record contains parameters for communicating with the service. You can also encode additional NDEF records into the Initial TNEP message in the following way:

  1. Pass a callback to nfc_tnep_tag_initial_msg_create().

  2. Define an additional record inside the callback and call nfc_tnep_initial_msg_encode().

See the following code for an example of the Initial message encoding:

static int tnep_initial_msg_encode(struct nfc_ndef_msg_desc *msg)
{
	const uint8_t text[] = "Hello World";

	NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_text, UTF_8, en_code,
				      sizeof(en_code), text,
				      strlen(text));

	return nfc_tnep_initial_msg_encode(msg,
					   &NFC_NDEF_TEXT_RECORD_DESC(nfc_text),
					   1);
}

The following code shows how to create the Initial NDEF message:

	err = nfc_tnep_tag_initial_msg_create(TNEP_INITIAL_MSG_RECORD_COUNT,
					      tnep_initial_msg_encode);
	if (err) {
		printk("Cannot create initial TNEP message, err: %d\n", err);
	}

See also the following code for an example of service definition and TNEP initialization:

#define NFC_TNEP_TAG_SERVICE_DEF(service_name, uri, uri_length, mode, t_wait, n_wait, mac_msg_size
                                                         select_cb, deselect_cb message_cb, timeout_cb, error_cb)

nfc_tnep_tx_msg_buffer_register(buffer, swap_buffer, buffer_length);

nfc_tnep_init(event, event_cnt, nfc_txt_rw_payload_set);

nfc_tXt_setup();

nfc_tnep_tag_initial_msg_create(1, NULL);

nfc_tXt_emulation_start();

Configuration

After initialization, the tag library is in the Service Ready state and the NDEF message buffer contains Service Parameter records provided with the initial TNEP message.

The initial TNEP message can also include NDEF records that are not related with TNEP. The polling device, which does not support TNEP, can still interact with them.

Make sure to call the nfc_tnep_tag_process() function in an infinite loop in the application.

Receiving new messages

When a new NDEF message appears in the buffer and it contains a Service Select record, the application can select this service. To do this, the application should inform the TNEP tag library of the received NDEF message by calling nfc_tnep_tag_rx_msg_indicate(). The tag library will then change its state to Service Selected. From that point, the service description message will no longer be provided.

After the successful service selection, the select callback function of the service is called. If the tag library was already in Service Selected state when receiving the NDEF message with the Service Select record, the deselect callback of the previous service is called before the select callback of the new service.

When the library is in the Service Selected state, the service’s new message callback is called after successfully processing the new message. Application data can be added to the reply message with nfc_tnep_tag_tx_msg_app_data(). This function can be called from the Service Selected callback or from any other context, for example a different thread.

If the tag application has no more data, it will reply by using nfc_tnep_tag_tx_msg_no_app_data(). If the application does not reply before the expiration on the time period specified by the service’s initialization parameters, the service will be deselected by the polling device.

The following code demonstrates how to exchange NDEF messages using the tag library after initialization:

static void tnep_svc_one_selected(void)
{
	int err;
	const char svc_one_msg[] = "Service pi = 3.14159265358979323846";

	printk("Service one selected\n");

	NFC_TNEP_TAG_APP_MSG_DEF(app_msg, 1);
	NFC_NDEF_TEXT_RECORD_DESC_DEF(svc_one_rec, UTF_8, en_code,
				      sizeof(en_code), svc_one_msg,
				      strlen(svc_one_msg));

	err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(app_msg),
				      &NFC_NDEF_TEXT_RECORD_DESC(svc_one_rec));

	err = nfc_tnep_tag_tx_msg_app_data(&NFC_NDEF_MSG(app_msg),
					   NFC_TNEP_STATUS_SUCCESS);
	if (err) {
		printk("Service app data set err: %d\n", err);
	}

	dk_set_led_on(LED_SVC_ONE);
}

static void tnep_svc_one_deselected(void)
{
	printk("Service one deselected\n");

	dk_set_led_off(LED_SVC_ONE);
}
static void tnep_svc_one_msg_received(const uint8_t *data, size_t len)
{
	int err;

	printk("New service data received. Finish service data exchange\n");

	err = nfc_tnep_tag_tx_msg_no_app_data();
	if (err) {
		printk("TNEP Service data finish err: %d\n", err);
	}
}

static void tnep_svc_error(int err_code)
{
	printk("Service data exchange error: %d\n", err_code);
}

The following code demonstrates how to process the TNEP library:

tag_x_tag_handler()
{
        nfc_tnep_tag_rx_msg_indicate();
}

main()
{
        /*initialization code, application code*/
        while (1) {
                nfc_tnep_tag_process();
        }
}

API documentation

Header file: include/tnep/tag.h
Source file: subsys/tnep/tag.c
group nfc_tnep_tag

TAG NDEF Exchange Protocol for the NFC Tag Device.

Defines

NFC_TNEP_EVENTS_NUMBER

NFC TNEP library event count.

NFC_TNEP_TAG_MAX_WAIT_TIME

Maximum Service Waiting Time.

NFC_TNEP_TAG_MAX_N_WAIT_TIME

Maximum Waiting Time extension.

NFC_TNEP_TAG_APP_MSG_DEF(_name, _max_record_cnt)

Create and initialize an NFC NDEF TNEP Application message descriptor.

Use the macro NFC_NDEF_MSG to access the NDEF message descriptor instance. This macro reserves place for the TNEP status record.

Parameters
  • _name – Name of the related instance.

  • _max_record_cnt – Maximum count of records in the message.

NFC_TNEP_TAG_SERVICE_DEF(_name, _uri, _uri_length, _mode, _t_wait, _n_wait, _max_msg_size, _select_cb, _deselect_cb, _message_cb, _error_cb)

Macro to define TNEP service.

TNEP Service instance contains information about Services Parameter and callback s for NDEF application. The Service Parameter contains the Service Name URI of the Service and the TNEP parameters used to communicate with the Service. The Reader/Writer will configure the TNEP communication according to the announced parameter in the Service Parameter record.

Parameters
  • _name[in] Service instance name used in code. Use NFC_TNEP_TAG_SERVICE to get service by name.

  • _uri[in] Service Name URI.

  • _uri_length[in] Service Name URI length in bytes.

  • _mode[in] Service mode - TNEP Communication Mode: Single Response or Service specific

  • _t_wait[in] Minimum Waiting Time measured between the end of the last write command of an NDEF Write Procedure and the start of the first command of the first NDEF Read Procedure following the NDEF Write Procedures. T_wait has a value between 0 and 63 and is converted to time units using protocol specified formula.

  • _n_wait[in] Maximum Number of Waiting Time Extensions is the maximum number of requested waiting time extensions n_wait for a specific Service. N_wait can vary between 0 and 15 repetitions.

  • _max_msg_size[in] Maximum NDEF message size in bytes.

  • _select_cb[in] Callback function, called by protocol library when service is selected by the Reader/Writer.

  • _deselect_cb[in] Callback function, called by protocol library when service is deselected by the Reader/Writer.

  • _message_cb[in] Callback function, called by protocol library when new message is received from the Reader/Writer.

  • _error_cb[in] Callback function, called by protocol library when an internal error occurred.

NFC_TNEP_TAG_SERVICE(_name)

macro for accessing the TNEP Service.

Typedefs

typedef int (*nfc_payload_set_t)(uint8_t*, size_t)
typedef int (*initial_msg_encode_t)(struct nfc_ndef_msg_desc *msg)

Callback type for encoding the Initial TNEP NDEF message.

This callback is called every time when the TNEP NDEF library encodes the Initial TNEP message. You must use nfc_tnep_initial_msg_encode function to finish encoding.

Param msg

[in] Pointer to the Initial TNEP NDEF message.

Retval 0

If the operation was successful. Otherwise, a (negative) error code is returned.

Functions

int nfc_tnep_tag_tx_msg_buffer_register(uint8_t *tx_buff, uint8_t *tx_swap_buff, size_t len)

Register TNEP message buffer.

TNEP Tag Device needs two buffers one for current NDEF message and seconds for new message.

Parameters
  • tx_buff[in] Pointer to NDEF message buffer.

  • tx_swap_buff[in] Pointer to swap NDEF message buffer.

  • len[in] Length of NDEF message buffers.

Return values

0 – If the operation was successful. Otherwise, a (negative) error code is returned.

int nfc_tnep_tag_init(struct k_poll_event *events, uint8_t event_cnt, nfc_payload_set_t payload_set)

Start communication using TNEP.

Parameters
  • events[out] TNEP Tag Events.

  • event_cnt[in] Event count. This library needs 2 events.

  • payload_set[in] Function for setting NDEF data for NFC TNEP Tag Device. This library use it internally to set raw NDEF message to the Tag NDEF file. This function is called from atomic context, so sleeping or anyhow delaying is not allowed there.

Return values

0 – If the operation was successful. Otherwise, a (negative) error code is returned.

int nfc_tnep_tag_initial_msg_create(size_t max_record_cnt, initial_msg_encode_t msg_encode_cb)

Create the Initial TNEP NDEF message.

This function creates the Initial TNEP message. Initial NDEF message has to contain at least one service parameters record defined using NFC_TNEP_TAG_SERVICE_DEF. It can also contain optional NDEF records which can be used by NFC Poller Device which does not support TNEP Protocol.

Parameters
  • max_record_cnt[in] Maximum count of the optional NDEF records

  • msg_encode_cb[in] Callback function for encoding the Initial TNEP NDEF message. Can be set to NULL if max_record_cnt is 0 then only the Service Parameters Records are encoded into the Initial TNEP NDEF message.

Return values

0 – If the operation was successful. Otherwise, a (negative) error code is returned.

int nfc_tnep_initial_msg_encode(struct nfc_ndef_msg_desc *msg, const struct nfc_ndef_record_desc *records, size_t records_cnt)

Encode the Initial NDEF message.

This function encodes the Initial TNEP NDEF message. It must be used in combination with initial_msg_encode_t callback function.

Parameters
  • msg[in] Pointer to the Initial NDEF message descriptor. NDEF records can be added to it also before calling this function.

  • records[in] Pointer to the first NDEF records structure. Can be set to NULL if there are no additional NDEF records to encode or records are encoded directly to the Initial NDEF message.

  • records_cnt[in] Number of provided NDEF records. It must be set to 0 when records is NULL.

Return values

0 – If the operation was successful. Otherwise, a (negative) error code is returned.

void nfc_tnep_tag_process(void)

Waiting for a signals to execute protocol logic.

This function must be called periodically from thread to process the TNEP Tag Device.

Note

This function cannot be called before nfc_tnep_init().

void nfc_tnep_tag_rx_msg_indicate(const uint8_t *rx_buffer, size_t len)

Indicate about new TNEP message available in buffer.

The NFC Tag Device concludes that the NDEF Write Procedure is finished when, after a write command of the NDEF Write Procedure, an NDEF message is available in the data area. If, after the write command of the NDEF Write Procedure, no NDEF message is available in the data area, the NFC Tag Device concludes that the actual NDEF Write Procedure is ongoing.

Parameters
  • rx_buffer[in] Pointer to NDEF message buffer.

  • len[in] Length of NDEF message buffer.

int nfc_tnep_tag_tx_msg_app_data(struct nfc_ndef_msg_desc *msg, enum nfc_tnep_status_value status)

Add application data record to next message.

Use this function to set application data after the service selection to set service application data and use it also during service data exchange with the NFC Polling Device. To indicate that application has no more data use nfc_tnep_tag_tx_msg_no_app_data().

Parameters
  • msg[in] Pointer to NDEF message with application data. The message must have at least one free slot for the TNEP status record which is added automatically by this function. So if you want to encode for example 2 NDEF records the NDEF message minimal record count is 3. Use NFC_TNEP_TAG_APP_MSG_DEF which reserves slot for the TNEP status record.

  • status[in] TNEP App data message status.

Return values

0 – If the operation was successful. Otherwise, a (negative) error code is returned.

int nfc_tnep_tag_tx_msg_no_app_data(void)

Respond with no more application data.

If the NDEF application on the NFC Tag Device has finished, and therefore the NFC Tag Device has no more application data available for the Reader/Writer, then the NFC Tag Device SHALL provide a status message containing a single record that is a TNEP status record indicating success.

Return values

0 – If the operation was successful. Otherwise, a (negative) error code is returned.

void nfc_tnep_tag_on_selected(void)

Handle NFC Tag selected event.

If data exchange with poller starts again, NFC TNEP Tag device shall provide TNEP Initial message.

size_t nfc_tnep_tag_svc_count_get(void)

Get the NFC Tag TNEP service count.

Returns

TNEP Tag Service count.

struct nfc_tnep_tag_service_cb
#include <tag.h>

Public Members

void (*selected)(void)

Function called when service was selected.

void (*deselected)(void)

Function called when service deselected.

void (*message_received)(const uint8_t *data, size_t len)

Function called when new message was received.

Param data

[in] Pointer to received data.

Param len

[in] Received data length.

void (*error_detected)(int err)

Function called when an internal error in TNEP detected.

Param err

Detected error code.

struct nfc_tnep_tag_service
#include <tag.h>

Service structure.

This structure contains all information about user service. It contains service parameters, service parameters record and services callbacks. It is used by tnep_init() function.

Public Members

struct nfc_ndef_tnep_rec_svc_param *parameters

Services parameters.

struct nfc_ndef_record_desc *ndef_record

NDEF records data.

const struct nfc_tnep_tag_service_cb *callbacks

Callbacks structure.