11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 230cc4587SSamuel Ortiz /* 330cc4587SSamuel Ortiz * Copyright (C) 2011 Intel Corporation. All rights reserved. 430cc4587SSamuel Ortiz */ 530cc4587SSamuel Ortiz 630cc4587SSamuel Ortiz #define pr_fmt(fmt) "llcp: %s: " fmt, __func__ 730cc4587SSamuel Ortiz 830cc4587SSamuel Ortiz #include <linux/init.h> 930cc4587SSamuel Ortiz #include <linux/kernel.h> 1030cc4587SSamuel Ortiz #include <linux/module.h> 1130cc4587SSamuel Ortiz #include <linux/nfc.h> 1230cc4587SSamuel Ortiz 1330cc4587SSamuel Ortiz #include <net/nfc/nfc.h> 1430cc4587SSamuel Ortiz 1530cc4587SSamuel Ortiz #include "nfc.h" 1630cc4587SSamuel Ortiz #include "llcp.h" 1730cc4587SSamuel Ortiz 18*3df40eb3SKrzysztof Kozlowski static const u8 llcp_tlv_length[LLCP_TLV_MAX] = { 1930cc4587SSamuel Ortiz 0, 2030cc4587SSamuel Ortiz 1, /* VERSION */ 2130cc4587SSamuel Ortiz 2, /* MIUX */ 2230cc4587SSamuel Ortiz 2, /* WKS */ 2330cc4587SSamuel Ortiz 1, /* LTO */ 2430cc4587SSamuel Ortiz 1, /* RW */ 2530cc4587SSamuel Ortiz 0, /* SN */ 2630cc4587SSamuel Ortiz 1, /* OPT */ 2730cc4587SSamuel Ortiz 0, /* SDREQ */ 2830cc4587SSamuel Ortiz 2, /* SDRES */ 2930cc4587SSamuel Ortiz 3030cc4587SSamuel Ortiz }; 3130cc4587SSamuel Ortiz 32*3df40eb3SKrzysztof Kozlowski static u8 llcp_tlv8(const u8 *tlv, u8 type) 3330cc4587SSamuel Ortiz { 3430cc4587SSamuel Ortiz if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]]) 3530cc4587SSamuel Ortiz return 0; 3630cc4587SSamuel Ortiz 3730cc4587SSamuel Ortiz return tlv[2]; 3830cc4587SSamuel Ortiz } 3930cc4587SSamuel Ortiz 40*3df40eb3SKrzysztof Kozlowski static u16 llcp_tlv16(const u8 *tlv, u8 type) 4130cc4587SSamuel Ortiz { 4230cc4587SSamuel Ortiz if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]]) 4330cc4587SSamuel Ortiz return 0; 4430cc4587SSamuel Ortiz 4530cc4587SSamuel Ortiz return be16_to_cpu(*((__be16 *)(tlv + 2))); 4630cc4587SSamuel Ortiz } 4730cc4587SSamuel Ortiz 4830cc4587SSamuel Ortiz 49*3df40eb3SKrzysztof Kozlowski static u8 llcp_tlv_version(const u8 *tlv) 5030cc4587SSamuel Ortiz { 5130cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_VERSION); 5230cc4587SSamuel Ortiz } 5330cc4587SSamuel Ortiz 54*3df40eb3SKrzysztof Kozlowski static u16 llcp_tlv_miux(const u8 *tlv) 5530cc4587SSamuel Ortiz { 5630cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7ff; 5730cc4587SSamuel Ortiz } 5830cc4587SSamuel Ortiz 59*3df40eb3SKrzysztof Kozlowski static u16 llcp_tlv_wks(const u8 *tlv) 6030cc4587SSamuel Ortiz { 6130cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_WKS); 6230cc4587SSamuel Ortiz } 6330cc4587SSamuel Ortiz 64*3df40eb3SKrzysztof Kozlowski static u16 llcp_tlv_lto(const u8 *tlv) 6530cc4587SSamuel Ortiz { 6630cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_LTO); 6730cc4587SSamuel Ortiz } 6830cc4587SSamuel Ortiz 69*3df40eb3SKrzysztof Kozlowski static u8 llcp_tlv_opt(const u8 *tlv) 7030cc4587SSamuel Ortiz { 7130cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_OPT); 7230cc4587SSamuel Ortiz } 7330cc4587SSamuel Ortiz 74*3df40eb3SKrzysztof Kozlowski static u8 llcp_tlv_rw(const u8 *tlv) 7530cc4587SSamuel Ortiz { 7630cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_RW) & 0xf; 7730cc4587SSamuel Ortiz } 7830cc4587SSamuel Ortiz 79*3df40eb3SKrzysztof Kozlowski u8 *nfc_llcp_build_tlv(u8 type, const u8 *value, u8 value_length, u8 *tlv_length) 8030cc4587SSamuel Ortiz { 8130cc4587SSamuel Ortiz u8 *tlv, length; 8230cc4587SSamuel Ortiz 8330cc4587SSamuel Ortiz pr_debug("type %d\n", type); 8430cc4587SSamuel Ortiz 8530cc4587SSamuel Ortiz if (type >= LLCP_TLV_MAX) 8630cc4587SSamuel Ortiz return NULL; 8730cc4587SSamuel Ortiz 8830cc4587SSamuel Ortiz length = llcp_tlv_length[type]; 8930cc4587SSamuel Ortiz if (length == 0 && value_length == 0) 9030cc4587SSamuel Ortiz return NULL; 9130cc4587SSamuel Ortiz else if (length == 0) 9230cc4587SSamuel Ortiz length = value_length; 9330cc4587SSamuel Ortiz 9430cc4587SSamuel Ortiz *tlv_length = 2 + length; 9530cc4587SSamuel Ortiz tlv = kzalloc(2 + length, GFP_KERNEL); 9630cc4587SSamuel Ortiz if (tlv == NULL) 9730cc4587SSamuel Ortiz return tlv; 9830cc4587SSamuel Ortiz 9930cc4587SSamuel Ortiz tlv[0] = type; 10030cc4587SSamuel Ortiz tlv[1] = length; 10130cc4587SSamuel Ortiz memcpy(tlv + 2, value, length); 10230cc4587SSamuel Ortiz 10330cc4587SSamuel Ortiz return tlv; 10430cc4587SSamuel Ortiz } 10530cc4587SSamuel Ortiz 10630cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap) 10730cc4587SSamuel Ortiz { 10830cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdres; 10930cc4587SSamuel Ortiz u8 value[2]; 11030cc4587SSamuel Ortiz 11130cc4587SSamuel Ortiz sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); 11230cc4587SSamuel Ortiz if (sdres == NULL) 11330cc4587SSamuel Ortiz return NULL; 11430cc4587SSamuel Ortiz 11530cc4587SSamuel Ortiz value[0] = tid; 11630cc4587SSamuel Ortiz value[1] = sap; 11730cc4587SSamuel Ortiz 11830cc4587SSamuel Ortiz sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2, 11930cc4587SSamuel Ortiz &sdres->tlv_len); 12030cc4587SSamuel Ortiz if (sdres->tlv == NULL) { 12130cc4587SSamuel Ortiz kfree(sdres); 12230cc4587SSamuel Ortiz return NULL; 12330cc4587SSamuel Ortiz } 12430cc4587SSamuel Ortiz 12530cc4587SSamuel Ortiz sdres->tid = tid; 12630cc4587SSamuel Ortiz sdres->sap = sap; 12730cc4587SSamuel Ortiz 12830cc4587SSamuel Ortiz INIT_HLIST_NODE(&sdres->node); 12930cc4587SSamuel Ortiz 13030cc4587SSamuel Ortiz return sdres; 13130cc4587SSamuel Ortiz } 13230cc4587SSamuel Ortiz 133*3df40eb3SKrzysztof Kozlowski struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, const char *uri, 13430cc4587SSamuel Ortiz size_t uri_len) 13530cc4587SSamuel Ortiz { 13630cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 13730cc4587SSamuel Ortiz 13830cc4587SSamuel Ortiz pr_debug("uri: %s, len: %zu\n", uri, uri_len); 13930cc4587SSamuel Ortiz 140fe9c8426SKees Cook /* sdreq->tlv_len is u8, takes uri_len, + 3 for header, + 1 for NULL */ 141fe9c8426SKees Cook if (WARN_ON_ONCE(uri_len > U8_MAX - 4)) 142fe9c8426SKees Cook return NULL; 143fe9c8426SKees Cook 14430cc4587SSamuel Ortiz sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); 14530cc4587SSamuel Ortiz if (sdreq == NULL) 14630cc4587SSamuel Ortiz return NULL; 14730cc4587SSamuel Ortiz 14830cc4587SSamuel Ortiz sdreq->tlv_len = uri_len + 3; 14930cc4587SSamuel Ortiz 15030cc4587SSamuel Ortiz if (uri[uri_len - 1] == 0) 15130cc4587SSamuel Ortiz sdreq->tlv_len--; 15230cc4587SSamuel Ortiz 15330cc4587SSamuel Ortiz sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL); 15430cc4587SSamuel Ortiz if (sdreq->tlv == NULL) { 15530cc4587SSamuel Ortiz kfree(sdreq); 15630cc4587SSamuel Ortiz return NULL; 15730cc4587SSamuel Ortiz } 15830cc4587SSamuel Ortiz 15930cc4587SSamuel Ortiz sdreq->tlv[0] = LLCP_TLV_SDREQ; 16030cc4587SSamuel Ortiz sdreq->tlv[1] = sdreq->tlv_len - 2; 16130cc4587SSamuel Ortiz sdreq->tlv[2] = tid; 16230cc4587SSamuel Ortiz 16330cc4587SSamuel Ortiz sdreq->tid = tid; 16430cc4587SSamuel Ortiz sdreq->uri = sdreq->tlv + 3; 16530cc4587SSamuel Ortiz memcpy(sdreq->uri, uri, uri_len); 16630cc4587SSamuel Ortiz 16730cc4587SSamuel Ortiz sdreq->time = jiffies; 16830cc4587SSamuel Ortiz 16930cc4587SSamuel Ortiz INIT_HLIST_NODE(&sdreq->node); 17030cc4587SSamuel Ortiz 17130cc4587SSamuel Ortiz return sdreq; 17230cc4587SSamuel Ortiz } 17330cc4587SSamuel Ortiz 17430cc4587SSamuel Ortiz void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp) 17530cc4587SSamuel Ortiz { 17630cc4587SSamuel Ortiz kfree(sdp->tlv); 17730cc4587SSamuel Ortiz kfree(sdp); 17830cc4587SSamuel Ortiz } 17930cc4587SSamuel Ortiz 18030cc4587SSamuel Ortiz void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head) 18130cc4587SSamuel Ortiz { 18230cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 18330cc4587SSamuel Ortiz struct hlist_node *n; 18430cc4587SSamuel Ortiz 18530cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, head, node) { 18630cc4587SSamuel Ortiz hlist_del(&sdp->node); 18730cc4587SSamuel Ortiz 18830cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 18930cc4587SSamuel Ortiz } 19030cc4587SSamuel Ortiz } 19130cc4587SSamuel Ortiz 19230cc4587SSamuel Ortiz int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local, 193*3df40eb3SKrzysztof Kozlowski const u8 *tlv_array, u16 tlv_array_len) 19430cc4587SSamuel Ortiz { 195*3df40eb3SKrzysztof Kozlowski const u8 *tlv = tlv_array; 196*3df40eb3SKrzysztof Kozlowski u8 type, length, offset = 0; 19730cc4587SSamuel Ortiz 19830cc4587SSamuel Ortiz pr_debug("TLV array length %d\n", tlv_array_len); 19930cc4587SSamuel Ortiz 20030cc4587SSamuel Ortiz if (local == NULL) 20130cc4587SSamuel Ortiz return -ENODEV; 20230cc4587SSamuel Ortiz 20330cc4587SSamuel Ortiz while (offset < tlv_array_len) { 20430cc4587SSamuel Ortiz type = tlv[0]; 20530cc4587SSamuel Ortiz length = tlv[1]; 20630cc4587SSamuel Ortiz 20730cc4587SSamuel Ortiz pr_debug("type 0x%x length %d\n", type, length); 20830cc4587SSamuel Ortiz 20930cc4587SSamuel Ortiz switch (type) { 21030cc4587SSamuel Ortiz case LLCP_TLV_VERSION: 21130cc4587SSamuel Ortiz local->remote_version = llcp_tlv_version(tlv); 21230cc4587SSamuel Ortiz break; 21330cc4587SSamuel Ortiz case LLCP_TLV_MIUX: 21430cc4587SSamuel Ortiz local->remote_miu = llcp_tlv_miux(tlv) + 128; 21530cc4587SSamuel Ortiz break; 21630cc4587SSamuel Ortiz case LLCP_TLV_WKS: 21730cc4587SSamuel Ortiz local->remote_wks = llcp_tlv_wks(tlv); 21830cc4587SSamuel Ortiz break; 21930cc4587SSamuel Ortiz case LLCP_TLV_LTO: 22030cc4587SSamuel Ortiz local->remote_lto = llcp_tlv_lto(tlv) * 10; 22130cc4587SSamuel Ortiz break; 22230cc4587SSamuel Ortiz case LLCP_TLV_OPT: 22330cc4587SSamuel Ortiz local->remote_opt = llcp_tlv_opt(tlv); 22430cc4587SSamuel Ortiz break; 22530cc4587SSamuel Ortiz default: 22630cc4587SSamuel Ortiz pr_err("Invalid gt tlv value 0x%x\n", type); 22730cc4587SSamuel Ortiz break; 22830cc4587SSamuel Ortiz } 22930cc4587SSamuel Ortiz 23030cc4587SSamuel Ortiz offset += length + 2; 23130cc4587SSamuel Ortiz tlv += length + 2; 23230cc4587SSamuel Ortiz } 23330cc4587SSamuel Ortiz 23430cc4587SSamuel Ortiz pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x\n", 23530cc4587SSamuel Ortiz local->remote_version, local->remote_miu, 23630cc4587SSamuel Ortiz local->remote_lto, local->remote_opt, 23730cc4587SSamuel Ortiz local->remote_wks); 23830cc4587SSamuel Ortiz 23930cc4587SSamuel Ortiz return 0; 24030cc4587SSamuel Ortiz } 24130cc4587SSamuel Ortiz 24230cc4587SSamuel Ortiz int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock, 243*3df40eb3SKrzysztof Kozlowski const u8 *tlv_array, u16 tlv_array_len) 24430cc4587SSamuel Ortiz { 245*3df40eb3SKrzysztof Kozlowski const u8 *tlv = tlv_array; 246*3df40eb3SKrzysztof Kozlowski u8 type, length, offset = 0; 24730cc4587SSamuel Ortiz 24830cc4587SSamuel Ortiz pr_debug("TLV array length %d\n", tlv_array_len); 24930cc4587SSamuel Ortiz 25030cc4587SSamuel Ortiz if (sock == NULL) 25130cc4587SSamuel Ortiz return -ENOTCONN; 25230cc4587SSamuel Ortiz 25330cc4587SSamuel Ortiz while (offset < tlv_array_len) { 25430cc4587SSamuel Ortiz type = tlv[0]; 25530cc4587SSamuel Ortiz length = tlv[1]; 25630cc4587SSamuel Ortiz 25730cc4587SSamuel Ortiz pr_debug("type 0x%x length %d\n", type, length); 25830cc4587SSamuel Ortiz 25930cc4587SSamuel Ortiz switch (type) { 26030cc4587SSamuel Ortiz case LLCP_TLV_MIUX: 26130cc4587SSamuel Ortiz sock->remote_miu = llcp_tlv_miux(tlv) + 128; 26230cc4587SSamuel Ortiz break; 26330cc4587SSamuel Ortiz case LLCP_TLV_RW: 26430cc4587SSamuel Ortiz sock->remote_rw = llcp_tlv_rw(tlv); 26530cc4587SSamuel Ortiz break; 26630cc4587SSamuel Ortiz case LLCP_TLV_SN: 26730cc4587SSamuel Ortiz break; 26830cc4587SSamuel Ortiz default: 26930cc4587SSamuel Ortiz pr_err("Invalid gt tlv value 0x%x\n", type); 27030cc4587SSamuel Ortiz break; 27130cc4587SSamuel Ortiz } 27230cc4587SSamuel Ortiz 27330cc4587SSamuel Ortiz offset += length + 2; 27430cc4587SSamuel Ortiz tlv += length + 2; 27530cc4587SSamuel Ortiz } 27630cc4587SSamuel Ortiz 27730cc4587SSamuel Ortiz pr_debug("sock %p rw %d miu %d\n", sock, 27830cc4587SSamuel Ortiz sock->remote_rw, sock->remote_miu); 27930cc4587SSamuel Ortiz 28030cc4587SSamuel Ortiz return 0; 28130cc4587SSamuel Ortiz } 28230cc4587SSamuel Ortiz 28330cc4587SSamuel Ortiz static struct sk_buff *llcp_add_header(struct sk_buff *pdu, 28430cc4587SSamuel Ortiz u8 dsap, u8 ssap, u8 ptype) 28530cc4587SSamuel Ortiz { 28630cc4587SSamuel Ortiz u8 header[2]; 28730cc4587SSamuel Ortiz 28830cc4587SSamuel Ortiz pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap); 28930cc4587SSamuel Ortiz 29030cc4587SSamuel Ortiz header[0] = (u8)((dsap << 2) | (ptype >> 2)); 29130cc4587SSamuel Ortiz header[1] = (u8)((ptype << 6) | ssap); 29230cc4587SSamuel Ortiz 29330cc4587SSamuel Ortiz pr_debug("header 0x%x 0x%x\n", header[0], header[1]); 29430cc4587SSamuel Ortiz 29559ae1d12SJohannes Berg skb_put_data(pdu, header, LLCP_HEADER_SIZE); 29630cc4587SSamuel Ortiz 29730cc4587SSamuel Ortiz return pdu; 29830cc4587SSamuel Ortiz } 29930cc4587SSamuel Ortiz 300*3df40eb3SKrzysztof Kozlowski static struct sk_buff *llcp_add_tlv(struct sk_buff *pdu, const u8 *tlv, 30130cc4587SSamuel Ortiz u8 tlv_length) 30230cc4587SSamuel Ortiz { 30330cc4587SSamuel Ortiz /* XXX Add an skb length check */ 30430cc4587SSamuel Ortiz 30530cc4587SSamuel Ortiz if (tlv == NULL) 30630cc4587SSamuel Ortiz return NULL; 30730cc4587SSamuel Ortiz 30859ae1d12SJohannes Berg skb_put_data(pdu, tlv, tlv_length); 30930cc4587SSamuel Ortiz 31030cc4587SSamuel Ortiz return pdu; 31130cc4587SSamuel Ortiz } 31230cc4587SSamuel Ortiz 31330cc4587SSamuel Ortiz static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock, 31430cc4587SSamuel Ortiz u8 cmd, u16 size) 31530cc4587SSamuel Ortiz { 31630cc4587SSamuel Ortiz struct sk_buff *skb; 31730cc4587SSamuel Ortiz int err; 31830cc4587SSamuel Ortiz 31930cc4587SSamuel Ortiz if (sock->ssap == 0) 32030cc4587SSamuel Ortiz return NULL; 32130cc4587SSamuel Ortiz 32230cc4587SSamuel Ortiz skb = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT, 32330cc4587SSamuel Ortiz size + LLCP_HEADER_SIZE, &err); 32430cc4587SSamuel Ortiz if (skb == NULL) { 32530cc4587SSamuel Ortiz pr_err("Could not allocate PDU\n"); 32630cc4587SSamuel Ortiz return NULL; 32730cc4587SSamuel Ortiz } 32830cc4587SSamuel Ortiz 32930cc4587SSamuel Ortiz skb = llcp_add_header(skb, sock->dsap, sock->ssap, cmd); 33030cc4587SSamuel Ortiz 33130cc4587SSamuel Ortiz return skb; 33230cc4587SSamuel Ortiz } 33330cc4587SSamuel Ortiz 33458e3dd15SThierry Escande int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock) 33530cc4587SSamuel Ortiz { 33630cc4587SSamuel Ortiz struct sk_buff *skb; 33730cc4587SSamuel Ortiz struct nfc_dev *dev; 33830cc4587SSamuel Ortiz struct nfc_llcp_local *local; 33930cc4587SSamuel Ortiz 34030cc4587SSamuel Ortiz pr_debug("Sending DISC\n"); 34130cc4587SSamuel Ortiz 34230cc4587SSamuel Ortiz local = sock->local; 34330cc4587SSamuel Ortiz if (local == NULL) 34430cc4587SSamuel Ortiz return -ENODEV; 34530cc4587SSamuel Ortiz 34630cc4587SSamuel Ortiz dev = sock->dev; 34730cc4587SSamuel Ortiz if (dev == NULL) 34830cc4587SSamuel Ortiz return -ENODEV; 34930cc4587SSamuel Ortiz 35030cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0); 35130cc4587SSamuel Ortiz if (skb == NULL) 35230cc4587SSamuel Ortiz return -ENOMEM; 35330cc4587SSamuel Ortiz 35430cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 35530cc4587SSamuel Ortiz 35630cc4587SSamuel Ortiz return 0; 35730cc4587SSamuel Ortiz } 35830cc4587SSamuel Ortiz 35930cc4587SSamuel Ortiz int nfc_llcp_send_symm(struct nfc_dev *dev) 36030cc4587SSamuel Ortiz { 36130cc4587SSamuel Ortiz struct sk_buff *skb; 36230cc4587SSamuel Ortiz struct nfc_llcp_local *local; 36330cc4587SSamuel Ortiz u16 size = 0; 36430cc4587SSamuel Ortiz 36530cc4587SSamuel Ortiz pr_debug("Sending SYMM\n"); 36630cc4587SSamuel Ortiz 36730cc4587SSamuel Ortiz local = nfc_llcp_find_local(dev); 36830cc4587SSamuel Ortiz if (local == NULL) 36930cc4587SSamuel Ortiz return -ENODEV; 37030cc4587SSamuel Ortiz 37130cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 37230cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 37330cc4587SSamuel Ortiz 37430cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 37530cc4587SSamuel Ortiz if (skb == NULL) 37630cc4587SSamuel Ortiz return -ENOMEM; 37730cc4587SSamuel Ortiz 37830cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 37930cc4587SSamuel Ortiz 38030cc4587SSamuel Ortiz skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); 38130cc4587SSamuel Ortiz 38230cc4587SSamuel Ortiz __net_timestamp(skb); 38330cc4587SSamuel Ortiz 38457be1f3fSHiren Tandel nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); 38530cc4587SSamuel Ortiz 38630cc4587SSamuel Ortiz return nfc_data_exchange(dev, local->target_idx, skb, 38730cc4587SSamuel Ortiz nfc_llcp_recv, local); 38830cc4587SSamuel Ortiz } 38930cc4587SSamuel Ortiz 39030cc4587SSamuel Ortiz int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) 39130cc4587SSamuel Ortiz { 39230cc4587SSamuel Ortiz struct nfc_llcp_local *local; 39330cc4587SSamuel Ortiz struct sk_buff *skb; 394*3df40eb3SKrzysztof Kozlowski const u8 *service_name_tlv = NULL; 395*3df40eb3SKrzysztof Kozlowski const u8 *miux_tlv = NULL; 396*3df40eb3SKrzysztof Kozlowski const u8 *rw_tlv = NULL; 397*3df40eb3SKrzysztof Kozlowski u8 service_name_tlv_length, miux_tlv_length, rw_tlv_length, rw; 39830cc4587SSamuel Ortiz int err; 399e5b53c0aSChristophe Ricard u16 size = 0; 400e5b53c0aSChristophe Ricard __be16 miux; 40130cc4587SSamuel Ortiz 40230cc4587SSamuel Ortiz pr_debug("Sending CONNECT\n"); 40330cc4587SSamuel Ortiz 40430cc4587SSamuel Ortiz local = sock->local; 40530cc4587SSamuel Ortiz if (local == NULL) 40630cc4587SSamuel Ortiz return -ENODEV; 40730cc4587SSamuel Ortiz 40830cc4587SSamuel Ortiz if (sock->service_name != NULL) { 40930cc4587SSamuel Ortiz service_name_tlv = nfc_llcp_build_tlv(LLCP_TLV_SN, 41030cc4587SSamuel Ortiz sock->service_name, 41130cc4587SSamuel Ortiz sock->service_name_len, 41230cc4587SSamuel Ortiz &service_name_tlv_length); 41358bdd544SYueHaibing if (!service_name_tlv) { 41458bdd544SYueHaibing err = -ENOMEM; 41558bdd544SYueHaibing goto error_tlv; 41658bdd544SYueHaibing } 41730cc4587SSamuel Ortiz size += service_name_tlv_length; 41830cc4587SSamuel Ortiz } 41930cc4587SSamuel Ortiz 42030cc4587SSamuel Ortiz /* If the socket parameters are not set, use the local ones */ 42130cc4587SSamuel Ortiz miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ? 42230cc4587SSamuel Ortiz local->miux : sock->miux; 42330cc4587SSamuel Ortiz rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw; 42430cc4587SSamuel Ortiz 42530cc4587SSamuel Ortiz miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, 42630cc4587SSamuel Ortiz &miux_tlv_length); 42758bdd544SYueHaibing if (!miux_tlv) { 42858bdd544SYueHaibing err = -ENOMEM; 42958bdd544SYueHaibing goto error_tlv; 43058bdd544SYueHaibing } 43130cc4587SSamuel Ortiz size += miux_tlv_length; 43230cc4587SSamuel Ortiz 43330cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 43458bdd544SYueHaibing if (!rw_tlv) { 43558bdd544SYueHaibing err = -ENOMEM; 43658bdd544SYueHaibing goto error_tlv; 43758bdd544SYueHaibing } 43830cc4587SSamuel Ortiz size += rw_tlv_length; 43930cc4587SSamuel Ortiz 44030cc4587SSamuel Ortiz pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len); 44130cc4587SSamuel Ortiz 44230cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CONNECT, size); 44330cc4587SSamuel Ortiz if (skb == NULL) { 44430cc4587SSamuel Ortiz err = -ENOMEM; 44530cc4587SSamuel Ortiz goto error_tlv; 44630cc4587SSamuel Ortiz } 44730cc4587SSamuel Ortiz 448de9e5aebSThierry Escande llcp_add_tlv(skb, service_name_tlv, service_name_tlv_length); 449de9e5aebSThierry Escande llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 450de9e5aebSThierry Escande llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 45130cc4587SSamuel Ortiz 45230cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 45330cc4587SSamuel Ortiz 454256f3ee3SThierry Escande err = 0; 45530cc4587SSamuel Ortiz 45630cc4587SSamuel Ortiz error_tlv: 457256f3ee3SThierry Escande if (err) 45830cc4587SSamuel Ortiz pr_err("error %d\n", err); 45930cc4587SSamuel Ortiz 46030cc4587SSamuel Ortiz kfree(service_name_tlv); 46130cc4587SSamuel Ortiz kfree(miux_tlv); 46230cc4587SSamuel Ortiz kfree(rw_tlv); 46330cc4587SSamuel Ortiz 46430cc4587SSamuel Ortiz return err; 46530cc4587SSamuel Ortiz } 46630cc4587SSamuel Ortiz 46730cc4587SSamuel Ortiz int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) 46830cc4587SSamuel Ortiz { 46930cc4587SSamuel Ortiz struct nfc_llcp_local *local; 47030cc4587SSamuel Ortiz struct sk_buff *skb; 471*3df40eb3SKrzysztof Kozlowski const u8 *miux_tlv = NULL; 472*3df40eb3SKrzysztof Kozlowski const u8 *rw_tlv = NULL; 473*3df40eb3SKrzysztof Kozlowski u8 miux_tlv_length, rw_tlv_length, rw; 47430cc4587SSamuel Ortiz int err; 475e5b53c0aSChristophe Ricard u16 size = 0; 476e5b53c0aSChristophe Ricard __be16 miux; 47730cc4587SSamuel Ortiz 47830cc4587SSamuel Ortiz pr_debug("Sending CC\n"); 47930cc4587SSamuel Ortiz 48030cc4587SSamuel Ortiz local = sock->local; 48130cc4587SSamuel Ortiz if (local == NULL) 48230cc4587SSamuel Ortiz return -ENODEV; 48330cc4587SSamuel Ortiz 48430cc4587SSamuel Ortiz /* If the socket parameters are not set, use the local ones */ 48530cc4587SSamuel Ortiz miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ? 48630cc4587SSamuel Ortiz local->miux : sock->miux; 48730cc4587SSamuel Ortiz rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw; 48830cc4587SSamuel Ortiz 48930cc4587SSamuel Ortiz miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, 49030cc4587SSamuel Ortiz &miux_tlv_length); 49158bdd544SYueHaibing if (!miux_tlv) { 49258bdd544SYueHaibing err = -ENOMEM; 49358bdd544SYueHaibing goto error_tlv; 49458bdd544SYueHaibing } 49530cc4587SSamuel Ortiz size += miux_tlv_length; 49630cc4587SSamuel Ortiz 49730cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 49858bdd544SYueHaibing if (!rw_tlv) { 49958bdd544SYueHaibing err = -ENOMEM; 50058bdd544SYueHaibing goto error_tlv; 50158bdd544SYueHaibing } 50230cc4587SSamuel Ortiz size += rw_tlv_length; 50330cc4587SSamuel Ortiz 50430cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); 50530cc4587SSamuel Ortiz if (skb == NULL) { 50630cc4587SSamuel Ortiz err = -ENOMEM; 50730cc4587SSamuel Ortiz goto error_tlv; 50830cc4587SSamuel Ortiz } 50930cc4587SSamuel Ortiz 510de9e5aebSThierry Escande llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 511de9e5aebSThierry Escande llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 51230cc4587SSamuel Ortiz 51330cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 51430cc4587SSamuel Ortiz 515256f3ee3SThierry Escande err = 0; 51630cc4587SSamuel Ortiz 51730cc4587SSamuel Ortiz error_tlv: 518256f3ee3SThierry Escande if (err) 51930cc4587SSamuel Ortiz pr_err("error %d\n", err); 52030cc4587SSamuel Ortiz 52130cc4587SSamuel Ortiz kfree(miux_tlv); 52230cc4587SSamuel Ortiz kfree(rw_tlv); 52330cc4587SSamuel Ortiz 52430cc4587SSamuel Ortiz return err; 52530cc4587SSamuel Ortiz } 52630cc4587SSamuel Ortiz 52730cc4587SSamuel Ortiz static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local, 52830cc4587SSamuel Ortiz size_t tlv_length) 52930cc4587SSamuel Ortiz { 53030cc4587SSamuel Ortiz struct sk_buff *skb; 53130cc4587SSamuel Ortiz struct nfc_dev *dev; 53230cc4587SSamuel Ortiz u16 size = 0; 53330cc4587SSamuel Ortiz 53430cc4587SSamuel Ortiz if (local == NULL) 53530cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 53630cc4587SSamuel Ortiz 53730cc4587SSamuel Ortiz dev = local->dev; 53830cc4587SSamuel Ortiz if (dev == NULL) 53930cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 54030cc4587SSamuel Ortiz 54130cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 54230cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 54330cc4587SSamuel Ortiz size += tlv_length; 54430cc4587SSamuel Ortiz 54530cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 54630cc4587SSamuel Ortiz if (skb == NULL) 54730cc4587SSamuel Ortiz return ERR_PTR(-ENOMEM); 54830cc4587SSamuel Ortiz 54930cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 55030cc4587SSamuel Ortiz 55130cc4587SSamuel Ortiz skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL); 55230cc4587SSamuel Ortiz 55330cc4587SSamuel Ortiz return skb; 55430cc4587SSamuel Ortiz } 55530cc4587SSamuel Ortiz 55630cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local, 55730cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 55830cc4587SSamuel Ortiz { 55930cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 56030cc4587SSamuel Ortiz struct hlist_node *n; 56130cc4587SSamuel Ortiz struct sk_buff *skb; 56230cc4587SSamuel Ortiz 56330cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 56430cc4587SSamuel Ortiz if (IS_ERR(skb)) 56530cc4587SSamuel Ortiz return PTR_ERR(skb); 56630cc4587SSamuel Ortiz 56730cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, tlv_list, node) { 56859ae1d12SJohannes Berg skb_put_data(skb, sdp->tlv, sdp->tlv_len); 56930cc4587SSamuel Ortiz 57030cc4587SSamuel Ortiz hlist_del(&sdp->node); 57130cc4587SSamuel Ortiz 57230cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 57330cc4587SSamuel Ortiz } 57430cc4587SSamuel Ortiz 57530cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 57630cc4587SSamuel Ortiz 57730cc4587SSamuel Ortiz return 0; 57830cc4587SSamuel Ortiz } 57930cc4587SSamuel Ortiz 58030cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local, 58130cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 58230cc4587SSamuel Ortiz { 58330cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 58430cc4587SSamuel Ortiz struct hlist_node *n; 58530cc4587SSamuel Ortiz struct sk_buff *skb; 58630cc4587SSamuel Ortiz 58730cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 58830cc4587SSamuel Ortiz if (IS_ERR(skb)) 58930cc4587SSamuel Ortiz return PTR_ERR(skb); 59030cc4587SSamuel Ortiz 59130cc4587SSamuel Ortiz mutex_lock(&local->sdreq_lock); 59230cc4587SSamuel Ortiz 59330cc4587SSamuel Ortiz if (hlist_empty(&local->pending_sdreqs)) 59430cc4587SSamuel Ortiz mod_timer(&local->sdreq_timer, 59530cc4587SSamuel Ortiz jiffies + msecs_to_jiffies(3 * local->remote_lto)); 59630cc4587SSamuel Ortiz 59730cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdreq, n, tlv_list, node) { 59830cc4587SSamuel Ortiz pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri); 59930cc4587SSamuel Ortiz 60059ae1d12SJohannes Berg skb_put_data(skb, sdreq->tlv, sdreq->tlv_len); 60130cc4587SSamuel Ortiz 60230cc4587SSamuel Ortiz hlist_del(&sdreq->node); 60330cc4587SSamuel Ortiz 60430cc4587SSamuel Ortiz hlist_add_head(&sdreq->node, &local->pending_sdreqs); 60530cc4587SSamuel Ortiz } 60630cc4587SSamuel Ortiz 60730cc4587SSamuel Ortiz mutex_unlock(&local->sdreq_lock); 60830cc4587SSamuel Ortiz 60930cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 61030cc4587SSamuel Ortiz 61130cc4587SSamuel Ortiz return 0; 61230cc4587SSamuel Ortiz } 61330cc4587SSamuel Ortiz 61430cc4587SSamuel Ortiz int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason) 61530cc4587SSamuel Ortiz { 61630cc4587SSamuel Ortiz struct sk_buff *skb; 61730cc4587SSamuel Ortiz struct nfc_dev *dev; 61830cc4587SSamuel Ortiz u16 size = 1; /* Reason code */ 61930cc4587SSamuel Ortiz 62030cc4587SSamuel Ortiz pr_debug("Sending DM reason 0x%x\n", reason); 62130cc4587SSamuel Ortiz 62230cc4587SSamuel Ortiz if (local == NULL) 62330cc4587SSamuel Ortiz return -ENODEV; 62430cc4587SSamuel Ortiz 62530cc4587SSamuel Ortiz dev = local->dev; 62630cc4587SSamuel Ortiz if (dev == NULL) 62730cc4587SSamuel Ortiz return -ENODEV; 62830cc4587SSamuel Ortiz 62930cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 63030cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 63130cc4587SSamuel Ortiz 63230cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 63330cc4587SSamuel Ortiz if (skb == NULL) 63430cc4587SSamuel Ortiz return -ENOMEM; 63530cc4587SSamuel Ortiz 63630cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 63730cc4587SSamuel Ortiz 63830cc4587SSamuel Ortiz skb = llcp_add_header(skb, dsap, ssap, LLCP_PDU_DM); 63930cc4587SSamuel Ortiz 64059ae1d12SJohannes Berg skb_put_data(skb, &reason, 1); 64130cc4587SSamuel Ortiz 64230cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 64330cc4587SSamuel Ortiz 64430cc4587SSamuel Ortiz return 0; 64530cc4587SSamuel Ortiz } 64630cc4587SSamuel Ortiz 64730cc4587SSamuel Ortiz int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, 64830cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 64930cc4587SSamuel Ortiz { 65030cc4587SSamuel Ortiz struct sk_buff *pdu; 65130cc4587SSamuel Ortiz struct sock *sk = &sock->sk; 65230cc4587SSamuel Ortiz struct nfc_llcp_local *local; 65330cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 65430cc4587SSamuel Ortiz u8 *msg_data, *msg_ptr; 65530cc4587SSamuel Ortiz u16 remote_miu; 65630cc4587SSamuel Ortiz 65730cc4587SSamuel Ortiz pr_debug("Send I frame len %zd\n", len); 65830cc4587SSamuel Ortiz 65930cc4587SSamuel Ortiz local = sock->local; 66030cc4587SSamuel Ortiz if (local == NULL) 66130cc4587SSamuel Ortiz return -ENODEV; 66230cc4587SSamuel Ortiz 66330cc4587SSamuel Ortiz /* Remote is ready but has not acknowledged our frames */ 66430cc4587SSamuel Ortiz if((sock->remote_ready && 66530cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw && 66630cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 66730cc4587SSamuel Ortiz pr_err("Pending queue is full %d frames\n", 66830cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue)); 66930cc4587SSamuel Ortiz return -ENOBUFS; 67030cc4587SSamuel Ortiz } 67130cc4587SSamuel Ortiz 67230cc4587SSamuel Ortiz /* Remote is not ready and we've been queueing enough frames */ 67330cc4587SSamuel Ortiz if ((!sock->remote_ready && 67430cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 67530cc4587SSamuel Ortiz pr_err("Tx queue is full %d frames\n", 67630cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue)); 67730cc4587SSamuel Ortiz return -ENOBUFS; 67830cc4587SSamuel Ortiz } 67930cc4587SSamuel Ortiz 68081ca7835SCong Wang msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN); 68130cc4587SSamuel Ortiz if (msg_data == NULL) 68230cc4587SSamuel Ortiz return -ENOMEM; 68330cc4587SSamuel Ortiz 6846ce8e9ceSAl Viro if (memcpy_from_msg(msg_data, msg, len)) { 68530cc4587SSamuel Ortiz kfree(msg_data); 68630cc4587SSamuel Ortiz return -EFAULT; 68730cc4587SSamuel Ortiz } 68830cc4587SSamuel Ortiz 68930cc4587SSamuel Ortiz remaining_len = len; 69030cc4587SSamuel Ortiz msg_ptr = msg_data; 69130cc4587SSamuel Ortiz 69230cc4587SSamuel Ortiz do { 69330cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 69411bfb1c4SSzymon Janc LLCP_DEFAULT_MIU : sock->remote_miu; 69530cc4587SSamuel Ortiz 69630cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 69730cc4587SSamuel Ortiz 69830cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 69930cc4587SSamuel Ortiz frag_len, remaining_len); 70030cc4587SSamuel Ortiz 70130cc4587SSamuel Ortiz pdu = llcp_allocate_pdu(sock, LLCP_PDU_I, 70230cc4587SSamuel Ortiz frag_len + LLCP_SEQUENCE_SIZE); 70343d53c29SSzymon Janc if (pdu == NULL) { 70443d53c29SSzymon Janc kfree(msg_data); 70530cc4587SSamuel Ortiz return -ENOMEM; 70643d53c29SSzymon Janc } 70730cc4587SSamuel Ortiz 70830cc4587SSamuel Ortiz skb_put(pdu, LLCP_SEQUENCE_SIZE); 70930cc4587SSamuel Ortiz 71030cc4587SSamuel Ortiz if (likely(frag_len > 0)) 71159ae1d12SJohannes Berg skb_put_data(pdu, msg_ptr, frag_len); 71230cc4587SSamuel Ortiz 71330cc4587SSamuel Ortiz skb_queue_tail(&sock->tx_queue, pdu); 71430cc4587SSamuel Ortiz 71530cc4587SSamuel Ortiz lock_sock(sk); 71630cc4587SSamuel Ortiz 71730cc4587SSamuel Ortiz nfc_llcp_queue_i_frames(sock); 71830cc4587SSamuel Ortiz 71930cc4587SSamuel Ortiz release_sock(sk); 72030cc4587SSamuel Ortiz 72130cc4587SSamuel Ortiz remaining_len -= frag_len; 72230cc4587SSamuel Ortiz msg_ptr += frag_len; 72330cc4587SSamuel Ortiz } while (remaining_len > 0); 72430cc4587SSamuel Ortiz 72530cc4587SSamuel Ortiz kfree(msg_data); 72630cc4587SSamuel Ortiz 72730cc4587SSamuel Ortiz return len; 72830cc4587SSamuel Ortiz } 72930cc4587SSamuel Ortiz 73030cc4587SSamuel Ortiz int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, 73130cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 73230cc4587SSamuel Ortiz { 73330cc4587SSamuel Ortiz struct sk_buff *pdu; 73430cc4587SSamuel Ortiz struct nfc_llcp_local *local; 73530cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 73630cc4587SSamuel Ortiz u8 *msg_ptr, *msg_data; 73730cc4587SSamuel Ortiz u16 remote_miu; 73830cc4587SSamuel Ortiz int err; 73930cc4587SSamuel Ortiz 74030cc4587SSamuel Ortiz pr_debug("Send UI frame len %zd\n", len); 74130cc4587SSamuel Ortiz 74230cc4587SSamuel Ortiz local = sock->local; 74330cc4587SSamuel Ortiz if (local == NULL) 74430cc4587SSamuel Ortiz return -ENODEV; 74530cc4587SSamuel Ortiz 74681ca7835SCong Wang msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN); 74730cc4587SSamuel Ortiz if (msg_data == NULL) 74830cc4587SSamuel Ortiz return -ENOMEM; 74930cc4587SSamuel Ortiz 7506ce8e9ceSAl Viro if (memcpy_from_msg(msg_data, msg, len)) { 75130cc4587SSamuel Ortiz kfree(msg_data); 75230cc4587SSamuel Ortiz return -EFAULT; 75330cc4587SSamuel Ortiz } 75430cc4587SSamuel Ortiz 75530cc4587SSamuel Ortiz remaining_len = len; 75630cc4587SSamuel Ortiz msg_ptr = msg_data; 75730cc4587SSamuel Ortiz 75830cc4587SSamuel Ortiz do { 75930cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 76030cc4587SSamuel Ortiz local->remote_miu : sock->remote_miu; 76130cc4587SSamuel Ortiz 76230cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 76330cc4587SSamuel Ortiz 76430cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 76530cc4587SSamuel Ortiz frag_len, remaining_len); 76630cc4587SSamuel Ortiz 7673bc53be9STetsuo Handa pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, 0, 76830cc4587SSamuel Ortiz frag_len + LLCP_HEADER_SIZE, &err); 76930cc4587SSamuel Ortiz if (pdu == NULL) { 7703bc53be9STetsuo Handa pr_err("Could not allocate PDU (error=%d)\n", err); 7713bc53be9STetsuo Handa len -= remaining_len; 7723bc53be9STetsuo Handa if (len == 0) 7733bc53be9STetsuo Handa len = err; 7743bc53be9STetsuo Handa break; 77530cc4587SSamuel Ortiz } 77630cc4587SSamuel Ortiz 77730cc4587SSamuel Ortiz pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI); 77830cc4587SSamuel Ortiz 77930cc4587SSamuel Ortiz if (likely(frag_len > 0)) 78059ae1d12SJohannes Berg skb_put_data(pdu, msg_ptr, frag_len); 78130cc4587SSamuel Ortiz 78230cc4587SSamuel Ortiz /* No need to check for the peer RW for UI frames */ 78330cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, pdu); 78430cc4587SSamuel Ortiz 78530cc4587SSamuel Ortiz remaining_len -= frag_len; 78630cc4587SSamuel Ortiz msg_ptr += frag_len; 78730cc4587SSamuel Ortiz } while (remaining_len > 0); 78830cc4587SSamuel Ortiz 78930cc4587SSamuel Ortiz kfree(msg_data); 79030cc4587SSamuel Ortiz 79130cc4587SSamuel Ortiz return len; 79230cc4587SSamuel Ortiz } 79330cc4587SSamuel Ortiz 79430cc4587SSamuel Ortiz int nfc_llcp_send_rr(struct nfc_llcp_sock *sock) 79530cc4587SSamuel Ortiz { 79630cc4587SSamuel Ortiz struct sk_buff *skb; 79730cc4587SSamuel Ortiz struct nfc_llcp_local *local; 79830cc4587SSamuel Ortiz 79930cc4587SSamuel Ortiz pr_debug("Send rr nr %d\n", sock->recv_n); 80030cc4587SSamuel Ortiz 80130cc4587SSamuel Ortiz local = sock->local; 80230cc4587SSamuel Ortiz if (local == NULL) 80330cc4587SSamuel Ortiz return -ENODEV; 80430cc4587SSamuel Ortiz 80530cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_RR, LLCP_SEQUENCE_SIZE); 80630cc4587SSamuel Ortiz if (skb == NULL) 80730cc4587SSamuel Ortiz return -ENOMEM; 80830cc4587SSamuel Ortiz 80930cc4587SSamuel Ortiz skb_put(skb, LLCP_SEQUENCE_SIZE); 81030cc4587SSamuel Ortiz 81130cc4587SSamuel Ortiz skb->data[2] = sock->recv_n; 81230cc4587SSamuel Ortiz 81330cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 81430cc4587SSamuel Ortiz 81530cc4587SSamuel Ortiz return 0; 81630cc4587SSamuel Ortiz } 817