NFC TNEP Connection Handover

The NFC TNEP Connection Handover service handles the exchange of Connection Handover Messages between an NFC Forum Tag and an NFC Forum Poller device. It uses the TNEP protocol in the Single Response Communication mode to exchange the handover messages between:

The Connection Handover Service name URI for the service announced in the Service Parameter record is urn:nfc:sn:handover.

Handover protocol

The Connection Handover Protocol enables two NFC Forum devices to negotiate a shared set of alternative (non-NFC) communication carriers. The Handover Requester Device is the NFC Forum Device that initiates the handover negotiation. The Handover Selector Device is the NFC Forum Device that is initially passive and responds to the Handover Requester.

Static handover

In the static handover, the Handover Requester only reads a Handover Select Message from the NFC Tag Device. TNEP is not used in this communication form.

Static handover

Negotiated handover

In the negotiated handover, the Handover Requester provides a Handover Request Message that contains its set of supported carriers to the Handover Selector. The Handover Selector replies with a Handover Select Message that contains the list of carriers that they both support. Connection Handover NDEF messages are exchanged as defined by TNEP.

Negotiated handover

Device Role

The role of the NFC Poller Device depends on the NFC Tag Device. If the NFC Tag Device takes the role of the Handover Requester, it responds to the Service Select with a Handover Request Message. This message includes a TNEP status record. The NFC Poller Device responds with the Handover Select Message after having received the Handover Request Message.

If the NFC Tag Device takes the role of the Handover Selector, then the NFC Tag Device does not add any NDEF records defined in this specification into the TNEP status message. The TNEP status message follows the Service Select message and the NFC Tag Device waits for a Handover Request Message from the NFC Poller Device.

The NFC Tag Device role is defined by callback set in the nfc_tnep_ch_cb structure and passed to the nfc_tnep_ch_service_init() function.

NFC Tag Device

The following code sample demonstrates how to use this module with an NFC Tag Device:

static int carrier_prepare(void)
{
	static struct nfc_ndef_le_oob_rec_payload_desc rec_payload;

	NFC_NDEF_LE_OOB_RECORD_DESC_DEF(oob_rec, '0', &rec_payload);
	NFC_NDEF_CH_AC_RECORD_DESC_DEF(oob_ac, NFC_AC_CPS_ACTIVE, 1, "0", 0);

	memset(&rec_payload, 0, sizeof(rec_payload));

	rec_payload.addr = &oob_local.addr;
	rec_payload.le_sc_data = &oob_local.le_sc_data;
	rec_payload.tk_value = tk_value;
	rec_payload.local_name = bt_get_name();
	rec_payload.le_role = NFC_NDEF_LE_OOB_REC_LE_ROLE(
		NFC_NDEF_LE_OOB_REC_LE_ROLE_PERIPH_ONLY);
	rec_payload.appearance = NFC_NDEF_LE_OOB_REC_APPEARANCE(
		CONFIG_BT_DEVICE_APPEARANCE);
	rec_payload.flags = NFC_NDEF_LE_OOB_REC_FLAGS(BT_LE_AD_NO_BREDR);

	return nfc_tnep_ch_carrier_set(&NFC_NDEF_CH_AC_RECORD_DESC(oob_ac),
				       &NFC_NDEF_LE_OOB_RECORD_DESC(oob_rec),
				       1);
}

#if defined(CONFIG_NFC_TAG_CH_REQUESTER)
static int tnep_ch_request_prepare(void)
{
	bt_le_adv_stop();
	return carrier_prepare();
}

static int tnep_ch_select_received(const struct nfc_tnep_ch_record *ch_select,
				   bool inactive)
{
	int err;
	const struct nfc_ndef_record_desc *oob_data = NULL;

	if (!ch_select->count) {
		return -EINVAL;
	}

	/* All alternative carrier are inactive */
	if (inactive) {
		/* Try send request again. */
		return carrier_prepare();
	}

	err = check_oob_carrier(ch_select, &oob_data);
	if (err) {
		return err;
	}

	err = oob_le_data_handle(oob_data, false);
	if (err) {
		return err;
	}

	return 0;

}
#endif /* defined(CONFIG_NFC_TAG_CH_REQUESTER) */

