18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  The NFC Controller Interface is the communication protocol between an
48c2ecf20Sopenharmony_ci *  NFC Controller (NFCC) and a Device Host (DH).
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Copyright (C) 2011 Texas Instruments, Inc.
78c2ecf20Sopenharmony_ci *  Copyright (C) 2014 Marvell International Ltd.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Written by Ilan Elias <ilane@ti.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/wait.h>
178c2ecf20Sopenharmony_ci#include <linux/bitops.h>
188c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "../nfc.h"
218c2ecf20Sopenharmony_ci#include <net/nfc/nci.h>
228c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h>
238c2ecf20Sopenharmony_ci#include <linux/nfc.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Complete data exchange transaction and forward skb to nfc core */
268c2ecf20Sopenharmony_civoid nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
278c2ecf20Sopenharmony_ci				__u8 conn_id, int err)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct nci_conn_info    *conn_info;
308c2ecf20Sopenharmony_ci	data_exchange_cb_t cb;
318c2ecf20Sopenharmony_ci	void *cb_context;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
348c2ecf20Sopenharmony_ci	if (!conn_info) {
358c2ecf20Sopenharmony_ci		kfree_skb(skb);
368c2ecf20Sopenharmony_ci		goto exit;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	cb = conn_info->data_exchange_cb;
408c2ecf20Sopenharmony_ci	cb_context = conn_info->data_exchange_cb_context;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	pr_debug("len %d, err %d\n", skb ? skb->len : 0, err);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* data exchange is complete, stop the data timer */
458c2ecf20Sopenharmony_ci	del_timer_sync(&ndev->data_timer);
468c2ecf20Sopenharmony_ci	clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (cb) {
498c2ecf20Sopenharmony_ci		/* forward skb to nfc core */
508c2ecf20Sopenharmony_ci		cb(cb_context, skb, err);
518c2ecf20Sopenharmony_ci	} else if (skb) {
528c2ecf20Sopenharmony_ci		pr_err("no rx callback, dropping rx data...\n");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		/* no waiting callback, free skb */
558c2ecf20Sopenharmony_ci		kfree_skb(skb);
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciexit:
598c2ecf20Sopenharmony_ci	clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* ----------------- NCI TX Data ----------------- */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline void nci_push_data_hdr(struct nci_dev *ndev,
658c2ecf20Sopenharmony_ci				     __u8 conn_id,
668c2ecf20Sopenharmony_ci				     struct sk_buff *skb,
678c2ecf20Sopenharmony_ci				     __u8 pbf)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct nci_data_hdr *hdr;
708c2ecf20Sopenharmony_ci	int plen = skb->len;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	hdr = skb_push(skb, NCI_DATA_HDR_SIZE);
738c2ecf20Sopenharmony_ci	hdr->conn_id = conn_id;
748c2ecf20Sopenharmony_ci	hdr->rfu = 0;
758c2ecf20Sopenharmony_ci	hdr->plen = plen;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
788c2ecf20Sopenharmony_ci	nci_pbf_set((__u8 *)hdr, pbf);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ciint nci_conn_max_data_pkt_payload_size(struct nci_dev *ndev, __u8 conn_id)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct nci_conn_info *conn_info;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
868c2ecf20Sopenharmony_ci	if (!conn_info)
878c2ecf20Sopenharmony_ci		return -EPROTO;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return conn_info->max_pkt_payload_len;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_conn_max_data_pkt_payload_size);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int nci_queue_tx_data_frags(struct nci_dev *ndev,
948c2ecf20Sopenharmony_ci				   __u8 conn_id,
958c2ecf20Sopenharmony_ci				   struct sk_buff *skb) {
968c2ecf20Sopenharmony_ci	struct nci_conn_info    *conn_info;
978c2ecf20Sopenharmony_ci	int total_len = skb->len;
988c2ecf20Sopenharmony_ci	unsigned char *data = skb->data;
998c2ecf20Sopenharmony_ci	unsigned long flags;
1008c2ecf20Sopenharmony_ci	struct sk_buff_head frags_q;
1018c2ecf20Sopenharmony_ci	struct sk_buff *skb_frag;
1028c2ecf20Sopenharmony_ci	int frag_len;
1038c2ecf20Sopenharmony_ci	int rc = 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	pr_debug("conn_id 0x%x, total_len %d\n", conn_id, total_len);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
1088c2ecf20Sopenharmony_ci	if (!conn_info) {
1098c2ecf20Sopenharmony_ci		rc = -EPROTO;
1108c2ecf20Sopenharmony_ci		goto exit;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	__skb_queue_head_init(&frags_q);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	while (total_len) {
1168c2ecf20Sopenharmony_ci		frag_len =
1178c2ecf20Sopenharmony_ci			min_t(int, total_len, conn_info->max_pkt_payload_len);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		skb_frag = nci_skb_alloc(ndev,
1208c2ecf20Sopenharmony_ci					 (NCI_DATA_HDR_SIZE + frag_len),
1218c2ecf20Sopenharmony_ci					 GFP_ATOMIC);
1228c2ecf20Sopenharmony_ci		if (skb_frag == NULL) {
1238c2ecf20Sopenharmony_ci			rc = -ENOMEM;
1248c2ecf20Sopenharmony_ci			goto free_exit;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		skb_reserve(skb_frag, NCI_DATA_HDR_SIZE);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		/* first, copy the data */
1298c2ecf20Sopenharmony_ci		skb_put_data(skb_frag, data, frag_len);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		/* second, set the header */
1328c2ecf20Sopenharmony_ci		nci_push_data_hdr(ndev, conn_id, skb_frag,
1338c2ecf20Sopenharmony_ci				  ((total_len == frag_len) ?
1348c2ecf20Sopenharmony_ci				   (NCI_PBF_LAST) : (NCI_PBF_CONT)));
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		__skb_queue_tail(&frags_q, skb_frag);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		data += frag_len;
1398c2ecf20Sopenharmony_ci		total_len -= frag_len;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		pr_debug("frag_len %d, remaining total_len %d\n",
1428c2ecf20Sopenharmony_ci			 frag_len, total_len);
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* queue all fragments atomically */
1468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndev->tx_q.lock, flags);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	while ((skb_frag = __skb_dequeue(&frags_q)) != NULL)
1498c2ecf20Sopenharmony_ci		__skb_queue_tail(&ndev->tx_q, skb_frag);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndev->tx_q.lock, flags);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* free the original skb */
1548c2ecf20Sopenharmony_ci	kfree_skb(skb);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	goto exit;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cifree_exit:
1598c2ecf20Sopenharmony_ci	while ((skb_frag = __skb_dequeue(&frags_q)) != NULL)
1608c2ecf20Sopenharmony_ci		kfree_skb(skb_frag);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciexit:
1638c2ecf20Sopenharmony_ci	return rc;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/* Send NCI data */
1678c2ecf20Sopenharmony_ciint nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct nci_conn_info    *conn_info;
1708c2ecf20Sopenharmony_ci	int rc = 0;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	pr_debug("conn_id 0x%x, plen %d\n", conn_id, skb->len);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
1758c2ecf20Sopenharmony_ci	if (!conn_info) {
1768c2ecf20Sopenharmony_ci		rc = -EPROTO;
1778c2ecf20Sopenharmony_ci		goto free_exit;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* check if the packet need to be fragmented */
1818c2ecf20Sopenharmony_ci	if (skb->len <= conn_info->max_pkt_payload_len) {
1828c2ecf20Sopenharmony_ci		/* no need to fragment packet */
1838c2ecf20Sopenharmony_ci		nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		skb_queue_tail(&ndev->tx_q, skb);
1868c2ecf20Sopenharmony_ci	} else {
1878c2ecf20Sopenharmony_ci		/* fragment packet and queue the fragments */
1888c2ecf20Sopenharmony_ci		rc = nci_queue_tx_data_frags(ndev, conn_id, skb);
1898c2ecf20Sopenharmony_ci		if (rc) {
1908c2ecf20Sopenharmony_ci			pr_err("failed to fragment tx data packet\n");
1918c2ecf20Sopenharmony_ci			goto free_exit;
1928c2ecf20Sopenharmony_ci		}
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ndev->cur_conn_id = conn_id;
1968c2ecf20Sopenharmony_ci	queue_work(ndev->tx_wq, &ndev->tx_work);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	goto exit;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cifree_exit:
2018c2ecf20Sopenharmony_ci	kfree_skb(skb);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciexit:
2048c2ecf20Sopenharmony_ci	return rc;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_send_data);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* ----------------- NCI RX Data ----------------- */
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic void nci_add_rx_data_frag(struct nci_dev *ndev,
2118c2ecf20Sopenharmony_ci				 struct sk_buff *skb,
2128c2ecf20Sopenharmony_ci				 __u8 pbf, __u8 conn_id, __u8 status)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int reassembly_len;
2158c2ecf20Sopenharmony_ci	int err = 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (status) {
2188c2ecf20Sopenharmony_ci		err = status;
2198c2ecf20Sopenharmony_ci		goto exit;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (ndev->rx_data_reassembly) {
2238c2ecf20Sopenharmony_ci		reassembly_len = ndev->rx_data_reassembly->len;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		/* first, make enough room for the already accumulated data */
2268c2ecf20Sopenharmony_ci		if (skb_cow_head(skb, reassembly_len)) {
2278c2ecf20Sopenharmony_ci			pr_err("error adding room for accumulated rx data\n");
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci			kfree_skb(skb);
2308c2ecf20Sopenharmony_ci			skb = NULL;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci			kfree_skb(ndev->rx_data_reassembly);
2338c2ecf20Sopenharmony_ci			ndev->rx_data_reassembly = NULL;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci			err = -ENOMEM;
2368c2ecf20Sopenharmony_ci			goto exit;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		/* second, combine the two fragments */
2408c2ecf20Sopenharmony_ci		memcpy(skb_push(skb, reassembly_len),
2418c2ecf20Sopenharmony_ci		       ndev->rx_data_reassembly->data,
2428c2ecf20Sopenharmony_ci		       reassembly_len);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		/* third, free old reassembly */
2458c2ecf20Sopenharmony_ci		kfree_skb(ndev->rx_data_reassembly);
2468c2ecf20Sopenharmony_ci		ndev->rx_data_reassembly = NULL;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (pbf == NCI_PBF_CONT) {
2508c2ecf20Sopenharmony_ci		/* need to wait for next fragment, store skb and exit */
2518c2ecf20Sopenharmony_ci		ndev->rx_data_reassembly = skb;
2528c2ecf20Sopenharmony_ci		return;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ciexit:
2568c2ecf20Sopenharmony_ci	if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
2578c2ecf20Sopenharmony_ci		/* Data received in Target mode, forward to nfc core */
2588c2ecf20Sopenharmony_ci		err = nfc_tm_data_received(ndev->nfc_dev, skb);
2598c2ecf20Sopenharmony_ci		if (err)
2608c2ecf20Sopenharmony_ci			pr_err("unable to handle received data\n");
2618c2ecf20Sopenharmony_ci	} else {
2628c2ecf20Sopenharmony_ci		nci_data_exchange_complete(ndev, skb, conn_id, err);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/* Rx Data packet */
2678c2ecf20Sopenharmony_civoid nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	__u8 pbf = nci_pbf(skb->data);
2708c2ecf20Sopenharmony_ci	__u8 status = 0;
2718c2ecf20Sopenharmony_ci	__u8 conn_id = nci_conn_id(skb->data);
2728c2ecf20Sopenharmony_ci	struct nci_conn_info    *conn_info;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	pr_debug("len %d\n", skb->len);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	pr_debug("NCI RX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
2778c2ecf20Sopenharmony_ci		 nci_pbf(skb->data),
2788c2ecf20Sopenharmony_ci		 nci_conn_id(skb->data),
2798c2ecf20Sopenharmony_ci		 nci_plen(skb->data));
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, nci_conn_id(skb->data));
2828c2ecf20Sopenharmony_ci	if (!conn_info) {
2838c2ecf20Sopenharmony_ci		kfree_skb(skb);
2848c2ecf20Sopenharmony_ci		return;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* strip the nci data header */
2888c2ecf20Sopenharmony_ci	skb_pull(skb, NCI_DATA_HDR_SIZE);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (ndev->target_active_prot == NFC_PROTO_MIFARE ||
2918c2ecf20Sopenharmony_ci	    ndev->target_active_prot == NFC_PROTO_JEWEL ||
2928c2ecf20Sopenharmony_ci	    ndev->target_active_prot == NFC_PROTO_FELICA ||
2938c2ecf20Sopenharmony_ci	    ndev->target_active_prot == NFC_PROTO_ISO15693) {
2948c2ecf20Sopenharmony_ci		/* frame I/F => remove the status byte */
2958c2ecf20Sopenharmony_ci		pr_debug("frame I/F => remove the status byte\n");
2968c2ecf20Sopenharmony_ci		status = skb->data[skb->len - 1];
2978c2ecf20Sopenharmony_ci		skb_trim(skb, (skb->len - 1));
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	nci_add_rx_data_frag(ndev, skb, pbf, conn_id, nci_to_errno(status));
3018c2ecf20Sopenharmony_ci}
302