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 183df40eb3SKrzysztof 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 323df40eb3SKrzysztof 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 403df40eb3SKrzysztof 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 493df40eb3SKrzysztof Kozlowski static u8 llcp_tlv_version(const u8 *tlv) 5030cc4587SSamuel Ortiz { 5130cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_VERSION); 5230cc4587SSamuel Ortiz } 5330cc4587SSamuel Ortiz 543df40eb3SKrzysztof 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 593df40eb3SKrzysztof Kozlowski static u16 llcp_tlv_wks(const u8 *tlv) 6030cc4587SSamuel Ortiz { 6130cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_WKS); 6230cc4587SSamuel Ortiz } 6330cc4587SSamuel Ortiz 643df40eb3SKrzysztof Kozlowski static u16 llcp_tlv_lto(const u8 *tlv) 6530cc4587SSamuel Ortiz { 6630cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_LTO); 6730cc4587SSamuel Ortiz } 6830cc4587SSamuel Ortiz 693df40eb3SKrzysztof Kozlowski static u8 llcp_tlv_opt(const u8 *tlv) 7030cc4587SSamuel Ortiz { 7130cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_OPT); 7230cc4587SSamuel Ortiz } 7330cc4587SSamuel Ortiz 743df40eb3SKrzysztof 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 793df40eb3SKrzysztof 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 1333df40eb3SKrzysztof 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, 1933df40eb3SKrzysztof Kozlowski const u8 *tlv_array, u16 tlv_array_len) 19430cc4587SSamuel Ortiz { 1953df40eb3SKrzysztof Kozlowski const u8 *tlv = tlv_array; 1963df40eb3SKrzysztof 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, 2433df40eb3SKrzysztof Kozlowski const u8 *tlv_array, u16 tlv_array_len) 24430cc4587SSamuel Ortiz { 2453df40eb3SKrzysztof Kozlowski const u8 *tlv = tlv_array; 2463df40eb3SKrzysztof 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 3003df40eb3SKrzysztof 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 local = sock->local; 34130cc4587SSamuel Ortiz if (local == NULL) 34230cc4587SSamuel Ortiz return -ENODEV; 34330cc4587SSamuel Ortiz 34430cc4587SSamuel Ortiz dev = sock->dev; 34530cc4587SSamuel Ortiz if (dev == NULL) 34630cc4587SSamuel Ortiz return -ENODEV; 34730cc4587SSamuel Ortiz 34830cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0); 34930cc4587SSamuel Ortiz if (skb == NULL) 35030cc4587SSamuel Ortiz return -ENOMEM; 35130cc4587SSamuel Ortiz 35230cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 35330cc4587SSamuel Ortiz 35430cc4587SSamuel Ortiz return 0; 35530cc4587SSamuel Ortiz } 35630cc4587SSamuel Ortiz 35730cc4587SSamuel Ortiz int nfc_llcp_send_symm(struct nfc_dev *dev) 35830cc4587SSamuel Ortiz { 35930cc4587SSamuel Ortiz struct sk_buff *skb; 36030cc4587SSamuel Ortiz struct nfc_llcp_local *local; 36130cc4587SSamuel Ortiz u16 size = 0; 362*6709d4b7SLin Ma int err; 36330cc4587SSamuel Ortiz 36430cc4587SSamuel Ortiz local = nfc_llcp_find_local(dev); 36530cc4587SSamuel Ortiz if (local == NULL) 36630cc4587SSamuel Ortiz return -ENODEV; 36730cc4587SSamuel Ortiz 36830cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 36930cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 37030cc4587SSamuel Ortiz 37130cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 372*6709d4b7SLin Ma if (skb == NULL) { 373*6709d4b7SLin Ma err = -ENOMEM; 374*6709d4b7SLin Ma goto out; 375*6709d4b7SLin Ma } 37630cc4587SSamuel Ortiz 37730cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 37830cc4587SSamuel Ortiz 37930cc4587SSamuel Ortiz skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); 38030cc4587SSamuel Ortiz 38130cc4587SSamuel Ortiz __net_timestamp(skb); 38230cc4587SSamuel Ortiz 38357be1f3fSHiren Tandel nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); 38430cc4587SSamuel Ortiz 385*6709d4b7SLin Ma err = nfc_data_exchange(dev, local->target_idx, skb, 38630cc4587SSamuel Ortiz nfc_llcp_recv, local); 387*6709d4b7SLin Ma out: 388*6709d4b7SLin Ma nfc_llcp_local_put(local); 389*6709d4b7SLin Ma return err; 39030cc4587SSamuel Ortiz } 39130cc4587SSamuel Ortiz 39230cc4587SSamuel Ortiz int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) 39330cc4587SSamuel Ortiz { 39430cc4587SSamuel Ortiz struct nfc_llcp_local *local; 39530cc4587SSamuel Ortiz struct sk_buff *skb; 3963df40eb3SKrzysztof Kozlowski const u8 *service_name_tlv = NULL; 3973df40eb3SKrzysztof Kozlowski const u8 *miux_tlv = NULL; 3983df40eb3SKrzysztof Kozlowski const u8 *rw_tlv = NULL; 3993df40eb3SKrzysztof Kozlowski u8 service_name_tlv_length, miux_tlv_length, rw_tlv_length, rw; 40030cc4587SSamuel Ortiz int err; 401e5b53c0aSChristophe Ricard u16 size = 0; 402e5b53c0aSChristophe Ricard __be16 miux; 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; 4713df40eb3SKrzysztof Kozlowski const u8 *miux_tlv = NULL; 4723df40eb3SKrzysztof Kozlowski const u8 *rw_tlv = NULL; 4733df40eb3SKrzysztof 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 local = sock->local; 47930cc4587SSamuel Ortiz if (local == NULL) 48030cc4587SSamuel Ortiz return -ENODEV; 48130cc4587SSamuel Ortiz 48230cc4587SSamuel Ortiz /* If the socket parameters are not set, use the local ones */ 48330cc4587SSamuel Ortiz miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ? 48430cc4587SSamuel Ortiz local->miux : sock->miux; 48530cc4587SSamuel Ortiz rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw; 48630cc4587SSamuel Ortiz 48730cc4587SSamuel Ortiz miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, 48830cc4587SSamuel Ortiz &miux_tlv_length); 48958bdd544SYueHaibing if (!miux_tlv) { 49058bdd544SYueHaibing err = -ENOMEM; 49158bdd544SYueHaibing goto error_tlv; 49258bdd544SYueHaibing } 49330cc4587SSamuel Ortiz size += miux_tlv_length; 49430cc4587SSamuel Ortiz 49530cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 49658bdd544SYueHaibing if (!rw_tlv) { 49758bdd544SYueHaibing err = -ENOMEM; 49858bdd544SYueHaibing goto error_tlv; 49958bdd544SYueHaibing } 50030cc4587SSamuel Ortiz size += rw_tlv_length; 50130cc4587SSamuel Ortiz 50230cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); 50330cc4587SSamuel Ortiz if (skb == NULL) { 50430cc4587SSamuel Ortiz err = -ENOMEM; 50530cc4587SSamuel Ortiz goto error_tlv; 50630cc4587SSamuel Ortiz } 50730cc4587SSamuel Ortiz 508de9e5aebSThierry Escande llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 509de9e5aebSThierry Escande llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 51030cc4587SSamuel Ortiz 51130cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 51230cc4587SSamuel Ortiz 513256f3ee3SThierry Escande err = 0; 51430cc4587SSamuel Ortiz 51530cc4587SSamuel Ortiz error_tlv: 516256f3ee3SThierry Escande if (err) 51730cc4587SSamuel Ortiz pr_err("error %d\n", err); 51830cc4587SSamuel Ortiz 51930cc4587SSamuel Ortiz kfree(miux_tlv); 52030cc4587SSamuel Ortiz kfree(rw_tlv); 52130cc4587SSamuel Ortiz 52230cc4587SSamuel Ortiz return err; 52330cc4587SSamuel Ortiz } 52430cc4587SSamuel Ortiz 52530cc4587SSamuel Ortiz static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local, 52630cc4587SSamuel Ortiz size_t tlv_length) 52730cc4587SSamuel Ortiz { 52830cc4587SSamuel Ortiz struct sk_buff *skb; 52930cc4587SSamuel Ortiz struct nfc_dev *dev; 53030cc4587SSamuel Ortiz u16 size = 0; 53130cc4587SSamuel Ortiz 53230cc4587SSamuel Ortiz if (local == NULL) 53330cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 53430cc4587SSamuel Ortiz 53530cc4587SSamuel Ortiz dev = local->dev; 53630cc4587SSamuel Ortiz if (dev == NULL) 53730cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 53830cc4587SSamuel Ortiz 53930cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 54030cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 54130cc4587SSamuel Ortiz size += tlv_length; 54230cc4587SSamuel Ortiz 54330cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 54430cc4587SSamuel Ortiz if (skb == NULL) 54530cc4587SSamuel Ortiz return ERR_PTR(-ENOMEM); 54630cc4587SSamuel Ortiz 54730cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 54830cc4587SSamuel Ortiz 54930cc4587SSamuel Ortiz skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL); 55030cc4587SSamuel Ortiz 55130cc4587SSamuel Ortiz return skb; 55230cc4587SSamuel Ortiz } 55330cc4587SSamuel Ortiz 55430cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local, 55530cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 55630cc4587SSamuel Ortiz { 55730cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 55830cc4587SSamuel Ortiz struct hlist_node *n; 55930cc4587SSamuel Ortiz struct sk_buff *skb; 56030cc4587SSamuel Ortiz 56130cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 56230cc4587SSamuel Ortiz if (IS_ERR(skb)) 56330cc4587SSamuel Ortiz return PTR_ERR(skb); 56430cc4587SSamuel Ortiz 56530cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, tlv_list, node) { 56659ae1d12SJohannes Berg skb_put_data(skb, sdp->tlv, sdp->tlv_len); 56730cc4587SSamuel Ortiz 56830cc4587SSamuel Ortiz hlist_del(&sdp->node); 56930cc4587SSamuel Ortiz 57030cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 57130cc4587SSamuel Ortiz } 57230cc4587SSamuel Ortiz 57330cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 57430cc4587SSamuel Ortiz 57530cc4587SSamuel Ortiz return 0; 57630cc4587SSamuel Ortiz } 57730cc4587SSamuel Ortiz 57830cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local, 57930cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 58030cc4587SSamuel Ortiz { 58130cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 58230cc4587SSamuel Ortiz struct hlist_node *n; 58330cc4587SSamuel Ortiz struct sk_buff *skb; 58430cc4587SSamuel Ortiz 58530cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 58630cc4587SSamuel Ortiz if (IS_ERR(skb)) 58730cc4587SSamuel Ortiz return PTR_ERR(skb); 58830cc4587SSamuel Ortiz 58930cc4587SSamuel Ortiz mutex_lock(&local->sdreq_lock); 59030cc4587SSamuel Ortiz 59130cc4587SSamuel Ortiz if (hlist_empty(&local->pending_sdreqs)) 59230cc4587SSamuel Ortiz mod_timer(&local->sdreq_timer, 59330cc4587SSamuel Ortiz jiffies + msecs_to_jiffies(3 * local->remote_lto)); 59430cc4587SSamuel Ortiz 59530cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdreq, n, tlv_list, node) { 59630cc4587SSamuel Ortiz pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri); 59730cc4587SSamuel Ortiz 59859ae1d12SJohannes Berg skb_put_data(skb, sdreq->tlv, sdreq->tlv_len); 59930cc4587SSamuel Ortiz 60030cc4587SSamuel Ortiz hlist_del(&sdreq->node); 60130cc4587SSamuel Ortiz 60230cc4587SSamuel Ortiz hlist_add_head(&sdreq->node, &local->pending_sdreqs); 60330cc4587SSamuel Ortiz } 60430cc4587SSamuel Ortiz 60530cc4587SSamuel Ortiz mutex_unlock(&local->sdreq_lock); 60630cc4587SSamuel Ortiz 60730cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 60830cc4587SSamuel Ortiz 60930cc4587SSamuel Ortiz return 0; 61030cc4587SSamuel Ortiz } 61130cc4587SSamuel Ortiz 61230cc4587SSamuel Ortiz int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason) 61330cc4587SSamuel Ortiz { 61430cc4587SSamuel Ortiz struct sk_buff *skb; 61530cc4587SSamuel Ortiz struct nfc_dev *dev; 61630cc4587SSamuel Ortiz u16 size = 1; /* Reason code */ 61730cc4587SSamuel Ortiz 61830cc4587SSamuel Ortiz pr_debug("Sending DM reason 0x%x\n", reason); 61930cc4587SSamuel Ortiz 62030cc4587SSamuel Ortiz if (local == NULL) 62130cc4587SSamuel Ortiz return -ENODEV; 62230cc4587SSamuel Ortiz 62330cc4587SSamuel Ortiz dev = local->dev; 62430cc4587SSamuel Ortiz if (dev == NULL) 62530cc4587SSamuel Ortiz return -ENODEV; 62630cc4587SSamuel Ortiz 62730cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 62830cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 62930cc4587SSamuel Ortiz 63030cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 63130cc4587SSamuel Ortiz if (skb == NULL) 63230cc4587SSamuel Ortiz return -ENOMEM; 63330cc4587SSamuel Ortiz 63430cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 63530cc4587SSamuel Ortiz 63630cc4587SSamuel Ortiz skb = llcp_add_header(skb, dsap, ssap, LLCP_PDU_DM); 63730cc4587SSamuel Ortiz 63859ae1d12SJohannes Berg skb_put_data(skb, &reason, 1); 63930cc4587SSamuel Ortiz 64030cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 64130cc4587SSamuel Ortiz 64230cc4587SSamuel Ortiz return 0; 64330cc4587SSamuel Ortiz } 64430cc4587SSamuel Ortiz 64530cc4587SSamuel Ortiz int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, 64630cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 64730cc4587SSamuel Ortiz { 64830cc4587SSamuel Ortiz struct sk_buff *pdu; 64930cc4587SSamuel Ortiz struct sock *sk = &sock->sk; 65030cc4587SSamuel Ortiz struct nfc_llcp_local *local; 65130cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 65230cc4587SSamuel Ortiz u8 *msg_data, *msg_ptr; 65330cc4587SSamuel Ortiz u16 remote_miu; 65430cc4587SSamuel Ortiz 65530cc4587SSamuel Ortiz pr_debug("Send I frame len %zd\n", len); 65630cc4587SSamuel Ortiz 65730cc4587SSamuel Ortiz local = sock->local; 65830cc4587SSamuel Ortiz if (local == NULL) 65930cc4587SSamuel Ortiz return -ENODEV; 66030cc4587SSamuel Ortiz 66130cc4587SSamuel Ortiz /* Remote is ready but has not acknowledged our frames */ 66230cc4587SSamuel Ortiz if((sock->remote_ready && 66330cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw && 66430cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 66530cc4587SSamuel Ortiz pr_err("Pending queue is full %d frames\n", 66630cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue)); 66730cc4587SSamuel Ortiz return -ENOBUFS; 66830cc4587SSamuel Ortiz } 66930cc4587SSamuel Ortiz 67030cc4587SSamuel Ortiz /* Remote is not ready and we've been queueing enough frames */ 67130cc4587SSamuel Ortiz if ((!sock->remote_ready && 67230cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 67330cc4587SSamuel Ortiz pr_err("Tx queue is full %d frames\n", 67430cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue)); 67530cc4587SSamuel Ortiz return -ENOBUFS; 67630cc4587SSamuel Ortiz } 67730cc4587SSamuel Ortiz 67881ca7835SCong Wang msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN); 67930cc4587SSamuel Ortiz if (msg_data == NULL) 68030cc4587SSamuel Ortiz return -ENOMEM; 68130cc4587SSamuel Ortiz 6826ce8e9ceSAl Viro if (memcpy_from_msg(msg_data, msg, len)) { 68330cc4587SSamuel Ortiz kfree(msg_data); 68430cc4587SSamuel Ortiz return -EFAULT; 68530cc4587SSamuel Ortiz } 68630cc4587SSamuel Ortiz 68730cc4587SSamuel Ortiz remaining_len = len; 68830cc4587SSamuel Ortiz msg_ptr = msg_data; 68930cc4587SSamuel Ortiz 69030cc4587SSamuel Ortiz do { 69130cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 69211bfb1c4SSzymon Janc LLCP_DEFAULT_MIU : sock->remote_miu; 69330cc4587SSamuel Ortiz 69430cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 69530cc4587SSamuel Ortiz 69630cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 69730cc4587SSamuel Ortiz frag_len, remaining_len); 69830cc4587SSamuel Ortiz 69930cc4587SSamuel Ortiz pdu = llcp_allocate_pdu(sock, LLCP_PDU_I, 70030cc4587SSamuel Ortiz frag_len + LLCP_SEQUENCE_SIZE); 70143d53c29SSzymon Janc if (pdu == NULL) { 70243d53c29SSzymon Janc kfree(msg_data); 70330cc4587SSamuel Ortiz return -ENOMEM; 70443d53c29SSzymon Janc } 70530cc4587SSamuel Ortiz 70630cc4587SSamuel Ortiz skb_put(pdu, LLCP_SEQUENCE_SIZE); 70730cc4587SSamuel Ortiz 70830cc4587SSamuel Ortiz if (likely(frag_len > 0)) 70959ae1d12SJohannes Berg skb_put_data(pdu, msg_ptr, frag_len); 71030cc4587SSamuel Ortiz 71130cc4587SSamuel Ortiz skb_queue_tail(&sock->tx_queue, pdu); 71230cc4587SSamuel Ortiz 71330cc4587SSamuel Ortiz lock_sock(sk); 71430cc4587SSamuel Ortiz 71530cc4587SSamuel Ortiz nfc_llcp_queue_i_frames(sock); 71630cc4587SSamuel Ortiz 71730cc4587SSamuel Ortiz release_sock(sk); 71830cc4587SSamuel Ortiz 71930cc4587SSamuel Ortiz remaining_len -= frag_len; 72030cc4587SSamuel Ortiz msg_ptr += frag_len; 72130cc4587SSamuel Ortiz } while (remaining_len > 0); 72230cc4587SSamuel Ortiz 72330cc4587SSamuel Ortiz kfree(msg_data); 72430cc4587SSamuel Ortiz 72530cc4587SSamuel Ortiz return len; 72630cc4587SSamuel Ortiz } 72730cc4587SSamuel Ortiz 72830cc4587SSamuel Ortiz int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, 72930cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 73030cc4587SSamuel Ortiz { 73130cc4587SSamuel Ortiz struct sk_buff *pdu; 73230cc4587SSamuel Ortiz struct nfc_llcp_local *local; 73330cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 73430cc4587SSamuel Ortiz u8 *msg_ptr, *msg_data; 73530cc4587SSamuel Ortiz u16 remote_miu; 73630cc4587SSamuel Ortiz int err; 73730cc4587SSamuel Ortiz 73830cc4587SSamuel Ortiz pr_debug("Send UI frame len %zd\n", len); 73930cc4587SSamuel Ortiz 74030cc4587SSamuel Ortiz local = sock->local; 74130cc4587SSamuel Ortiz if (local == NULL) 74230cc4587SSamuel Ortiz return -ENODEV; 74330cc4587SSamuel Ortiz 74481ca7835SCong Wang msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN); 74530cc4587SSamuel Ortiz if (msg_data == NULL) 74630cc4587SSamuel Ortiz return -ENOMEM; 74730cc4587SSamuel Ortiz 7486ce8e9ceSAl Viro if (memcpy_from_msg(msg_data, msg, len)) { 74930cc4587SSamuel Ortiz kfree(msg_data); 75030cc4587SSamuel Ortiz return -EFAULT; 75130cc4587SSamuel Ortiz } 75230cc4587SSamuel Ortiz 75330cc4587SSamuel Ortiz remaining_len = len; 75430cc4587SSamuel Ortiz msg_ptr = msg_data; 75530cc4587SSamuel Ortiz 75630cc4587SSamuel Ortiz do { 75730cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 75830cc4587SSamuel Ortiz local->remote_miu : sock->remote_miu; 75930cc4587SSamuel Ortiz 76030cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 76130cc4587SSamuel Ortiz 76230cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 76330cc4587SSamuel Ortiz frag_len, remaining_len); 76430cc4587SSamuel Ortiz 7653bc53be9STetsuo Handa pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, 0, 76630cc4587SSamuel Ortiz frag_len + LLCP_HEADER_SIZE, &err); 76730cc4587SSamuel Ortiz if (pdu == NULL) { 7683bc53be9STetsuo Handa pr_err("Could not allocate PDU (error=%d)\n", err); 7693bc53be9STetsuo Handa len -= remaining_len; 7703bc53be9STetsuo Handa if (len == 0) 7713bc53be9STetsuo Handa len = err; 7723bc53be9STetsuo Handa break; 77330cc4587SSamuel Ortiz } 77430cc4587SSamuel Ortiz 77530cc4587SSamuel Ortiz pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI); 77630cc4587SSamuel Ortiz 77730cc4587SSamuel Ortiz if (likely(frag_len > 0)) 77859ae1d12SJohannes Berg skb_put_data(pdu, msg_ptr, frag_len); 77930cc4587SSamuel Ortiz 78030cc4587SSamuel Ortiz /* No need to check for the peer RW for UI frames */ 78130cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, pdu); 78230cc4587SSamuel Ortiz 78330cc4587SSamuel Ortiz remaining_len -= frag_len; 78430cc4587SSamuel Ortiz msg_ptr += frag_len; 78530cc4587SSamuel Ortiz } while (remaining_len > 0); 78630cc4587SSamuel Ortiz 78730cc4587SSamuel Ortiz kfree(msg_data); 78830cc4587SSamuel Ortiz 78930cc4587SSamuel Ortiz return len; 79030cc4587SSamuel Ortiz } 79130cc4587SSamuel Ortiz 79230cc4587SSamuel Ortiz int nfc_llcp_send_rr(struct nfc_llcp_sock *sock) 79330cc4587SSamuel Ortiz { 79430cc4587SSamuel Ortiz struct sk_buff *skb; 79530cc4587SSamuel Ortiz struct nfc_llcp_local *local; 79630cc4587SSamuel Ortiz 79730cc4587SSamuel Ortiz pr_debug("Send rr nr %d\n", sock->recv_n); 79830cc4587SSamuel Ortiz 79930cc4587SSamuel Ortiz local = sock->local; 80030cc4587SSamuel Ortiz if (local == NULL) 80130cc4587SSamuel Ortiz return -ENODEV; 80230cc4587SSamuel Ortiz 80330cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_RR, LLCP_SEQUENCE_SIZE); 80430cc4587SSamuel Ortiz if (skb == NULL) 80530cc4587SSamuel Ortiz return -ENOMEM; 80630cc4587SSamuel Ortiz 80730cc4587SSamuel Ortiz skb_put(skb, LLCP_SEQUENCE_SIZE); 80830cc4587SSamuel Ortiz 80930cc4587SSamuel Ortiz skb->data[2] = sock->recv_n; 81030cc4587SSamuel Ortiz 81130cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 81230cc4587SSamuel Ortiz 81330cc4587SSamuel Ortiz return 0; 81430cc4587SSamuel Ortiz } 815