static int tnep_ch_request_received(const struct nfc_tnep_ch_request *ch_req)
{
	int err;
	const struct nfc_ndef_record_desc *oob_data = NULL;

	if (!ch_req->ch_record.count) {
		return -EINVAL;
	}

	err = check_oob_carrier(&ch_req->ch_record, &oob_data);
	if (err) {
		return err;
	}

	bt_le_adv_stop();

	err = oob_le_data_handle(oob_data, true);
	if (err) {
		return err;
	}

	return carrier_prepare();
}

static struct nfc_tnep_ch_cb ch_cb = {
#if defined(CONFIG_NFC_TAG_CH_REQUESTER)
	.request_msg_prepare = tnep_ch_request_prepare,
	.select_msg_recv = tnep_ch_select_received,
#endif
	.request_msg_recv = tnep_ch_request_received
};

This library is used in the Bluetooth: NFC pairing sample.

NFC Poller Device

The following code sample demonstrates how to use this module with an NFC Poller Device:

static int carrier_prepare(void)
{
	static struct nfc_ndef_le_oob_rec_payload_desc rec_payload;

	NFC_NDEF_LE_OOB_RECORD_DESC_DEF(oob_rec, '0', &rec_payload);
	NFC_NDEF_CH_AC_RECORD_DESC_DEF(oob_ac, NFC_AC_CPS_ACTIVE, 1, "0", 0);

	memset(&rec_payload, 0, sizeof(rec_payload));

	rec_payload.addr = &oob_local.addr;
	rec_payload.le_sc_data = &oob_local.le_sc_data;
	rec_payload.tk_value = tk_value;
	rec_payload.local_name = bt_get_name();
	rec_payload.le_role = NFC_NDEF_LE_OOB_REC_LE_ROLE(
		NFC_NDEF_LE_OOB_REC_LE_ROLE_CENTRAL_ONLY);
	rec_payload.appearance = NFC_NDEF_LE_OOB_REC_APPEARANCE(
		CONFIG_BT_DEVICE_APPEARANCE);
	rec_payload.flags = NFC_NDEF_LE_OOB_REC_FLAGS(BT_LE_AD_NO_BREDR);

	return nfc_tnep_ch_carrier_set(&NFC_NDEF_CH_AC_RECORD_DESC(oob_ac),
				       &NFC_NDEF_LE_OOB_RECORD_DESC(oob_rec),
				       1);
}

static int tnep_ch_request_prepare(void)
{
	return carrier_prepare();
}

static int tnep_ch_select_received(const struct nfc_tnep_ch_record *ch_select,
				   bool inactive)
{
	int err;
	const struct nfc_ndef_record_desc *oob_data = NULL;

	if (!ch_select->count) {
		return -EINVAL;
	}

	/* All alternative carrier are inactive */
	if (inactive) {
		/* Try send request again. */
		return carrier_prepare();
	}

	err = check_oob_carrier(ch_select, &oob_data);
	if (err) {
		return err;
	}

	err = oob_le_data_handle(oob_data, false);
	if (err) {
		return err;
	}

	return 0;

}

static int tnep_ch_request_received(const struct nfc_tnep_ch_request *ch_req)
{
	int err;
	const struct nfc_ndef_record_desc *oob_data = NULL;

	if (!ch_req->ch_record.count) {
		return -EINVAL;
	}

	err = check_oob_carrier(&ch_req->ch_record, &oob_data);
	if (err) {
		return err;
	}

	err = oob_le_data_handle(oob_data, true);
	if (err) {
		return err;
	}

	return carrier_prepare();
}

static struct nfc_tnep_ch_cb ch_cb = {
	.request_msg_prepare = tnep_ch_request_prepare,
	.select_msg_recv = tnep_ch_select_received,
	.request_msg_recv = tnep_ch_request_received
};

This library is used in the Bluetooth: Central NFC pairing sample.

API documentation

Header file: include/nfc/tnep/ch.h
Source files: subsys/nfc/tnep/ch/
group nfc_tnep_ch

NFC Connection Handover TNEP service API.

Defines

NFC_TNEP_CH_URI_LENGTH

NFC Connection Handover service URI name length.

Functions

int nfc_tnep_ch_service_init(struct nfc_tnep_ch_cb *cb)

