130cc4587SSamuel Ortiz /* 230cc4587SSamuel Ortiz * Copyright (C) 2011 Intel Corporation. All rights reserved. 330cc4587SSamuel Ortiz * 430cc4587SSamuel Ortiz * This program is free software; you can redistribute it and/or modify 530cc4587SSamuel Ortiz * it under the terms of the GNU General Public License as published by 630cc4587SSamuel Ortiz * the Free Software Foundation; either version 2 of the License, or 730cc4587SSamuel Ortiz * (at your option) any later version. 830cc4587SSamuel Ortiz * 930cc4587SSamuel Ortiz * This program is distributed in the hope that it will be useful, 1030cc4587SSamuel Ortiz * but WITHOUT ANY WARRANTY; without even the implied warranty of 1130cc4587SSamuel Ortiz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1230cc4587SSamuel Ortiz * GNU General Public License for more details. 1330cc4587SSamuel Ortiz * 1430cc4587SSamuel Ortiz * You should have received a copy of the GNU General Public License 1530cc4587SSamuel Ortiz * along with this program; if not, write to the 1630cc4587SSamuel Ortiz * Free Software Foundation, Inc., 1730cc4587SSamuel Ortiz * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1830cc4587SSamuel Ortiz */ 1930cc4587SSamuel Ortiz 2030cc4587SSamuel Ortiz #define pr_fmt(fmt) "llcp: %s: " fmt, __func__ 2130cc4587SSamuel Ortiz 2230cc4587SSamuel Ortiz #include <linux/init.h> 2330cc4587SSamuel Ortiz #include <linux/kernel.h> 2430cc4587SSamuel Ortiz #include <linux/module.h> 2530cc4587SSamuel Ortiz #include <linux/nfc.h> 2630cc4587SSamuel Ortiz 2730cc4587SSamuel Ortiz #include <net/nfc/nfc.h> 2830cc4587SSamuel Ortiz 2930cc4587SSamuel Ortiz #include "nfc.h" 3030cc4587SSamuel Ortiz #include "llcp.h" 3130cc4587SSamuel Ortiz 3230cc4587SSamuel Ortiz static u8 llcp_tlv_length[LLCP_TLV_MAX] = { 3330cc4587SSamuel Ortiz 0, 3430cc4587SSamuel Ortiz 1, /* VERSION */ 3530cc4587SSamuel Ortiz 2, /* MIUX */ 3630cc4587SSamuel Ortiz 2, /* WKS */ 3730cc4587SSamuel Ortiz 1, /* LTO */ 3830cc4587SSamuel Ortiz 1, /* RW */ 3930cc4587SSamuel Ortiz 0, /* SN */ 4030cc4587SSamuel Ortiz 1, /* OPT */ 4130cc4587SSamuel Ortiz 0, /* SDREQ */ 4230cc4587SSamuel Ortiz 2, /* SDRES */ 4330cc4587SSamuel Ortiz 4430cc4587SSamuel Ortiz }; 4530cc4587SSamuel Ortiz 4630cc4587SSamuel Ortiz static u8 llcp_tlv8(u8 *tlv, u8 type) 4730cc4587SSamuel Ortiz { 4830cc4587SSamuel Ortiz if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]]) 4930cc4587SSamuel Ortiz return 0; 5030cc4587SSamuel Ortiz 5130cc4587SSamuel Ortiz return tlv[2]; 5230cc4587SSamuel Ortiz } 5330cc4587SSamuel Ortiz 5430cc4587SSamuel Ortiz static u16 llcp_tlv16(u8 *tlv, u8 type) 5530cc4587SSamuel Ortiz { 5630cc4587SSamuel Ortiz if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]]) 5730cc4587SSamuel Ortiz return 0; 5830cc4587SSamuel Ortiz 5930cc4587SSamuel Ortiz return be16_to_cpu(*((__be16 *)(tlv + 2))); 6030cc4587SSamuel Ortiz } 6130cc4587SSamuel Ortiz 6230cc4587SSamuel Ortiz 6330cc4587SSamuel Ortiz static u8 llcp_tlv_version(u8 *tlv) 6430cc4587SSamuel Ortiz { 6530cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_VERSION); 6630cc4587SSamuel Ortiz } 6730cc4587SSamuel Ortiz 6830cc4587SSamuel Ortiz static u16 llcp_tlv_miux(u8 *tlv) 6930cc4587SSamuel Ortiz { 7030cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7ff; 7130cc4587SSamuel Ortiz } 7230cc4587SSamuel Ortiz 7330cc4587SSamuel Ortiz static u16 llcp_tlv_wks(u8 *tlv) 7430cc4587SSamuel Ortiz { 7530cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_WKS); 7630cc4587SSamuel Ortiz } 7730cc4587SSamuel Ortiz 7830cc4587SSamuel Ortiz static u16 llcp_tlv_lto(u8 *tlv) 7930cc4587SSamuel Ortiz { 8030cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_LTO); 8130cc4587SSamuel Ortiz } 8230cc4587SSamuel Ortiz 8330cc4587SSamuel Ortiz static u8 llcp_tlv_opt(u8 *tlv) 8430cc4587SSamuel Ortiz { 8530cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_OPT); 8630cc4587SSamuel Ortiz } 8730cc4587SSamuel Ortiz 8830cc4587SSamuel Ortiz static u8 llcp_tlv_rw(u8 *tlv) 8930cc4587SSamuel Ortiz { 9030cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_RW) & 0xf; 9130cc4587SSamuel Ortiz } 9230cc4587SSamuel Ortiz 9330cc4587SSamuel Ortiz u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length) 9430cc4587SSamuel Ortiz { 9530cc4587SSamuel Ortiz u8 *tlv, length; 9630cc4587SSamuel Ortiz 9730cc4587SSamuel Ortiz pr_debug("type %d\n", type); 9830cc4587SSamuel Ortiz 9930cc4587SSamuel Ortiz if (type >= LLCP_TLV_MAX) 10030cc4587SSamuel Ortiz return NULL; 10130cc4587SSamuel Ortiz 10230cc4587SSamuel Ortiz length = llcp_tlv_length[type]; 10330cc4587SSamuel Ortiz if (length == 0 && value_length == 0) 10430cc4587SSamuel Ortiz return NULL; 10530cc4587SSamuel Ortiz else if (length == 0) 10630cc4587SSamuel Ortiz length = value_length; 10730cc4587SSamuel Ortiz 10830cc4587SSamuel Ortiz *tlv_length = 2 + length; 10930cc4587SSamuel Ortiz tlv = kzalloc(2 + length, GFP_KERNEL); 11030cc4587SSamuel Ortiz if (tlv == NULL) 11130cc4587SSamuel Ortiz return tlv; 11230cc4587SSamuel Ortiz 11330cc4587SSamuel Ortiz tlv[0] = type; 11430cc4587SSamuel Ortiz tlv[1] = length; 11530cc4587SSamuel Ortiz memcpy(tlv + 2, value, length); 11630cc4587SSamuel Ortiz 11730cc4587SSamuel Ortiz return tlv; 11830cc4587SSamuel Ortiz } 11930cc4587SSamuel Ortiz 12030cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap) 12130cc4587SSamuel Ortiz { 12230cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdres; 12330cc4587SSamuel Ortiz u8 value[2]; 12430cc4587SSamuel Ortiz 12530cc4587SSamuel Ortiz sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); 12630cc4587SSamuel Ortiz if (sdres == NULL) 12730cc4587SSamuel Ortiz return NULL; 12830cc4587SSamuel Ortiz 12930cc4587SSamuel Ortiz value[0] = tid; 13030cc4587SSamuel Ortiz value[1] = sap; 13130cc4587SSamuel Ortiz 13230cc4587SSamuel Ortiz sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2, 13330cc4587SSamuel Ortiz &sdres->tlv_len); 13430cc4587SSamuel Ortiz if (sdres->tlv == NULL) { 13530cc4587SSamuel Ortiz kfree(sdres); 13630cc4587SSamuel Ortiz return NULL; 13730cc4587SSamuel Ortiz } 13830cc4587SSamuel Ortiz 13930cc4587SSamuel Ortiz sdres->tid = tid; 14030cc4587SSamuel Ortiz sdres->sap = sap; 14130cc4587SSamuel Ortiz 14230cc4587SSamuel Ortiz INIT_HLIST_NODE(&sdres->node); 14330cc4587SSamuel Ortiz 14430cc4587SSamuel Ortiz return sdres; 14530cc4587SSamuel Ortiz } 14630cc4587SSamuel Ortiz 14730cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri, 14830cc4587SSamuel Ortiz size_t uri_len) 14930cc4587SSamuel Ortiz { 15030cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 15130cc4587SSamuel Ortiz 15230cc4587SSamuel Ortiz pr_debug("uri: %s, len: %zu\n", uri, uri_len); 15330cc4587SSamuel Ortiz 15430cc4587SSamuel Ortiz sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); 15530cc4587SSamuel Ortiz if (sdreq == NULL) 15630cc4587SSamuel Ortiz return NULL; 15730cc4587SSamuel Ortiz 15830cc4587SSamuel Ortiz sdreq->tlv_len = uri_len + 3; 15930cc4587SSamuel Ortiz 16030cc4587SSamuel Ortiz if (uri[uri_len - 1] == 0) 16130cc4587SSamuel Ortiz sdreq->tlv_len--; 16230cc4587SSamuel Ortiz 16330cc4587SSamuel Ortiz sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL); 16430cc4587SSamuel Ortiz if (sdreq->tlv == NULL) { 16530cc4587SSamuel Ortiz kfree(sdreq); 16630cc4587SSamuel Ortiz return NULL; 16730cc4587SSamuel Ortiz } 16830cc4587SSamuel Ortiz 16930cc4587SSamuel Ortiz sdreq->tlv[0] = LLCP_TLV_SDREQ; 17030cc4587SSamuel Ortiz sdreq->tlv[1] = sdreq->tlv_len - 2; 17130cc4587SSamuel Ortiz sdreq->tlv[2] = tid; 17230cc4587SSamuel Ortiz 17330cc4587SSamuel Ortiz sdreq->tid = tid; 17430cc4587SSamuel Ortiz sdreq->uri = sdreq->tlv + 3; 17530cc4587SSamuel Ortiz memcpy(sdreq->uri, uri, uri_len); 17630cc4587SSamuel Ortiz 17730cc4587SSamuel Ortiz sdreq->time = jiffies; 17830cc4587SSamuel Ortiz 17930cc4587SSamuel Ortiz INIT_HLIST_NODE(&sdreq->node); 18030cc4587SSamuel Ortiz 18130cc4587SSamuel Ortiz return sdreq; 18230cc4587SSamuel Ortiz } 18330cc4587SSamuel Ortiz 18430cc4587SSamuel Ortiz void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp) 18530cc4587SSamuel Ortiz { 18630cc4587SSamuel Ortiz kfree(sdp->tlv); 18730cc4587SSamuel Ortiz kfree(sdp); 18830cc4587SSamuel Ortiz } 18930cc4587SSamuel Ortiz 19030cc4587SSamuel Ortiz void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head) 19130cc4587SSamuel Ortiz { 19230cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 19330cc4587SSamuel Ortiz struct hlist_node *n; 19430cc4587SSamuel Ortiz 19530cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, head, node) { 19630cc4587SSamuel Ortiz hlist_del(&sdp->node); 19730cc4587SSamuel Ortiz 19830cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 19930cc4587SSamuel Ortiz } 20030cc4587SSamuel Ortiz } 20130cc4587SSamuel Ortiz 20230cc4587SSamuel Ortiz int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local, 20330cc4587SSamuel Ortiz u8 *tlv_array, u16 tlv_array_len) 20430cc4587SSamuel Ortiz { 20530cc4587SSamuel Ortiz u8 *tlv = tlv_array, type, length, offset = 0; 20630cc4587SSamuel Ortiz 20730cc4587SSamuel Ortiz pr_debug("TLV array length %d\n", tlv_array_len); 20830cc4587SSamuel Ortiz 20930cc4587SSamuel Ortiz if (local == NULL) 21030cc4587SSamuel Ortiz return -ENODEV; 21130cc4587SSamuel Ortiz 21230cc4587SSamuel Ortiz while (offset < tlv_array_len) { 21330cc4587SSamuel Ortiz type = tlv[0]; 21430cc4587SSamuel Ortiz length = tlv[1]; 21530cc4587SSamuel Ortiz 21630cc4587SSamuel Ortiz pr_debug("type 0x%x length %d\n", type, length); 21730cc4587SSamuel Ortiz 21830cc4587SSamuel Ortiz switch (type) { 21930cc4587SSamuel Ortiz case LLCP_TLV_VERSION: 22030cc4587SSamuel Ortiz local->remote_version = llcp_tlv_version(tlv); 22130cc4587SSamuel Ortiz break; 22230cc4587SSamuel Ortiz case LLCP_TLV_MIUX: 22330cc4587SSamuel Ortiz local->remote_miu = llcp_tlv_miux(tlv) + 128; 22430cc4587SSamuel Ortiz break; 22530cc4587SSamuel Ortiz case LLCP_TLV_WKS: 22630cc4587SSamuel Ortiz local->remote_wks = llcp_tlv_wks(tlv); 22730cc4587SSamuel Ortiz break; 22830cc4587SSamuel Ortiz case LLCP_TLV_LTO: 22930cc4587SSamuel Ortiz local->remote_lto = llcp_tlv_lto(tlv) * 10; 23030cc4587SSamuel Ortiz break; 23130cc4587SSamuel Ortiz case LLCP_TLV_OPT: 23230cc4587SSamuel Ortiz local->remote_opt = llcp_tlv_opt(tlv); 23330cc4587SSamuel Ortiz break; 23430cc4587SSamuel Ortiz default: 23530cc4587SSamuel Ortiz pr_err("Invalid gt tlv value 0x%x\n", type); 23630cc4587SSamuel Ortiz break; 23730cc4587SSamuel Ortiz } 23830cc4587SSamuel Ortiz 23930cc4587SSamuel Ortiz offset += length + 2; 24030cc4587SSamuel Ortiz tlv += length + 2; 24130cc4587SSamuel Ortiz } 24230cc4587SSamuel Ortiz 24330cc4587SSamuel Ortiz pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x\n", 24430cc4587SSamuel Ortiz local->remote_version, local->remote_miu, 24530cc4587SSamuel Ortiz local->remote_lto, local->remote_opt, 24630cc4587SSamuel Ortiz local->remote_wks); 24730cc4587SSamuel Ortiz 24830cc4587SSamuel Ortiz return 0; 24930cc4587SSamuel Ortiz } 25030cc4587SSamuel Ortiz 25130cc4587SSamuel Ortiz int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock, 25230cc4587SSamuel Ortiz u8 *tlv_array, u16 tlv_array_len) 25330cc4587SSamuel Ortiz { 25430cc4587SSamuel Ortiz u8 *tlv = tlv_array, type, length, offset = 0; 25530cc4587SSamuel Ortiz 25630cc4587SSamuel Ortiz pr_debug("TLV array length %d\n", tlv_array_len); 25730cc4587SSamuel Ortiz 25830cc4587SSamuel Ortiz if (sock == NULL) 25930cc4587SSamuel Ortiz return -ENOTCONN; 26030cc4587SSamuel Ortiz 26130cc4587SSamuel Ortiz while (offset < tlv_array_len) { 26230cc4587SSamuel Ortiz type = tlv[0]; 26330cc4587SSamuel Ortiz length = tlv[1]; 26430cc4587SSamuel Ortiz 26530cc4587SSamuel Ortiz pr_debug("type 0x%x length %d\n", type, length); 26630cc4587SSamuel Ortiz 26730cc4587SSamuel Ortiz switch (type) { 26830cc4587SSamuel Ortiz case LLCP_TLV_MIUX: 26930cc4587SSamuel Ortiz sock->remote_miu = llcp_tlv_miux(tlv) + 128; 27030cc4587SSamuel Ortiz break; 27130cc4587SSamuel Ortiz case LLCP_TLV_RW: 27230cc4587SSamuel Ortiz sock->remote_rw = llcp_tlv_rw(tlv); 27330cc4587SSamuel Ortiz break; 27430cc4587SSamuel Ortiz case LLCP_TLV_SN: 27530cc4587SSamuel Ortiz break; 27630cc4587SSamuel Ortiz default: 27730cc4587SSamuel Ortiz pr_err("Invalid gt tlv value 0x%x\n", type); 27830cc4587SSamuel Ortiz break; 27930cc4587SSamuel Ortiz } 28030cc4587SSamuel Ortiz 28130cc4587SSamuel Ortiz offset += length + 2; 28230cc4587SSamuel Ortiz tlv += length + 2; 28330cc4587SSamuel Ortiz } 28430cc4587SSamuel Ortiz 28530cc4587SSamuel Ortiz pr_debug("sock %p rw %d miu %d\n", sock, 28630cc4587SSamuel Ortiz sock->remote_rw, sock->remote_miu); 28730cc4587SSamuel Ortiz 28830cc4587SSamuel Ortiz return 0; 28930cc4587SSamuel Ortiz } 29030cc4587SSamuel Ortiz 29130cc4587SSamuel Ortiz static struct sk_buff *llcp_add_header(struct sk_buff *pdu, 29230cc4587SSamuel Ortiz u8 dsap, u8 ssap, u8 ptype) 29330cc4587SSamuel Ortiz { 29430cc4587SSamuel Ortiz u8 header[2]; 29530cc4587SSamuel Ortiz 29630cc4587SSamuel Ortiz pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap); 29730cc4587SSamuel Ortiz 29830cc4587SSamuel Ortiz header[0] = (u8)((dsap << 2) | (ptype >> 2)); 29930cc4587SSamuel Ortiz header[1] = (u8)((ptype << 6) | ssap); 30030cc4587SSamuel Ortiz 30130cc4587SSamuel Ortiz pr_debug("header 0x%x 0x%x\n", header[0], header[1]); 30230cc4587SSamuel Ortiz 30330cc4587SSamuel Ortiz memcpy(skb_put(pdu, LLCP_HEADER_SIZE), header, LLCP_HEADER_SIZE); 30430cc4587SSamuel Ortiz 30530cc4587SSamuel Ortiz return pdu; 30630cc4587SSamuel Ortiz } 30730cc4587SSamuel Ortiz 30830cc4587SSamuel Ortiz static struct sk_buff *llcp_add_tlv(struct sk_buff *pdu, u8 *tlv, 30930cc4587SSamuel Ortiz u8 tlv_length) 31030cc4587SSamuel Ortiz { 31130cc4587SSamuel Ortiz /* XXX Add an skb length check */ 31230cc4587SSamuel Ortiz 31330cc4587SSamuel Ortiz if (tlv == NULL) 31430cc4587SSamuel Ortiz return NULL; 31530cc4587SSamuel Ortiz 31630cc4587SSamuel Ortiz memcpy(skb_put(pdu, tlv_length), tlv, tlv_length); 31730cc4587SSamuel Ortiz 31830cc4587SSamuel Ortiz return pdu; 31930cc4587SSamuel Ortiz } 32030cc4587SSamuel Ortiz 32130cc4587SSamuel Ortiz static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock, 32230cc4587SSamuel Ortiz u8 cmd, u16 size) 32330cc4587SSamuel Ortiz { 32430cc4587SSamuel Ortiz struct sk_buff *skb; 32530cc4587SSamuel Ortiz int err; 32630cc4587SSamuel Ortiz 32730cc4587SSamuel Ortiz if (sock->ssap == 0) 32830cc4587SSamuel Ortiz return NULL; 32930cc4587SSamuel Ortiz 33030cc4587SSamuel Ortiz skb = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT, 33130cc4587SSamuel Ortiz size + LLCP_HEADER_SIZE, &err); 33230cc4587SSamuel Ortiz if (skb == NULL) { 33330cc4587SSamuel Ortiz pr_err("Could not allocate PDU\n"); 33430cc4587SSamuel Ortiz return NULL; 33530cc4587SSamuel Ortiz } 33630cc4587SSamuel Ortiz 33730cc4587SSamuel Ortiz skb = llcp_add_header(skb, sock->dsap, sock->ssap, cmd); 33830cc4587SSamuel Ortiz 33930cc4587SSamuel Ortiz return skb; 34030cc4587SSamuel Ortiz } 34130cc4587SSamuel Ortiz 342*58e3dd15SThierry Escande int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock) 34330cc4587SSamuel Ortiz { 34430cc4587SSamuel Ortiz struct sk_buff *skb; 34530cc4587SSamuel Ortiz struct nfc_dev *dev; 34630cc4587SSamuel Ortiz struct nfc_llcp_local *local; 34730cc4587SSamuel Ortiz 34830cc4587SSamuel Ortiz pr_debug("Sending DISC\n"); 34930cc4587SSamuel Ortiz 35030cc4587SSamuel Ortiz local = sock->local; 35130cc4587SSamuel Ortiz if (local == NULL) 35230cc4587SSamuel Ortiz return -ENODEV; 35330cc4587SSamuel Ortiz 35430cc4587SSamuel Ortiz dev = sock->dev; 35530cc4587SSamuel Ortiz if (dev == NULL) 35630cc4587SSamuel Ortiz return -ENODEV; 35730cc4587SSamuel Ortiz 35830cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0); 35930cc4587SSamuel Ortiz if (skb == NULL) 36030cc4587SSamuel Ortiz return -ENOMEM; 36130cc4587SSamuel Ortiz 36230cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 36330cc4587SSamuel Ortiz 36430cc4587SSamuel Ortiz return 0; 36530cc4587SSamuel Ortiz } 36630cc4587SSamuel Ortiz 36730cc4587SSamuel Ortiz int nfc_llcp_send_symm(struct nfc_dev *dev) 36830cc4587SSamuel Ortiz { 36930cc4587SSamuel Ortiz struct sk_buff *skb; 37030cc4587SSamuel Ortiz struct nfc_llcp_local *local; 37130cc4587SSamuel Ortiz u16 size = 0; 37230cc4587SSamuel Ortiz 37330cc4587SSamuel Ortiz pr_debug("Sending SYMM\n"); 37430cc4587SSamuel Ortiz 37530cc4587SSamuel Ortiz local = nfc_llcp_find_local(dev); 37630cc4587SSamuel Ortiz if (local == NULL) 37730cc4587SSamuel Ortiz return -ENODEV; 37830cc4587SSamuel Ortiz 37930cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 38030cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 38130cc4587SSamuel Ortiz 38230cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 38330cc4587SSamuel Ortiz if (skb == NULL) 38430cc4587SSamuel Ortiz return -ENOMEM; 38530cc4587SSamuel Ortiz 38630cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 38730cc4587SSamuel Ortiz 38830cc4587SSamuel Ortiz skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); 38930cc4587SSamuel Ortiz 39030cc4587SSamuel Ortiz __net_timestamp(skb); 39130cc4587SSamuel Ortiz 39230cc4587SSamuel Ortiz nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); 39330cc4587SSamuel Ortiz 39430cc4587SSamuel Ortiz return nfc_data_exchange(dev, local->target_idx, skb, 39530cc4587SSamuel Ortiz nfc_llcp_recv, local); 39630cc4587SSamuel Ortiz } 39730cc4587SSamuel Ortiz 39830cc4587SSamuel Ortiz int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) 39930cc4587SSamuel Ortiz { 40030cc4587SSamuel Ortiz struct nfc_llcp_local *local; 40130cc4587SSamuel Ortiz struct sk_buff *skb; 40230cc4587SSamuel Ortiz u8 *service_name_tlv = NULL, service_name_tlv_length; 40330cc4587SSamuel Ortiz u8 *miux_tlv = NULL, miux_tlv_length; 40430cc4587SSamuel Ortiz u8 *rw_tlv = NULL, rw_tlv_length, rw; 40530cc4587SSamuel Ortiz int err; 40630cc4587SSamuel Ortiz u16 size = 0, miux; 40730cc4587SSamuel Ortiz 40830cc4587SSamuel Ortiz pr_debug("Sending CONNECT\n"); 40930cc4587SSamuel Ortiz 41030cc4587SSamuel Ortiz local = sock->local; 41130cc4587SSamuel Ortiz if (local == NULL) 41230cc4587SSamuel Ortiz return -ENODEV; 41330cc4587SSamuel Ortiz 41430cc4587SSamuel Ortiz if (sock->service_name != NULL) { 41530cc4587SSamuel Ortiz service_name_tlv = nfc_llcp_build_tlv(LLCP_TLV_SN, 41630cc4587SSamuel Ortiz sock->service_name, 41730cc4587SSamuel Ortiz sock->service_name_len, 41830cc4587SSamuel Ortiz &service_name_tlv_length); 41930cc4587SSamuel Ortiz size += service_name_tlv_length; 42030cc4587SSamuel Ortiz } 42130cc4587SSamuel Ortiz 42230cc4587SSamuel Ortiz /* If the socket parameters are not set, use the local ones */ 42330cc4587SSamuel Ortiz miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ? 42430cc4587SSamuel Ortiz local->miux : sock->miux; 42530cc4587SSamuel Ortiz rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw; 42630cc4587SSamuel Ortiz 42730cc4587SSamuel Ortiz miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, 42830cc4587SSamuel Ortiz &miux_tlv_length); 42930cc4587SSamuel Ortiz size += miux_tlv_length; 43030cc4587SSamuel Ortiz 43130cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 43230cc4587SSamuel Ortiz size += rw_tlv_length; 43330cc4587SSamuel Ortiz 43430cc4587SSamuel Ortiz pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len); 43530cc4587SSamuel Ortiz 43630cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CONNECT, size); 43730cc4587SSamuel Ortiz if (skb == NULL) { 43830cc4587SSamuel Ortiz err = -ENOMEM; 43930cc4587SSamuel Ortiz goto error_tlv; 44030cc4587SSamuel Ortiz } 44130cc4587SSamuel Ortiz 44230cc4587SSamuel Ortiz if (service_name_tlv != NULL) 44330cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, service_name_tlv, 44430cc4587SSamuel Ortiz service_name_tlv_length); 44530cc4587SSamuel Ortiz 44630cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 44730cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 44830cc4587SSamuel Ortiz 44930cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 45030cc4587SSamuel Ortiz 45130cc4587SSamuel Ortiz return 0; 45230cc4587SSamuel Ortiz 45330cc4587SSamuel Ortiz error_tlv: 45430cc4587SSamuel Ortiz pr_err("error %d\n", err); 45530cc4587SSamuel Ortiz 45630cc4587SSamuel Ortiz kfree(service_name_tlv); 45730cc4587SSamuel Ortiz kfree(miux_tlv); 45830cc4587SSamuel Ortiz kfree(rw_tlv); 45930cc4587SSamuel Ortiz 46030cc4587SSamuel Ortiz return err; 46130cc4587SSamuel Ortiz } 46230cc4587SSamuel Ortiz 46330cc4587SSamuel Ortiz int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) 46430cc4587SSamuel Ortiz { 46530cc4587SSamuel Ortiz struct nfc_llcp_local *local; 46630cc4587SSamuel Ortiz struct sk_buff *skb; 46730cc4587SSamuel Ortiz u8 *miux_tlv = NULL, miux_tlv_length; 46830cc4587SSamuel Ortiz u8 *rw_tlv = NULL, rw_tlv_length, rw; 46930cc4587SSamuel Ortiz int err; 47030cc4587SSamuel Ortiz u16 size = 0, miux; 47130cc4587SSamuel Ortiz 47230cc4587SSamuel Ortiz pr_debug("Sending CC\n"); 47330cc4587SSamuel Ortiz 47430cc4587SSamuel Ortiz local = sock->local; 47530cc4587SSamuel Ortiz if (local == NULL) 47630cc4587SSamuel Ortiz return -ENODEV; 47730cc4587SSamuel Ortiz 47830cc4587SSamuel Ortiz /* If the socket parameters are not set, use the local ones */ 47930cc4587SSamuel Ortiz miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ? 48030cc4587SSamuel Ortiz local->miux : sock->miux; 48130cc4587SSamuel Ortiz rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw; 48230cc4587SSamuel Ortiz 48330cc4587SSamuel Ortiz miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, 48430cc4587SSamuel Ortiz &miux_tlv_length); 48530cc4587SSamuel Ortiz size += miux_tlv_length; 48630cc4587SSamuel Ortiz 48730cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 48830cc4587SSamuel Ortiz size += rw_tlv_length; 48930cc4587SSamuel Ortiz 49030cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); 49130cc4587SSamuel Ortiz if (skb == NULL) { 49230cc4587SSamuel Ortiz err = -ENOMEM; 49330cc4587SSamuel Ortiz goto error_tlv; 49430cc4587SSamuel Ortiz } 49530cc4587SSamuel Ortiz 49630cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 49730cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 49830cc4587SSamuel Ortiz 49930cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 50030cc4587SSamuel Ortiz 50130cc4587SSamuel Ortiz return 0; 50230cc4587SSamuel Ortiz 50330cc4587SSamuel Ortiz error_tlv: 50430cc4587SSamuel Ortiz pr_err("error %d\n", err); 50530cc4587SSamuel Ortiz 50630cc4587SSamuel Ortiz kfree(miux_tlv); 50730cc4587SSamuel Ortiz kfree(rw_tlv); 50830cc4587SSamuel Ortiz 50930cc4587SSamuel Ortiz return err; 51030cc4587SSamuel Ortiz } 51130cc4587SSamuel Ortiz 51230cc4587SSamuel Ortiz static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local, 51330cc4587SSamuel Ortiz size_t tlv_length) 51430cc4587SSamuel Ortiz { 51530cc4587SSamuel Ortiz struct sk_buff *skb; 51630cc4587SSamuel Ortiz struct nfc_dev *dev; 51730cc4587SSamuel Ortiz u16 size = 0; 51830cc4587SSamuel Ortiz 51930cc4587SSamuel Ortiz if (local == NULL) 52030cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 52130cc4587SSamuel Ortiz 52230cc4587SSamuel Ortiz dev = local->dev; 52330cc4587SSamuel Ortiz if (dev == NULL) 52430cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 52530cc4587SSamuel Ortiz 52630cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 52730cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 52830cc4587SSamuel Ortiz size += tlv_length; 52930cc4587SSamuel Ortiz 53030cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 53130cc4587SSamuel Ortiz if (skb == NULL) 53230cc4587SSamuel Ortiz return ERR_PTR(-ENOMEM); 53330cc4587SSamuel Ortiz 53430cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 53530cc4587SSamuel Ortiz 53630cc4587SSamuel Ortiz skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL); 53730cc4587SSamuel Ortiz 53830cc4587SSamuel Ortiz return skb; 53930cc4587SSamuel Ortiz } 54030cc4587SSamuel Ortiz 54130cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local, 54230cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 54330cc4587SSamuel Ortiz { 54430cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 54530cc4587SSamuel Ortiz struct hlist_node *n; 54630cc4587SSamuel Ortiz struct sk_buff *skb; 54730cc4587SSamuel Ortiz 54830cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 54930cc4587SSamuel Ortiz if (IS_ERR(skb)) 55030cc4587SSamuel Ortiz return PTR_ERR(skb); 55130cc4587SSamuel Ortiz 55230cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, tlv_list, node) { 55330cc4587SSamuel Ortiz memcpy(skb_put(skb, sdp->tlv_len), sdp->tlv, sdp->tlv_len); 55430cc4587SSamuel Ortiz 55530cc4587SSamuel Ortiz hlist_del(&sdp->node); 55630cc4587SSamuel Ortiz 55730cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 55830cc4587SSamuel Ortiz } 55930cc4587SSamuel Ortiz 56030cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 56130cc4587SSamuel Ortiz 56230cc4587SSamuel Ortiz return 0; 56330cc4587SSamuel Ortiz } 56430cc4587SSamuel Ortiz 56530cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local, 56630cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 56730cc4587SSamuel Ortiz { 56830cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 56930cc4587SSamuel Ortiz struct hlist_node *n; 57030cc4587SSamuel Ortiz struct sk_buff *skb; 57130cc4587SSamuel Ortiz 57230cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 57330cc4587SSamuel Ortiz if (IS_ERR(skb)) 57430cc4587SSamuel Ortiz return PTR_ERR(skb); 57530cc4587SSamuel Ortiz 57630cc4587SSamuel Ortiz mutex_lock(&local->sdreq_lock); 57730cc4587SSamuel Ortiz 57830cc4587SSamuel Ortiz if (hlist_empty(&local->pending_sdreqs)) 57930cc4587SSamuel Ortiz mod_timer(&local->sdreq_timer, 58030cc4587SSamuel Ortiz jiffies + msecs_to_jiffies(3 * local->remote_lto)); 58130cc4587SSamuel Ortiz 58230cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdreq, n, tlv_list, node) { 58330cc4587SSamuel Ortiz pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri); 58430cc4587SSamuel Ortiz 58530cc4587SSamuel Ortiz memcpy(skb_put(skb, sdreq->tlv_len), sdreq->tlv, 58630cc4587SSamuel Ortiz sdreq->tlv_len); 58730cc4587SSamuel Ortiz 58830cc4587SSamuel Ortiz hlist_del(&sdreq->node); 58930cc4587SSamuel Ortiz 59030cc4587SSamuel Ortiz hlist_add_head(&sdreq->node, &local->pending_sdreqs); 59130cc4587SSamuel Ortiz } 59230cc4587SSamuel Ortiz 59330cc4587SSamuel Ortiz mutex_unlock(&local->sdreq_lock); 59430cc4587SSamuel Ortiz 59530cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 59630cc4587SSamuel Ortiz 59730cc4587SSamuel Ortiz return 0; 59830cc4587SSamuel Ortiz } 59930cc4587SSamuel Ortiz 60030cc4587SSamuel Ortiz int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason) 60130cc4587SSamuel Ortiz { 60230cc4587SSamuel Ortiz struct sk_buff *skb; 60330cc4587SSamuel Ortiz struct nfc_dev *dev; 60430cc4587SSamuel Ortiz u16 size = 1; /* Reason code */ 60530cc4587SSamuel Ortiz 60630cc4587SSamuel Ortiz pr_debug("Sending DM reason 0x%x\n", reason); 60730cc4587SSamuel Ortiz 60830cc4587SSamuel Ortiz if (local == NULL) 60930cc4587SSamuel Ortiz return -ENODEV; 61030cc4587SSamuel Ortiz 61130cc4587SSamuel Ortiz dev = local->dev; 61230cc4587SSamuel Ortiz if (dev == NULL) 61330cc4587SSamuel Ortiz return -ENODEV; 61430cc4587SSamuel Ortiz 61530cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 61630cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 61730cc4587SSamuel Ortiz 61830cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 61930cc4587SSamuel Ortiz if (skb == NULL) 62030cc4587SSamuel Ortiz return -ENOMEM; 62130cc4587SSamuel Ortiz 62230cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 62330cc4587SSamuel Ortiz 62430cc4587SSamuel Ortiz skb = llcp_add_header(skb, dsap, ssap, LLCP_PDU_DM); 62530cc4587SSamuel Ortiz 62630cc4587SSamuel Ortiz memcpy(skb_put(skb, 1), &reason, 1); 62730cc4587SSamuel Ortiz 62830cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 62930cc4587SSamuel Ortiz 63030cc4587SSamuel Ortiz return 0; 63130cc4587SSamuel Ortiz } 63230cc4587SSamuel Ortiz 63330cc4587SSamuel Ortiz int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, 63430cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 63530cc4587SSamuel Ortiz { 63630cc4587SSamuel Ortiz struct sk_buff *pdu; 63730cc4587SSamuel Ortiz struct sock *sk = &sock->sk; 63830cc4587SSamuel Ortiz struct nfc_llcp_local *local; 63930cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 64030cc4587SSamuel Ortiz u8 *msg_data, *msg_ptr; 64130cc4587SSamuel Ortiz u16 remote_miu; 64230cc4587SSamuel Ortiz 64330cc4587SSamuel Ortiz pr_debug("Send I frame len %zd\n", len); 64430cc4587SSamuel Ortiz 64530cc4587SSamuel Ortiz local = sock->local; 64630cc4587SSamuel Ortiz if (local == NULL) 64730cc4587SSamuel Ortiz return -ENODEV; 64830cc4587SSamuel Ortiz 64930cc4587SSamuel Ortiz /* Remote is ready but has not acknowledged our frames */ 65030cc4587SSamuel Ortiz if((sock->remote_ready && 65130cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw && 65230cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 65330cc4587SSamuel Ortiz pr_err("Pending queue is full %d frames\n", 65430cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue)); 65530cc4587SSamuel Ortiz return -ENOBUFS; 65630cc4587SSamuel Ortiz } 65730cc4587SSamuel Ortiz 65830cc4587SSamuel Ortiz /* Remote is not ready and we've been queueing enough frames */ 65930cc4587SSamuel Ortiz if ((!sock->remote_ready && 66030cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 66130cc4587SSamuel Ortiz pr_err("Tx queue is full %d frames\n", 66230cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue)); 66330cc4587SSamuel Ortiz return -ENOBUFS; 66430cc4587SSamuel Ortiz } 66530cc4587SSamuel Ortiz 66630cc4587SSamuel Ortiz msg_data = kzalloc(len, GFP_KERNEL); 66730cc4587SSamuel Ortiz if (msg_data == NULL) 66830cc4587SSamuel Ortiz return -ENOMEM; 66930cc4587SSamuel Ortiz 67030cc4587SSamuel Ortiz if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) { 67130cc4587SSamuel Ortiz kfree(msg_data); 67230cc4587SSamuel Ortiz return -EFAULT; 67330cc4587SSamuel Ortiz } 67430cc4587SSamuel Ortiz 67530cc4587SSamuel Ortiz remaining_len = len; 67630cc4587SSamuel Ortiz msg_ptr = msg_data; 67730cc4587SSamuel Ortiz 67830cc4587SSamuel Ortiz do { 67930cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 68030cc4587SSamuel Ortiz local->remote_miu : sock->remote_miu; 68130cc4587SSamuel Ortiz 68230cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 68330cc4587SSamuel Ortiz 68430cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 68530cc4587SSamuel Ortiz frag_len, remaining_len); 68630cc4587SSamuel Ortiz 68730cc4587SSamuel Ortiz pdu = llcp_allocate_pdu(sock, LLCP_PDU_I, 68830cc4587SSamuel Ortiz frag_len + LLCP_SEQUENCE_SIZE); 68930cc4587SSamuel Ortiz if (pdu == NULL) 69030cc4587SSamuel Ortiz return -ENOMEM; 69130cc4587SSamuel Ortiz 69230cc4587SSamuel Ortiz skb_put(pdu, LLCP_SEQUENCE_SIZE); 69330cc4587SSamuel Ortiz 69430cc4587SSamuel Ortiz if (likely(frag_len > 0)) 69530cc4587SSamuel Ortiz memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len); 69630cc4587SSamuel Ortiz 69730cc4587SSamuel Ortiz skb_queue_tail(&sock->tx_queue, pdu); 69830cc4587SSamuel Ortiz 69930cc4587SSamuel Ortiz lock_sock(sk); 70030cc4587SSamuel Ortiz 70130cc4587SSamuel Ortiz nfc_llcp_queue_i_frames(sock); 70230cc4587SSamuel Ortiz 70330cc4587SSamuel Ortiz release_sock(sk); 70430cc4587SSamuel Ortiz 70530cc4587SSamuel Ortiz remaining_len -= frag_len; 70630cc4587SSamuel Ortiz msg_ptr += frag_len; 70730cc4587SSamuel Ortiz } while (remaining_len > 0); 70830cc4587SSamuel Ortiz 70930cc4587SSamuel Ortiz kfree(msg_data); 71030cc4587SSamuel Ortiz 71130cc4587SSamuel Ortiz return len; 71230cc4587SSamuel Ortiz } 71330cc4587SSamuel Ortiz 71430cc4587SSamuel Ortiz int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, 71530cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 71630cc4587SSamuel Ortiz { 71730cc4587SSamuel Ortiz struct sk_buff *pdu; 71830cc4587SSamuel Ortiz struct nfc_llcp_local *local; 71930cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 72030cc4587SSamuel Ortiz u8 *msg_ptr, *msg_data; 72130cc4587SSamuel Ortiz u16 remote_miu; 72230cc4587SSamuel Ortiz int err; 72330cc4587SSamuel Ortiz 72430cc4587SSamuel Ortiz pr_debug("Send UI frame len %zd\n", len); 72530cc4587SSamuel Ortiz 72630cc4587SSamuel Ortiz local = sock->local; 72730cc4587SSamuel Ortiz if (local == NULL) 72830cc4587SSamuel Ortiz return -ENODEV; 72930cc4587SSamuel Ortiz 73030cc4587SSamuel Ortiz msg_data = kzalloc(len, GFP_KERNEL); 73130cc4587SSamuel Ortiz if (msg_data == NULL) 73230cc4587SSamuel Ortiz return -ENOMEM; 73330cc4587SSamuel Ortiz 73430cc4587SSamuel Ortiz if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) { 73530cc4587SSamuel Ortiz kfree(msg_data); 73630cc4587SSamuel Ortiz return -EFAULT; 73730cc4587SSamuel Ortiz } 73830cc4587SSamuel Ortiz 73930cc4587SSamuel Ortiz remaining_len = len; 74030cc4587SSamuel Ortiz msg_ptr = msg_data; 74130cc4587SSamuel Ortiz 74230cc4587SSamuel Ortiz do { 74330cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 74430cc4587SSamuel Ortiz local->remote_miu : sock->remote_miu; 74530cc4587SSamuel Ortiz 74630cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 74730cc4587SSamuel Ortiz 74830cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 74930cc4587SSamuel Ortiz frag_len, remaining_len); 75030cc4587SSamuel Ortiz 75130cc4587SSamuel Ortiz pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT, 75230cc4587SSamuel Ortiz frag_len + LLCP_HEADER_SIZE, &err); 75330cc4587SSamuel Ortiz if (pdu == NULL) { 75430cc4587SSamuel Ortiz pr_err("Could not allocate PDU\n"); 75530cc4587SSamuel Ortiz continue; 75630cc4587SSamuel Ortiz } 75730cc4587SSamuel Ortiz 75830cc4587SSamuel Ortiz pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI); 75930cc4587SSamuel Ortiz 76030cc4587SSamuel Ortiz if (likely(frag_len > 0)) 76130cc4587SSamuel Ortiz memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len); 76230cc4587SSamuel Ortiz 76330cc4587SSamuel Ortiz /* No need to check for the peer RW for UI frames */ 76430cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, pdu); 76530cc4587SSamuel Ortiz 76630cc4587SSamuel Ortiz remaining_len -= frag_len; 76730cc4587SSamuel Ortiz msg_ptr += frag_len; 76830cc4587SSamuel Ortiz } while (remaining_len > 0); 76930cc4587SSamuel Ortiz 77030cc4587SSamuel Ortiz kfree(msg_data); 77130cc4587SSamuel Ortiz 77230cc4587SSamuel Ortiz return len; 77330cc4587SSamuel Ortiz } 77430cc4587SSamuel Ortiz 77530cc4587SSamuel Ortiz int nfc_llcp_send_rr(struct nfc_llcp_sock *sock) 77630cc4587SSamuel Ortiz { 77730cc4587SSamuel Ortiz struct sk_buff *skb; 77830cc4587SSamuel Ortiz struct nfc_llcp_local *local; 77930cc4587SSamuel Ortiz 78030cc4587SSamuel Ortiz pr_debug("Send rr nr %d\n", sock->recv_n); 78130cc4587SSamuel Ortiz 78230cc4587SSamuel Ortiz local = sock->local; 78330cc4587SSamuel Ortiz if (local == NULL) 78430cc4587SSamuel Ortiz return -ENODEV; 78530cc4587SSamuel Ortiz 78630cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_RR, LLCP_SEQUENCE_SIZE); 78730cc4587SSamuel Ortiz if (skb == NULL) 78830cc4587SSamuel Ortiz return -ENOMEM; 78930cc4587SSamuel Ortiz 79030cc4587SSamuel Ortiz skb_put(skb, LLCP_SEQUENCE_SIZE); 79130cc4587SSamuel Ortiz 79230cc4587SSamuel Ortiz skb->data[2] = sock->recv_n; 79330cc4587SSamuel Ortiz 79430cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 79530cc4587SSamuel Ortiz 79630cc4587SSamuel Ortiz return 0; 79730cc4587SSamuel Ortiz } 798