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/
NFC Connection Handover TNEP service