18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2011  Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/nfc.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <net/nfc/nfc.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "nfc.h"
168c2ecf20Sopenharmony_ci#include "llcp.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic const u8 llcp_tlv_length[LLCP_TLV_MAX] = {
198c2ecf20Sopenharmony_ci	0,
208c2ecf20Sopenharmony_ci	1, /* VERSION */
218c2ecf20Sopenharmony_ci	2, /* MIUX */
228c2ecf20Sopenharmony_ci	2, /* WKS */
238c2ecf20Sopenharmony_ci	1, /* LTO */
248c2ecf20Sopenharmony_ci	1, /* RW */
258c2ecf20Sopenharmony_ci	0, /* SN */
268c2ecf20Sopenharmony_ci	1, /* OPT */
278c2ecf20Sopenharmony_ci	0, /* SDREQ */
288c2ecf20Sopenharmony_ci	2, /* SDRES */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic u8 llcp_tlv8(const u8 *tlv, u8 type)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]])
358c2ecf20Sopenharmony_ci		return 0;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	return tlv[2];
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic u16 llcp_tlv16(const u8 *tlv, u8 type)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]])
438c2ecf20Sopenharmony_ci		return 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return be16_to_cpu(*((__be16 *)(tlv + 2)));
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic u8 llcp_tlv_version(const u8 *tlv)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return llcp_tlv8(tlv, LLCP_TLV_VERSION);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic u16 llcp_tlv_miux(const u8 *tlv)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7ff;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic u16 llcp_tlv_wks(const u8 *tlv)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	return llcp_tlv16(tlv, LLCP_TLV_WKS);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic u16 llcp_tlv_lto(const u8 *tlv)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return llcp_tlv8(tlv, LLCP_TLV_LTO);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic u8 llcp_tlv_opt(const u8 *tlv)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	return llcp_tlv8(tlv, LLCP_TLV_OPT);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic u8 llcp_tlv_rw(const u8 *tlv)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	return llcp_tlv8(tlv, LLCP_TLV_RW) & 0xf;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciu8 *nfc_llcp_build_tlv(u8 type, const u8 *value, u8 value_length, u8 *tlv_length)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	u8 *tlv, length;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	pr_debug("type %d\n", type);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (type >= LLCP_TLV_MAX)
868c2ecf20Sopenharmony_ci		return NULL;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	length = llcp_tlv_length[type];
898c2ecf20Sopenharmony_ci	if (length == 0 && value_length == 0)
908c2ecf20Sopenharmony_ci		return NULL;
918c2ecf20Sopenharmony_ci	else if (length == 0)
928c2ecf20Sopenharmony_ci		length = value_length;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	*tlv_length = 2 + length;
958c2ecf20Sopenharmony_ci	tlv = kzalloc(2 + length, GFP_KERNEL);
968c2ecf20Sopenharmony_ci	if (tlv == NULL)
978c2ecf20Sopenharmony_ci		return tlv;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	tlv[0] = type;
1008c2ecf20Sopenharmony_ci	tlv[1] = length;
1018c2ecf20Sopenharmony_ci	memcpy(tlv + 2, value, length);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return tlv;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistruct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct nfc_llcp_sdp_tlv *sdres;
1098c2ecf20Sopenharmony_ci	u8 value[2];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
1128c2ecf20Sopenharmony_ci	if (sdres == NULL)
1138c2ecf20Sopenharmony_ci		return NULL;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	value[0] = tid;
1168c2ecf20Sopenharmony_ci	value[1] = sap;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2,
1198c2ecf20Sopenharmony_ci					&sdres->tlv_len);
1208c2ecf20Sopenharmony_ci	if (sdres->tlv == NULL) {
1218c2ecf20Sopenharmony_ci		kfree(sdres);
1228c2ecf20Sopenharmony_ci		return NULL;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	sdres->tid = tid;
1268c2ecf20Sopenharmony_ci	sdres->sap = sap;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&sdres->node);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return sdres;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistruct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, const char *uri,
1348c2ecf20Sopenharmony_ci						  size_t uri_len)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct nfc_llcp_sdp_tlv *sdreq;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	pr_debug("uri: %s, len: %zu\n", uri, uri_len);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* sdreq->tlv_len is u8, takes uri_len, + 3 for header, + 1 for NULL */
1418c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(uri_len > U8_MAX - 4))
1428c2ecf20Sopenharmony_ci		return NULL;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
1458c2ecf20Sopenharmony_ci	if (sdreq == NULL)
1468c2ecf20Sopenharmony_ci		return NULL;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	sdreq->tlv_len = uri_len + 3;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (uri[uri_len - 1] == 0)
1518c2ecf20Sopenharmony_ci		sdreq->tlv_len--;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL);
1548c2ecf20Sopenharmony_ci	if (sdreq->tlv == NULL) {
1558c2ecf20Sopenharmony_ci		kfree(sdreq);
1568c2ecf20Sopenharmony_ci		return NULL;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	sdreq->tlv[0] = LLCP_TLV_SDREQ;
1608c2ecf20Sopenharmony_ci	sdreq->tlv[1] = sdreq->tlv_len - 2;
1618c2ecf20Sopenharmony_ci	sdreq->tlv[2] = tid;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	sdreq->tid = tid;
1648c2ecf20Sopenharmony_ci	sdreq->uri = sdreq->tlv + 3;
1658c2ecf20Sopenharmony_ci	memcpy(sdreq->uri, uri, uri_len);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	sdreq->time = jiffies;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&sdreq->node);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return sdreq;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_civoid nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	kfree(sdp->tlv);
1778c2ecf20Sopenharmony_ci	kfree(sdp);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_civoid nfc_llcp_free_sdp_tlv_list(struct hlist_head *head)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct nfc_llcp_sdp_tlv *sdp;
1838c2ecf20Sopenharmony_ci	struct hlist_node *n;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(sdp, n, head, node) {
1868c2ecf20Sopenharmony_ci		hlist_del(&sdp->node);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		nfc_llcp_free_sdp_tlv(sdp);
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ciint nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
1938c2ecf20Sopenharmony_ci			  const u8 *tlv_array, u16 tlv_array_len)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	const u8 *tlv = tlv_array;
1968c2ecf20Sopenharmony_ci	u8 type, length, offset = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	pr_debug("TLV array length %d\n", tlv_array_len);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (local == NULL)
2018c2ecf20Sopenharmony_ci		return -ENODEV;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	while (offset < tlv_array_len) {
2048c2ecf20Sopenharmony_ci		type = tlv[0];
2058c2ecf20Sopenharmony_ci		length = tlv[1];
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		pr_debug("type 0x%x length %d\n", type, length);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		switch (type) {
2108c2ecf20Sopenharmony_ci		case LLCP_TLV_VERSION:
2118c2ecf20Sopenharmony_ci			local->remote_version = llcp_tlv_version(tlv);
2128c2ecf20Sopenharmony_ci			break;
2138c2ecf20Sopenharmony_ci		case LLCP_TLV_MIUX:
2148c2ecf20Sopenharmony_ci			local->remote_miu = llcp_tlv_miux(tlv) + 128;
2158c2ecf20Sopenharmony_ci			break;
2168c2ecf20Sopenharmony_ci		case LLCP_TLV_WKS:
2178c2ecf20Sopenharmony_ci			local->remote_wks = llcp_tlv_wks(tlv);
2188c2ecf20Sopenharmony_ci			break;
2198c2ecf20Sopenharmony_ci		case LLCP_TLV_LTO:
2208c2ecf20Sopenharmony_ci			local->remote_lto = llcp_tlv_lto(tlv) * 10;
2218c2ecf20Sopenharmony_ci			break;
2228c2ecf20Sopenharmony_ci		case LLCP_TLV_OPT:
2238c2ecf20Sopenharmony_ci			local->remote_opt = llcp_tlv_opt(tlv);
2248c2ecf20Sopenharmony_ci			break;
2258c2ecf20Sopenharmony_ci		default:
2268c2ecf20Sopenharmony_ci			pr_err("Invalid gt tlv value 0x%x\n", type);
2278c2ecf20Sopenharmony_ci			break;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		offset += length + 2;
2318c2ecf20Sopenharmony_ci		tlv += length + 2;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x\n",
2358c2ecf20Sopenharmony_ci		 local->remote_version, local->remote_miu,
2368c2ecf20Sopenharmony_ci		 local->remote_lto, local->remote_opt,
2378c2ecf20Sopenharmony_ci		 local->remote_wks);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciint nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
2438c2ecf20Sopenharmony_ci				  const u8 *tlv_array, u16 tlv_array_len)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	const u8 *tlv = tlv_array;
2468c2ecf20Sopenharmony_ci	u8 type, length, offset = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	pr_debug("TLV array length %d\n", tlv_array_len);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (sock == NULL)
2518c2ecf20Sopenharmony_ci		return -ENOTCONN;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	while (offset < tlv_array_len) {
2548c2ecf20Sopenharmony_ci		type = tlv[0];
2558c2ecf20Sopenharmony_ci		length = tlv[1];
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		pr_debug("type 0x%x length %d\n", type, length);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		switch (type) {
2608c2ecf20Sopenharmony_ci		case LLCP_TLV_MIUX:
2618c2ecf20Sopenharmony_ci			sock->remote_miu = llcp_tlv_miux(tlv) + 128;
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci		case LLCP_TLV_RW:
2648c2ecf20Sopenharmony_ci			sock->remote_rw = llcp_tlv_rw(tlv);
2658c2ecf20Sopenharmony_ci			break;
2668c2ecf20Sopenharmony_ci		case LLCP_TLV_SN:
2678c2ecf20Sopenharmony_ci			break;
2688c2ecf20Sopenharmony_ci		default:
2698c2ecf20Sopenharmony_ci			pr_err("Invalid gt tlv value 0x%x\n", type);
2708c2ecf20Sopenharmony_ci			break;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		offset += length + 2;
2748c2ecf20Sopenharmony_ci		tlv += length + 2;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	pr_debug("sock %p rw %d miu %d\n", sock,
2788c2ecf20Sopenharmony_ci		 sock->remote_rw, sock->remote_miu);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic struct sk_buff *llcp_add_header(struct sk_buff *pdu,
2848c2ecf20Sopenharmony_ci				       u8 dsap, u8 ssap, u8 ptype)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	u8 header[2];
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	header[0] = (u8)((dsap << 2) | (ptype >> 2));
2918c2ecf20Sopenharmony_ci	header[1] = (u8)((ptype << 6) | ssap);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	pr_debug("header 0x%x 0x%x\n", header[0], header[1]);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	skb_put_data(pdu, header, LLCP_HEADER_SIZE);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return pdu;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic struct sk_buff *llcp_add_tlv(struct sk_buff *pdu, const u8 *tlv,
3018c2ecf20Sopenharmony_ci				    u8 tlv_length)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	/* XXX Add an skb length check */
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (tlv == NULL)
3068c2ecf20Sopenharmony_ci		return NULL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	skb_put_data(pdu, tlv, tlv_length);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return pdu;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock,
3148c2ecf20Sopenharmony_ci					 u8 cmd, u16 size)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3178c2ecf20Sopenharmony_ci	int err;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (sock->ssap == 0)
3208c2ecf20Sopenharmony_ci		return NULL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	skb = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT,
3238c2ecf20Sopenharmony_ci				 size + LLCP_HEADER_SIZE, &err);
3248c2ecf20Sopenharmony_ci	if (skb == NULL) {
3258c2ecf20Sopenharmony_ci		pr_err("Could not allocate PDU\n");
3268c2ecf20Sopenharmony_ci		return NULL;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	skb = llcp_add_header(skb, sock->dsap, sock->ssap, cmd);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return skb;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ciint nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3378c2ecf20Sopenharmony_ci	struct nfc_dev *dev;
3388c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	pr_debug("Sending DISC\n");
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	local = sock->local;
3438c2ecf20Sopenharmony_ci	if (local == NULL)
3448c2ecf20Sopenharmony_ci		return -ENODEV;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	dev = sock->dev;
3478c2ecf20Sopenharmony_ci	if (dev == NULL)
3488c2ecf20Sopenharmony_ci		return -ENODEV;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
3518c2ecf20Sopenharmony_ci	if (skb == NULL)
3528c2ecf20Sopenharmony_ci		return -ENOMEM;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	skb_queue_tail(&local->tx_queue, skb);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ciint nfc_llcp_send_symm(struct nfc_dev *dev)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3628c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
3638c2ecf20Sopenharmony_ci	u16 size = 0;
3648c2ecf20Sopenharmony_ci	int err;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	pr_debug("Sending SYMM\n");
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	local = nfc_llcp_find_local(dev);
3698c2ecf20Sopenharmony_ci	if (local == NULL)
3708c2ecf20Sopenharmony_ci		return -ENODEV;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	size += LLCP_HEADER_SIZE;
3738c2ecf20Sopenharmony_ci	size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	skb = alloc_skb(size, GFP_KERNEL);
3768c2ecf20Sopenharmony_ci	if (skb == NULL) {
3778c2ecf20Sopenharmony_ci		err = -ENOMEM;
3788c2ecf20Sopenharmony_ci		goto out;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	__net_timestamp(skb);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	err = nfc_data_exchange(dev, local->target_idx, skb,
3908c2ecf20Sopenharmony_ci				 nfc_llcp_recv, local);
3918c2ecf20Sopenharmony_ciout:
3928c2ecf20Sopenharmony_ci	nfc_llcp_local_put(local);
3938c2ecf20Sopenharmony_ci	return err;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ciint nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
3998c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4008c2ecf20Sopenharmony_ci	const u8 *service_name_tlv = NULL;
4018c2ecf20Sopenharmony_ci	const u8 *miux_tlv = NULL;
4028c2ecf20Sopenharmony_ci	const u8 *rw_tlv = NULL;
4038c2ecf20Sopenharmony_ci	u8 service_name_tlv_length = 0;
4048c2ecf20Sopenharmony_ci	u8 miux_tlv_length,  rw_tlv_length, rw;
4058c2ecf20Sopenharmony_ci	int err;
4068c2ecf20Sopenharmony_ci	u16 size = 0;
4078c2ecf20Sopenharmony_ci	__be16 miux;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	pr_debug("Sending CONNECT\n");
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	local = sock->local;
4128c2ecf20Sopenharmony_ci	if (local == NULL)
4138c2ecf20Sopenharmony_ci		return -ENODEV;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (sock->service_name != NULL) {
4168c2ecf20Sopenharmony_ci		service_name_tlv = nfc_llcp_build_tlv(LLCP_TLV_SN,
4178c2ecf20Sopenharmony_ci						      sock->service_name,
4188c2ecf20Sopenharmony_ci						      sock->service_name_len,
4198c2ecf20Sopenharmony_ci						      &service_name_tlv_length);
4208c2ecf20Sopenharmony_ci		if (!service_name_tlv) {
4218c2ecf20Sopenharmony_ci			err = -ENOMEM;
4228c2ecf20Sopenharmony_ci			goto error_tlv;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci		size += service_name_tlv_length;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* If the socket parameters are not set, use the local ones */
4288c2ecf20Sopenharmony_ci	miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ?
4298c2ecf20Sopenharmony_ci		local->miux : sock->miux;
4308c2ecf20Sopenharmony_ci	rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
4338c2ecf20Sopenharmony_ci				      &miux_tlv_length);
4348c2ecf20Sopenharmony_ci	if (!miux_tlv) {
4358c2ecf20Sopenharmony_ci		err = -ENOMEM;
4368c2ecf20Sopenharmony_ci		goto error_tlv;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci	size += miux_tlv_length;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
4418c2ecf20Sopenharmony_ci	if (!rw_tlv) {
4428c2ecf20Sopenharmony_ci		err = -ENOMEM;
4438c2ecf20Sopenharmony_ci		goto error_tlv;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci	size += rw_tlv_length;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	skb = llcp_allocate_pdu(sock, LLCP_PDU_CONNECT, size);
4508c2ecf20Sopenharmony_ci	if (skb == NULL) {
4518c2ecf20Sopenharmony_ci		err = -ENOMEM;
4528c2ecf20Sopenharmony_ci		goto error_tlv;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	llcp_add_tlv(skb, service_name_tlv, service_name_tlv_length);
4568c2ecf20Sopenharmony_ci	llcp_add_tlv(skb, miux_tlv, miux_tlv_length);
4578c2ecf20Sopenharmony_ci	llcp_add_tlv(skb, rw_tlv, rw_tlv_length);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	skb_queue_tail(&local->tx_queue, skb);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	err = 0;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cierror_tlv:
4648c2ecf20Sopenharmony_ci	if (err)
4658c2ecf20Sopenharmony_ci		pr_err("error %d\n", err);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	kfree(service_name_tlv);
4688c2ecf20Sopenharmony_ci	kfree(miux_tlv);
4698c2ecf20Sopenharmony_ci	kfree(rw_tlv);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return err;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ciint nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
4778c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4788c2ecf20Sopenharmony_ci	const u8 *miux_tlv = NULL;
4798c2ecf20Sopenharmony_ci	const u8 *rw_tlv = NULL;
4808c2ecf20Sopenharmony_ci	u8 miux_tlv_length, rw_tlv_length, rw;
4818c2ecf20Sopenharmony_ci	int err;
4828c2ecf20Sopenharmony_ci	u16 size = 0;
4838c2ecf20Sopenharmony_ci	__be16 miux;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	pr_debug("Sending CC\n");
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	local = sock->local;
4888c2ecf20Sopenharmony_ci	if (local == NULL)
4898c2ecf20Sopenharmony_ci		return -ENODEV;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* If the socket parameters are not set, use the local ones */
4928c2ecf20Sopenharmony_ci	miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ?
4938c2ecf20Sopenharmony_ci		local->miux : sock->miux;
4948c2ecf20Sopenharmony_ci	rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
4978c2ecf20Sopenharmony_ci				      &miux_tlv_length);
4988c2ecf20Sopenharmony_ci	if (!miux_tlv) {
4998c2ecf20Sopenharmony_ci		err = -ENOMEM;
5008c2ecf20Sopenharmony_ci		goto error_tlv;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	size += miux_tlv_length;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
5058c2ecf20Sopenharmony_ci	if (!rw_tlv) {
5068c2ecf20Sopenharmony_ci		err = -ENOMEM;
5078c2ecf20Sopenharmony_ci		goto error_tlv;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci	size += rw_tlv_length;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size);
5128c2ecf20Sopenharmony_ci	if (skb == NULL) {
5138c2ecf20Sopenharmony_ci		err = -ENOMEM;
5148c2ecf20Sopenharmony_ci		goto error_tlv;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	llcp_add_tlv(skb, miux_tlv, miux_tlv_length);
5188c2ecf20Sopenharmony_ci	llcp_add_tlv(skb, rw_tlv, rw_tlv_length);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	skb_queue_tail(&local->tx_queue, skb);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	err = 0;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cierror_tlv:
5258c2ecf20Sopenharmony_ci	if (err)
5268c2ecf20Sopenharmony_ci		pr_err("error %d\n", err);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	kfree(miux_tlv);
5298c2ecf20Sopenharmony_ci	kfree(rw_tlv);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return err;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local,
5358c2ecf20Sopenharmony_ci					     size_t tlv_length)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5388c2ecf20Sopenharmony_ci	struct nfc_dev *dev;
5398c2ecf20Sopenharmony_ci	u16 size = 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (local == NULL)
5428c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	dev = local->dev;
5458c2ecf20Sopenharmony_ci	if (dev == NULL)
5468c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	size += LLCP_HEADER_SIZE;
5498c2ecf20Sopenharmony_ci	size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
5508c2ecf20Sopenharmony_ci	size += tlv_length;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	skb = alloc_skb(size, GFP_KERNEL);
5538c2ecf20Sopenharmony_ci	if (skb == NULL)
5548c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	return skb;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ciint nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
5648c2ecf20Sopenharmony_ci			    struct hlist_head *tlv_list, size_t tlvs_len)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct nfc_llcp_sdp_tlv *sdp;
5678c2ecf20Sopenharmony_ci	struct hlist_node *n;
5688c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	skb = nfc_llcp_allocate_snl(local, tlvs_len);
5718c2ecf20Sopenharmony_ci	if (IS_ERR(skb))
5728c2ecf20Sopenharmony_ci		return PTR_ERR(skb);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(sdp, n, tlv_list, node) {
5758c2ecf20Sopenharmony_ci		skb_put_data(skb, sdp->tlv, sdp->tlv_len);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		hlist_del(&sdp->node);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		nfc_llcp_free_sdp_tlv(sdp);
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	skb_queue_tail(&local->tx_queue, skb);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return 0;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ciint nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
5888c2ecf20Sopenharmony_ci			    struct hlist_head *tlv_list, size_t tlvs_len)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct nfc_llcp_sdp_tlv *sdreq;
5918c2ecf20Sopenharmony_ci	struct hlist_node *n;
5928c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	skb = nfc_llcp_allocate_snl(local, tlvs_len);
5958c2ecf20Sopenharmony_ci	if (IS_ERR(skb))
5968c2ecf20Sopenharmony_ci		return PTR_ERR(skb);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	mutex_lock(&local->sdreq_lock);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (hlist_empty(&local->pending_sdreqs))
6018c2ecf20Sopenharmony_ci		mod_timer(&local->sdreq_timer,
6028c2ecf20Sopenharmony_ci			  jiffies + msecs_to_jiffies(3 * local->remote_lto));
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(sdreq, n, tlv_list, node) {
6058c2ecf20Sopenharmony_ci		pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci		skb_put_data(skb, sdreq->tlv, sdreq->tlv_len);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		hlist_del(&sdreq->node);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		hlist_add_head(&sdreq->node, &local->pending_sdreqs);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	mutex_unlock(&local->sdreq_lock);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	skb_queue_tail(&local->tx_queue, skb);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return 0;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ciint nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6248c2ecf20Sopenharmony_ci	struct nfc_dev *dev;
6258c2ecf20Sopenharmony_ci	u16 size = 1; /* Reason code */
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	pr_debug("Sending DM reason 0x%x\n", reason);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (local == NULL)
6308c2ecf20Sopenharmony_ci		return -ENODEV;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	dev = local->dev;
6338c2ecf20Sopenharmony_ci	if (dev == NULL)
6348c2ecf20Sopenharmony_ci		return -ENODEV;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	size += LLCP_HEADER_SIZE;
6378c2ecf20Sopenharmony_ci	size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	skb = alloc_skb(size, GFP_KERNEL);
6408c2ecf20Sopenharmony_ci	if (skb == NULL)
6418c2ecf20Sopenharmony_ci		return -ENOMEM;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	skb = llcp_add_header(skb, dsap, ssap, LLCP_PDU_DM);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	skb_put_data(skb, &reason, 1);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	skb_queue_head(&local->tx_queue, skb);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	return 0;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ciint nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
6558c2ecf20Sopenharmony_ci			  struct msghdr *msg, size_t len)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct sk_buff *pdu;
6588c2ecf20Sopenharmony_ci	struct sock *sk = &sock->sk;
6598c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
6608c2ecf20Sopenharmony_ci	size_t frag_len = 0, remaining_len;
6618c2ecf20Sopenharmony_ci	u8 *msg_data, *msg_ptr;
6628c2ecf20Sopenharmony_ci	u16 remote_miu;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	pr_debug("Send I frame len %zd\n", len);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	local = sock->local;
6678c2ecf20Sopenharmony_ci	if (local == NULL)
6688c2ecf20Sopenharmony_ci		return -ENODEV;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* Remote is ready but has not acknowledged our frames */
6718c2ecf20Sopenharmony_ci	if((sock->remote_ready &&
6728c2ecf20Sopenharmony_ci	    skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw &&
6738c2ecf20Sopenharmony_ci	    skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
6748c2ecf20Sopenharmony_ci		pr_err("Pending queue is full %d frames\n",
6758c2ecf20Sopenharmony_ci		       skb_queue_len(&sock->tx_pending_queue));
6768c2ecf20Sopenharmony_ci		return -ENOBUFS;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	/* Remote is not ready and we've been queueing enough frames */
6808c2ecf20Sopenharmony_ci	if ((!sock->remote_ready &&
6818c2ecf20Sopenharmony_ci	     skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
6828c2ecf20Sopenharmony_ci		pr_err("Tx queue is full %d frames\n",
6838c2ecf20Sopenharmony_ci		       skb_queue_len(&sock->tx_queue));
6848c2ecf20Sopenharmony_ci		return -ENOBUFS;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN);
6888c2ecf20Sopenharmony_ci	if (msg_data == NULL)
6898c2ecf20Sopenharmony_ci		return -ENOMEM;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (memcpy_from_msg(msg_data, msg, len)) {
6928c2ecf20Sopenharmony_ci		kfree(msg_data);
6938c2ecf20Sopenharmony_ci		return -EFAULT;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	remaining_len = len;
6978c2ecf20Sopenharmony_ci	msg_ptr = msg_data;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	do {
7008c2ecf20Sopenharmony_ci		remote_miu = sock->remote_miu > LLCP_MAX_MIU ?
7018c2ecf20Sopenharmony_ci				LLCP_DEFAULT_MIU : sock->remote_miu;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		frag_len = min_t(size_t, remote_miu, remaining_len);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci		pr_debug("Fragment %zd bytes remaining %zd",
7068c2ecf20Sopenharmony_ci			 frag_len, remaining_len);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		pdu = llcp_allocate_pdu(sock, LLCP_PDU_I,
7098c2ecf20Sopenharmony_ci					frag_len + LLCP_SEQUENCE_SIZE);
7108c2ecf20Sopenharmony_ci		if (pdu == NULL) {
7118c2ecf20Sopenharmony_ci			kfree(msg_data);
7128c2ecf20Sopenharmony_ci			return -ENOMEM;
7138c2ecf20Sopenharmony_ci		}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci		skb_put(pdu, LLCP_SEQUENCE_SIZE);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		if (likely(frag_len > 0))
7188c2ecf20Sopenharmony_ci			skb_put_data(pdu, msg_ptr, frag_len);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		skb_queue_tail(&sock->tx_queue, pdu);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		lock_sock(sk);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		nfc_llcp_queue_i_frames(sock);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		release_sock(sk);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		remaining_len -= frag_len;
7298c2ecf20Sopenharmony_ci		msg_ptr += frag_len;
7308c2ecf20Sopenharmony_ci	} while (remaining_len > 0);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	kfree(msg_data);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return len;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ciint nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
7388c2ecf20Sopenharmony_ci			   struct msghdr *msg, size_t len)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct sk_buff *pdu;
7418c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
7428c2ecf20Sopenharmony_ci	size_t frag_len = 0, remaining_len;
7438c2ecf20Sopenharmony_ci	u8 *msg_ptr, *msg_data;
7448c2ecf20Sopenharmony_ci	u16 remote_miu;
7458c2ecf20Sopenharmony_ci	int err;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	pr_debug("Send UI frame len %zd\n", len);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	local = sock->local;
7508c2ecf20Sopenharmony_ci	if (local == NULL)
7518c2ecf20Sopenharmony_ci		return -ENODEV;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN);
7548c2ecf20Sopenharmony_ci	if (msg_data == NULL)
7558c2ecf20Sopenharmony_ci		return -ENOMEM;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (memcpy_from_msg(msg_data, msg, len)) {
7588c2ecf20Sopenharmony_ci		kfree(msg_data);
7598c2ecf20Sopenharmony_ci		return -EFAULT;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	remaining_len = len;
7638c2ecf20Sopenharmony_ci	msg_ptr = msg_data;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	do {
7668c2ecf20Sopenharmony_ci		remote_miu = sock->remote_miu > LLCP_MAX_MIU ?
7678c2ecf20Sopenharmony_ci				local->remote_miu : sock->remote_miu;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		frag_len = min_t(size_t, remote_miu, remaining_len);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci		pr_debug("Fragment %zd bytes remaining %zd",
7728c2ecf20Sopenharmony_ci			 frag_len, remaining_len);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, 0,
7758c2ecf20Sopenharmony_ci					 frag_len + LLCP_HEADER_SIZE, &err);
7768c2ecf20Sopenharmony_ci		if (pdu == NULL) {
7778c2ecf20Sopenharmony_ci			pr_err("Could not allocate PDU (error=%d)\n", err);
7788c2ecf20Sopenharmony_ci			len -= remaining_len;
7798c2ecf20Sopenharmony_ci			if (len == 0)
7808c2ecf20Sopenharmony_ci				len = err;
7818c2ecf20Sopenharmony_ci			break;
7828c2ecf20Sopenharmony_ci		}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci		if (likely(frag_len > 0))
7878c2ecf20Sopenharmony_ci			skb_put_data(pdu, msg_ptr, frag_len);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		/* No need to check for the peer RW for UI frames */
7908c2ecf20Sopenharmony_ci		skb_queue_tail(&local->tx_queue, pdu);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		remaining_len -= frag_len;
7938c2ecf20Sopenharmony_ci		msg_ptr += frag_len;
7948c2ecf20Sopenharmony_ci	} while (remaining_len > 0);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	kfree(msg_data);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	return len;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ciint nfc_llcp_send_rr(struct nfc_llcp_sock *sock)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8048c2ecf20Sopenharmony_ci	struct nfc_llcp_local *local;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	pr_debug("Send rr nr %d\n", sock->recv_n);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	local = sock->local;
8098c2ecf20Sopenharmony_ci	if (local == NULL)
8108c2ecf20Sopenharmony_ci		return -ENODEV;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	skb = llcp_allocate_pdu(sock, LLCP_PDU_RR, LLCP_SEQUENCE_SIZE);
8138c2ecf20Sopenharmony_ci	if (skb == NULL)
8148c2ecf20Sopenharmony_ci		return -ENOMEM;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	skb_put(skb, LLCP_SEQUENCE_SIZE);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	skb->data[2] = sock->recv_n;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	skb_queue_head(&local->tx_queue, skb);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	return 0;
8238c2ecf20Sopenharmony_ci}
824