Initialize the TNEP Connection Handover service.

Parameters:
  • cb[in] Callback structure.

Return values:

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

int nfc_tnep_ch_carrier_set(struct nfc_ndef_record_desc *ac_rec, struct nfc_ndef_record_desc *carrier_rec, size_t count)

Set Connection Handover carriers.

Function for setting the Connection Handover carriers. It should be used in the callback from nfc_tnep_ch_cb when the Application should reply to the Connection Handover Message. The type of message is determined automatically based on the received message and the role of the device in the data exchange.

Parameters:
  • ac_rec[in] Array of the Alternative Carrier Records.

  • carrier_rec[in] Array of the Carrier Records.

  • count[in] Count of the Alternative Carrier Records and the Carrier Records.

Return values:

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

Helper function for searching the Connection Handover service in the Initial NDEF Message.

Parameters:
  • services[in] Array of the TNEP services.

  • cnt[in] Services count.

Returns:

Pointer to the Connection Handover service. NULL if service is not found.

Variables

const uint8_t nfc_tnep_ch_svc_uri[19]

NFC Connection Handover service URI.

struct nfc_tnep_ch_record
#include <ch.h>

Connection Handover Record structure.

Public Members

const struct nfc_ndef_ch_ac_rec *ac

Pointer to the first Alternative Carrier Record.

const struct nfc_ndef_record_desc **carrier

Pointer to the first Carrier Record.

size_t count

Count of the Alternative Carriers Records and Carrier Records.

uint8_t major_ver

Connection Handover major version.

uint8_t minor_ver

Connection Handover minor version.

struct nfc_tnep_ch_request
#include <ch.h>

Connection Handover Request Record structure.

Public Members

const struct nfc_ndef_ch_cr_rec *cr

Pointer to the Collision Resolution Record data.

struct nfc_tnep_ch_record ch_record

Connection Handover Record data.

struct nfc_tnep_ch_cb
#include <ch.h>

Connection Handover service callback structure.

Public Members

int (*request_msg_prepare)(void)

The Connection Handover Request Message prepare callback.

This callback is called every time when the NFC Forum Device takes the Connection Handover Requester role and the Connection Handover Message must be sent to the other NFC Forum Device.

Retval 0:

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

int (*request_msg_recv)(const struct nfc_tnep_ch_request *ch_req)

The Connection Handover Request Message received callback.

This callback is called always when NFC Forum Device which supports TNEP received the Connection Handover Request Message. After receiving this message, application shall respond with the Handover Select Message by calling nfc_tnep_ch_carrier_set.

Param ch_req:

[in] Pointer to the Connection Handover Request structure which contains the parsed Connection Handover Request Message.

Retval 0:

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

int (*select_msg_recv)(const struct nfc_tnep_ch_record *ch_select, bool inactive)

The Connection Handover Select Message received callback.

This callback is called always when the NFC Device receive the Connection Select Message in response to the Connection Handover Request message.

Param ch_select:

[in] Pointer to Connection Handover structure which contains the parsed Connection Handover Select Message.

Param inactive:

[in] Indicates that all Alternative Carriers are inactive. The Handover Request Message should be sent again with only one Alternative Carrier using nfc_tnep_ch_carrier_set.

Retval 0:

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

int (*mediation_req_recv)(void)

The Connection Handover Mediation Request Message received callback.

This callback is called when an NFC Forum Tag Device that supports TNEP receives a Connection Handover Request Message that contains, in a Handover Carrier Record, the information for a single alternative carrier of NFC Forum Well Known type “Hm”. After receiving this message, the application shall respond with the Handover Mediation Message by calling nfc_tnep_ch_carrier_set.

Retval 0:

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

int (*initial_msg_recv)(const struct nfc_tnep_ch_record *ch_initial)

The Connection Handover Initial Message received callback.

This callback is called always when NFC Forum Tag Device which supports TNEP receives the Connection Handover Initial Message. After receiving this message, application shall respond also with the Handover Initial Message by calling nfc_tnep_ch_carrier_set.

Param ch_initial:

[in] Pointer to Connection Handover structure which contains the parsed Connection Handover Initial Message.

Retval 0:

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

void (*error)(int err)

Error callback.

Called always when internal error was detected.

Param err:

[in] Detected error code.