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 1598b32decSJeff Kirsher * along with this program; if not, see <http://www.gnu.org/licenses/>. 1630cc4587SSamuel Ortiz */ 1730cc4587SSamuel Ortiz 1830cc4587SSamuel Ortiz #define pr_fmt(fmt) "llcp: %s: " fmt, __func__ 1930cc4587SSamuel Ortiz 2030cc4587SSamuel Ortiz #include <linux/init.h> 2130cc4587SSamuel Ortiz #include <linux/kernel.h> 2230cc4587SSamuel Ortiz #include <linux/module.h> 2330cc4587SSamuel Ortiz #include <linux/nfc.h> 2430cc4587SSamuel Ortiz 2530cc4587SSamuel Ortiz #include <net/nfc/nfc.h> 2630cc4587SSamuel Ortiz 2730cc4587SSamuel Ortiz #include "nfc.h" 2830cc4587SSamuel Ortiz #include "llcp.h" 2930cc4587SSamuel Ortiz 3030cc4587SSamuel Ortiz static u8 llcp_tlv_length[LLCP_TLV_MAX] = { 3130cc4587SSamuel Ortiz 0, 3230cc4587SSamuel Ortiz 1, /* VERSION */ 3330cc4587SSamuel Ortiz 2, /* MIUX */ 3430cc4587SSamuel Ortiz 2, /* WKS */ 3530cc4587SSamuel Ortiz 1, /* LTO */ 3630cc4587SSamuel Ortiz 1, /* RW */ 3730cc4587SSamuel Ortiz 0, /* SN */ 3830cc4587SSamuel Ortiz 1, /* OPT */ 3930cc4587SSamuel Ortiz 0, /* SDREQ */ 4030cc4587SSamuel Ortiz 2, /* SDRES */ 4130cc4587SSamuel Ortiz 4230cc4587SSamuel Ortiz }; 4330cc4587SSamuel Ortiz 4430cc4587SSamuel Ortiz static u8 llcp_tlv8(u8 *tlv, u8 type) 4530cc4587SSamuel Ortiz { 4630cc4587SSamuel Ortiz if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]]) 4730cc4587SSamuel Ortiz return 0; 4830cc4587SSamuel Ortiz 4930cc4587SSamuel Ortiz return tlv[2]; 5030cc4587SSamuel Ortiz } 5130cc4587SSamuel Ortiz 5230cc4587SSamuel Ortiz static u16 llcp_tlv16(u8 *tlv, u8 type) 5330cc4587SSamuel Ortiz { 5430cc4587SSamuel Ortiz if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]]) 5530cc4587SSamuel Ortiz return 0; 5630cc4587SSamuel Ortiz 5730cc4587SSamuel Ortiz return be16_to_cpu(*((__be16 *)(tlv + 2))); 5830cc4587SSamuel Ortiz } 5930cc4587SSamuel Ortiz 6030cc4587SSamuel Ortiz 6130cc4587SSamuel Ortiz static u8 llcp_tlv_version(u8 *tlv) 6230cc4587SSamuel Ortiz { 6330cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_VERSION); 6430cc4587SSamuel Ortiz } 6530cc4587SSamuel Ortiz 6630cc4587SSamuel Ortiz static u16 llcp_tlv_miux(u8 *tlv) 6730cc4587SSamuel Ortiz { 6830cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7ff; 6930cc4587SSamuel Ortiz } 7030cc4587SSamuel Ortiz 7130cc4587SSamuel Ortiz static u16 llcp_tlv_wks(u8 *tlv) 7230cc4587SSamuel Ortiz { 7330cc4587SSamuel Ortiz return llcp_tlv16(tlv, LLCP_TLV_WKS); 7430cc4587SSamuel Ortiz } 7530cc4587SSamuel Ortiz 7630cc4587SSamuel Ortiz static u16 llcp_tlv_lto(u8 *tlv) 7730cc4587SSamuel Ortiz { 7830cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_LTO); 7930cc4587SSamuel Ortiz } 8030cc4587SSamuel Ortiz 8130cc4587SSamuel Ortiz static u8 llcp_tlv_opt(u8 *tlv) 8230cc4587SSamuel Ortiz { 8330cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_OPT); 8430cc4587SSamuel Ortiz } 8530cc4587SSamuel Ortiz 8630cc4587SSamuel Ortiz static u8 llcp_tlv_rw(u8 *tlv) 8730cc4587SSamuel Ortiz { 8830cc4587SSamuel Ortiz return llcp_tlv8(tlv, LLCP_TLV_RW) & 0xf; 8930cc4587SSamuel Ortiz } 9030cc4587SSamuel Ortiz 9130cc4587SSamuel Ortiz u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length) 9230cc4587SSamuel Ortiz { 9330cc4587SSamuel Ortiz u8 *tlv, length; 9430cc4587SSamuel Ortiz 9530cc4587SSamuel Ortiz pr_debug("type %d\n", type); 9630cc4587SSamuel Ortiz 9730cc4587SSamuel Ortiz if (type >= LLCP_TLV_MAX) 9830cc4587SSamuel Ortiz return NULL; 9930cc4587SSamuel Ortiz 10030cc4587SSamuel Ortiz length = llcp_tlv_length[type]; 10130cc4587SSamuel Ortiz if (length == 0 && value_length == 0) 10230cc4587SSamuel Ortiz return NULL; 10330cc4587SSamuel Ortiz else if (length == 0) 10430cc4587SSamuel Ortiz length = value_length; 10530cc4587SSamuel Ortiz 10630cc4587SSamuel Ortiz *tlv_length = 2 + length; 10730cc4587SSamuel Ortiz tlv = kzalloc(2 + length, GFP_KERNEL); 10830cc4587SSamuel Ortiz if (tlv == NULL) 10930cc4587SSamuel Ortiz return tlv; 11030cc4587SSamuel Ortiz 11130cc4587SSamuel Ortiz tlv[0] = type; 11230cc4587SSamuel Ortiz tlv[1] = length; 11330cc4587SSamuel Ortiz memcpy(tlv + 2, value, length); 11430cc4587SSamuel Ortiz 11530cc4587SSamuel Ortiz return tlv; 11630cc4587SSamuel Ortiz } 11730cc4587SSamuel Ortiz 11830cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap) 11930cc4587SSamuel Ortiz { 12030cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdres; 12130cc4587SSamuel Ortiz u8 value[2]; 12230cc4587SSamuel Ortiz 12330cc4587SSamuel Ortiz sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); 12430cc4587SSamuel Ortiz if (sdres == NULL) 12530cc4587SSamuel Ortiz return NULL; 12630cc4587SSamuel Ortiz 12730cc4587SSamuel Ortiz value[0] = tid; 12830cc4587SSamuel Ortiz value[1] = sap; 12930cc4587SSamuel Ortiz 13030cc4587SSamuel Ortiz sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2, 13130cc4587SSamuel Ortiz &sdres->tlv_len); 13230cc4587SSamuel Ortiz if (sdres->tlv == NULL) { 13330cc4587SSamuel Ortiz kfree(sdres); 13430cc4587SSamuel Ortiz return NULL; 13530cc4587SSamuel Ortiz } 13630cc4587SSamuel Ortiz 13730cc4587SSamuel Ortiz sdres->tid = tid; 13830cc4587SSamuel Ortiz sdres->sap = sap; 13930cc4587SSamuel Ortiz 14030cc4587SSamuel Ortiz INIT_HLIST_NODE(&sdres->node); 14130cc4587SSamuel Ortiz 14230cc4587SSamuel Ortiz return sdres; 14330cc4587SSamuel Ortiz } 14430cc4587SSamuel Ortiz 14530cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri, 14630cc4587SSamuel Ortiz size_t uri_len) 14730cc4587SSamuel Ortiz { 14830cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 14930cc4587SSamuel Ortiz 15030cc4587SSamuel Ortiz pr_debug("uri: %s, len: %zu\n", uri, uri_len); 15130cc4587SSamuel Ortiz 15230cc4587SSamuel Ortiz sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); 15330cc4587SSamuel Ortiz if (sdreq == NULL) 15430cc4587SSamuel Ortiz return NULL; 15530cc4587SSamuel Ortiz 15630cc4587SSamuel Ortiz sdreq->tlv_len = uri_len + 3; 15730cc4587SSamuel Ortiz 15830cc4587SSamuel Ortiz if (uri[uri_len - 1] == 0) 15930cc4587SSamuel Ortiz sdreq->tlv_len--; 16030cc4587SSamuel Ortiz 16130cc4587SSamuel Ortiz sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL); 16230cc4587SSamuel Ortiz if (sdreq->tlv == NULL) { 16330cc4587SSamuel Ortiz kfree(sdreq); 16430cc4587SSamuel Ortiz return NULL; 16530cc4587SSamuel Ortiz } 16630cc4587SSamuel Ortiz 16730cc4587SSamuel Ortiz sdreq->tlv[0] = LLCP_TLV_SDREQ; 16830cc4587SSamuel Ortiz sdreq->tlv[1] = sdreq->tlv_len - 2; 16930cc4587SSamuel Ortiz sdreq->tlv[2] = tid; 17030cc4587SSamuel Ortiz 17130cc4587SSamuel Ortiz sdreq->tid = tid; 17230cc4587SSamuel Ortiz sdreq->uri = sdreq->tlv + 3; 17330cc4587SSamuel Ortiz memcpy(sdreq->uri, uri, uri_len); 17430cc4587SSamuel Ortiz 17530cc4587SSamuel Ortiz sdreq->time = jiffies; 17630cc4587SSamuel Ortiz 17730cc4587SSamuel Ortiz INIT_HLIST_NODE(&sdreq->node); 17830cc4587SSamuel Ortiz 17930cc4587SSamuel Ortiz return sdreq; 18030cc4587SSamuel Ortiz } 18130cc4587SSamuel Ortiz 18230cc4587SSamuel Ortiz void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp) 18330cc4587SSamuel Ortiz { 18430cc4587SSamuel Ortiz kfree(sdp->tlv); 18530cc4587SSamuel Ortiz kfree(sdp); 18630cc4587SSamuel Ortiz } 18730cc4587SSamuel Ortiz 18830cc4587SSamuel Ortiz void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head) 18930cc4587SSamuel Ortiz { 19030cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 19130cc4587SSamuel Ortiz struct hlist_node *n; 19230cc4587SSamuel Ortiz 19330cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, head, node) { 19430cc4587SSamuel Ortiz hlist_del(&sdp->node); 19530cc4587SSamuel Ortiz 19630cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 19730cc4587SSamuel Ortiz } 19830cc4587SSamuel Ortiz } 19930cc4587SSamuel Ortiz 20030cc4587SSamuel Ortiz int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local, 20130cc4587SSamuel Ortiz u8 *tlv_array, u16 tlv_array_len) 20230cc4587SSamuel Ortiz { 20330cc4587SSamuel Ortiz u8 *tlv = tlv_array, type, length, offset = 0; 20430cc4587SSamuel Ortiz 20530cc4587SSamuel Ortiz pr_debug("TLV array length %d\n", tlv_array_len); 20630cc4587SSamuel Ortiz 20730cc4587SSamuel Ortiz if (local == NULL) 20830cc4587SSamuel Ortiz return -ENODEV; 20930cc4587SSamuel Ortiz 21030cc4587SSamuel Ortiz while (offset < tlv_array_len) { 21130cc4587SSamuel Ortiz type = tlv[0]; 21230cc4587SSamuel Ortiz length = tlv[1]; 21330cc4587SSamuel Ortiz 21430cc4587SSamuel Ortiz pr_debug("type 0x%x length %d\n", type, length); 21530cc4587SSamuel Ortiz 21630cc4587SSamuel Ortiz switch (type) { 21730cc4587SSamuel Ortiz case LLCP_TLV_VERSION: 21830cc4587SSamuel Ortiz local->remote_version = llcp_tlv_version(tlv); 21930cc4587SSamuel Ortiz break; 22030cc4587SSamuel Ortiz case LLCP_TLV_MIUX: 22130cc4587SSamuel Ortiz local->remote_miu = llcp_tlv_miux(tlv) + 128; 22230cc4587SSamuel Ortiz break; 22330cc4587SSamuel Ortiz case LLCP_TLV_WKS: 22430cc4587SSamuel Ortiz local->remote_wks = llcp_tlv_wks(tlv); 22530cc4587SSamuel Ortiz break; 22630cc4587SSamuel Ortiz case LLCP_TLV_LTO: 22730cc4587SSamuel Ortiz local->remote_lto = llcp_tlv_lto(tlv) * 10; 22830cc4587SSamuel Ortiz break; 22930cc4587SSamuel Ortiz case LLCP_TLV_OPT: 23030cc4587SSamuel Ortiz local->remote_opt = llcp_tlv_opt(tlv); 23130cc4587SSamuel Ortiz break; 23230cc4587SSamuel Ortiz default: 23330cc4587SSamuel Ortiz pr_err("Invalid gt tlv value 0x%x\n", type); 23430cc4587SSamuel Ortiz break; 23530cc4587SSamuel Ortiz } 23630cc4587SSamuel Ortiz 23730cc4587SSamuel Ortiz offset += length + 2; 23830cc4587SSamuel Ortiz tlv += length + 2; 23930cc4587SSamuel Ortiz } 24030cc4587SSamuel Ortiz 24130cc4587SSamuel Ortiz pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x\n", 24230cc4587SSamuel Ortiz local->remote_version, local->remote_miu, 24330cc4587SSamuel Ortiz local->remote_lto, local->remote_opt, 24430cc4587SSamuel Ortiz local->remote_wks); 24530cc4587SSamuel Ortiz 24630cc4587SSamuel Ortiz return 0; 24730cc4587SSamuel Ortiz } 24830cc4587SSamuel Ortiz 24930cc4587SSamuel Ortiz int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock, 25030cc4587SSamuel Ortiz u8 *tlv_array, u16 tlv_array_len) 25130cc4587SSamuel Ortiz { 25230cc4587SSamuel Ortiz u8 *tlv = tlv_array, type, length, offset = 0; 25330cc4587SSamuel Ortiz 25430cc4587SSamuel Ortiz pr_debug("TLV array length %d\n", tlv_array_len); 25530cc4587SSamuel Ortiz 25630cc4587SSamuel Ortiz if (sock == NULL) 25730cc4587SSamuel Ortiz return -ENOTCONN; 25830cc4587SSamuel Ortiz 25930cc4587SSamuel Ortiz while (offset < tlv_array_len) { 26030cc4587SSamuel Ortiz type = tlv[0]; 26130cc4587SSamuel Ortiz length = tlv[1]; 26230cc4587SSamuel Ortiz 26330cc4587SSamuel Ortiz pr_debug("type 0x%x length %d\n", type, length); 26430cc4587SSamuel Ortiz 26530cc4587SSamuel Ortiz switch (type) { 26630cc4587SSamuel Ortiz case LLCP_TLV_MIUX: 26730cc4587SSamuel Ortiz sock->remote_miu = llcp_tlv_miux(tlv) + 128; 26830cc4587SSamuel Ortiz break; 26930cc4587SSamuel Ortiz case LLCP_TLV_RW: 27030cc4587SSamuel Ortiz sock->remote_rw = llcp_tlv_rw(tlv); 27130cc4587SSamuel Ortiz break; 27230cc4587SSamuel Ortiz case LLCP_TLV_SN: 27330cc4587SSamuel Ortiz break; 27430cc4587SSamuel Ortiz default: 27530cc4587SSamuel Ortiz pr_err("Invalid gt tlv value 0x%x\n", type); 27630cc4587SSamuel Ortiz break; 27730cc4587SSamuel Ortiz } 27830cc4587SSamuel Ortiz 27930cc4587SSamuel Ortiz offset += length + 2; 28030cc4587SSamuel Ortiz tlv += length + 2; 28130cc4587SSamuel Ortiz } 28230cc4587SSamuel Ortiz 28330cc4587SSamuel Ortiz pr_debug("sock %p rw %d miu %d\n", sock, 28430cc4587SSamuel Ortiz sock->remote_rw, sock->remote_miu); 28530cc4587SSamuel Ortiz 28630cc4587SSamuel Ortiz return 0; 28730cc4587SSamuel Ortiz } 28830cc4587SSamuel Ortiz 28930cc4587SSamuel Ortiz static struct sk_buff *llcp_add_header(struct sk_buff *pdu, 29030cc4587SSamuel Ortiz u8 dsap, u8 ssap, u8 ptype) 29130cc4587SSamuel Ortiz { 29230cc4587SSamuel Ortiz u8 header[2]; 29330cc4587SSamuel Ortiz 29430cc4587SSamuel Ortiz pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap); 29530cc4587SSamuel Ortiz 29630cc4587SSamuel Ortiz header[0] = (u8)((dsap << 2) | (ptype >> 2)); 29730cc4587SSamuel Ortiz header[1] = (u8)((ptype << 6) | ssap); 29830cc4587SSamuel Ortiz 29930cc4587SSamuel Ortiz pr_debug("header 0x%x 0x%x\n", header[0], header[1]); 30030cc4587SSamuel Ortiz 30130cc4587SSamuel Ortiz memcpy(skb_put(pdu, LLCP_HEADER_SIZE), header, LLCP_HEADER_SIZE); 30230cc4587SSamuel Ortiz 30330cc4587SSamuel Ortiz return pdu; 30430cc4587SSamuel Ortiz } 30530cc4587SSamuel Ortiz 30630cc4587SSamuel Ortiz static struct sk_buff *llcp_add_tlv(struct sk_buff *pdu, u8 *tlv, 30730cc4587SSamuel Ortiz u8 tlv_length) 30830cc4587SSamuel Ortiz { 30930cc4587SSamuel Ortiz /* XXX Add an skb length check */ 31030cc4587SSamuel Ortiz 31130cc4587SSamuel Ortiz if (tlv == NULL) 31230cc4587SSamuel Ortiz return NULL; 31330cc4587SSamuel Ortiz 31430cc4587SSamuel Ortiz memcpy(skb_put(pdu, tlv_length), tlv, tlv_length); 31530cc4587SSamuel Ortiz 31630cc4587SSamuel Ortiz return pdu; 31730cc4587SSamuel Ortiz } 31830cc4587SSamuel Ortiz 31930cc4587SSamuel Ortiz static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock, 32030cc4587SSamuel Ortiz u8 cmd, u16 size) 32130cc4587SSamuel Ortiz { 32230cc4587SSamuel Ortiz struct sk_buff *skb; 32330cc4587SSamuel Ortiz int err; 32430cc4587SSamuel Ortiz 32530cc4587SSamuel Ortiz if (sock->ssap == 0) 32630cc4587SSamuel Ortiz return NULL; 32730cc4587SSamuel Ortiz 32830cc4587SSamuel Ortiz skb = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT, 32930cc4587SSamuel Ortiz size + LLCP_HEADER_SIZE, &err); 33030cc4587SSamuel Ortiz if (skb == NULL) { 33130cc4587SSamuel Ortiz pr_err("Could not allocate PDU\n"); 33230cc4587SSamuel Ortiz return NULL; 33330cc4587SSamuel Ortiz } 33430cc4587SSamuel Ortiz 33530cc4587SSamuel Ortiz skb = llcp_add_header(skb, sock->dsap, sock->ssap, cmd); 33630cc4587SSamuel Ortiz 33730cc4587SSamuel Ortiz return skb; 33830cc4587SSamuel Ortiz } 33930cc4587SSamuel Ortiz 34058e3dd15SThierry Escande int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock) 34130cc4587SSamuel Ortiz { 34230cc4587SSamuel Ortiz struct sk_buff *skb; 34330cc4587SSamuel Ortiz struct nfc_dev *dev; 34430cc4587SSamuel Ortiz struct nfc_llcp_local *local; 34530cc4587SSamuel Ortiz 34630cc4587SSamuel Ortiz pr_debug("Sending DISC\n"); 34730cc4587SSamuel Ortiz 34830cc4587SSamuel Ortiz local = sock->local; 34930cc4587SSamuel Ortiz if (local == NULL) 35030cc4587SSamuel Ortiz return -ENODEV; 35130cc4587SSamuel Ortiz 35230cc4587SSamuel Ortiz dev = sock->dev; 35330cc4587SSamuel Ortiz if (dev == NULL) 35430cc4587SSamuel Ortiz return -ENODEV; 35530cc4587SSamuel Ortiz 35630cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0); 35730cc4587SSamuel Ortiz if (skb == NULL) 35830cc4587SSamuel Ortiz return -ENOMEM; 35930cc4587SSamuel Ortiz 36030cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 36130cc4587SSamuel Ortiz 36230cc4587SSamuel Ortiz return 0; 36330cc4587SSamuel Ortiz } 36430cc4587SSamuel Ortiz 36530cc4587SSamuel Ortiz int nfc_llcp_send_symm(struct nfc_dev *dev) 36630cc4587SSamuel Ortiz { 36730cc4587SSamuel Ortiz struct sk_buff *skb; 36830cc4587SSamuel Ortiz struct nfc_llcp_local *local; 36930cc4587SSamuel Ortiz u16 size = 0; 37030cc4587SSamuel Ortiz 37130cc4587SSamuel Ortiz pr_debug("Sending SYMM\n"); 37230cc4587SSamuel Ortiz 37330cc4587SSamuel Ortiz local = nfc_llcp_find_local(dev); 37430cc4587SSamuel Ortiz if (local == NULL) 37530cc4587SSamuel Ortiz return -ENODEV; 37630cc4587SSamuel Ortiz 37730cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 37830cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 37930cc4587SSamuel Ortiz 38030cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 38130cc4587SSamuel Ortiz if (skb == NULL) 38230cc4587SSamuel Ortiz return -ENOMEM; 38330cc4587SSamuel Ortiz 38430cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 38530cc4587SSamuel Ortiz 38630cc4587SSamuel Ortiz skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); 38730cc4587SSamuel Ortiz 38830cc4587SSamuel Ortiz __net_timestamp(skb); 38930cc4587SSamuel Ortiz 39057be1f3fSHiren Tandel nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); 39130cc4587SSamuel Ortiz 39230cc4587SSamuel Ortiz return nfc_data_exchange(dev, local->target_idx, skb, 39330cc4587SSamuel Ortiz nfc_llcp_recv, local); 39430cc4587SSamuel Ortiz } 39530cc4587SSamuel Ortiz 39630cc4587SSamuel Ortiz int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) 39730cc4587SSamuel Ortiz { 39830cc4587SSamuel Ortiz struct nfc_llcp_local *local; 39930cc4587SSamuel Ortiz struct sk_buff *skb; 40030cc4587SSamuel Ortiz u8 *service_name_tlv = NULL, service_name_tlv_length; 40130cc4587SSamuel Ortiz u8 *miux_tlv = NULL, miux_tlv_length; 40230cc4587SSamuel Ortiz u8 *rw_tlv = NULL, rw_tlv_length, rw; 40330cc4587SSamuel Ortiz int err; 40430cc4587SSamuel Ortiz u16 size = 0, miux; 40530cc4587SSamuel Ortiz 40630cc4587SSamuel Ortiz pr_debug("Sending CONNECT\n"); 40730cc4587SSamuel Ortiz 40830cc4587SSamuel Ortiz local = sock->local; 40930cc4587SSamuel Ortiz if (local == NULL) 41030cc4587SSamuel Ortiz return -ENODEV; 41130cc4587SSamuel Ortiz 41230cc4587SSamuel Ortiz if (sock->service_name != NULL) { 41330cc4587SSamuel Ortiz service_name_tlv = nfc_llcp_build_tlv(LLCP_TLV_SN, 41430cc4587SSamuel Ortiz sock->service_name, 41530cc4587SSamuel Ortiz sock->service_name_len, 41630cc4587SSamuel Ortiz &service_name_tlv_length); 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); 42730cc4587SSamuel Ortiz size += miux_tlv_length; 42830cc4587SSamuel Ortiz 42930cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 43030cc4587SSamuel Ortiz size += rw_tlv_length; 43130cc4587SSamuel Ortiz 43230cc4587SSamuel Ortiz pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len); 43330cc4587SSamuel Ortiz 43430cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CONNECT, size); 43530cc4587SSamuel Ortiz if (skb == NULL) { 43630cc4587SSamuel Ortiz err = -ENOMEM; 43730cc4587SSamuel Ortiz goto error_tlv; 43830cc4587SSamuel Ortiz } 43930cc4587SSamuel Ortiz 44030cc4587SSamuel Ortiz if (service_name_tlv != NULL) 44130cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, service_name_tlv, 44230cc4587SSamuel Ortiz service_name_tlv_length); 44330cc4587SSamuel Ortiz 44430cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 44530cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 44630cc4587SSamuel Ortiz 44730cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 44830cc4587SSamuel Ortiz 44930cc4587SSamuel Ortiz return 0; 45030cc4587SSamuel Ortiz 45130cc4587SSamuel Ortiz error_tlv: 45230cc4587SSamuel Ortiz pr_err("error %d\n", err); 45330cc4587SSamuel Ortiz 45430cc4587SSamuel Ortiz kfree(service_name_tlv); 45530cc4587SSamuel Ortiz kfree(miux_tlv); 45630cc4587SSamuel Ortiz kfree(rw_tlv); 45730cc4587SSamuel Ortiz 45830cc4587SSamuel Ortiz return err; 45930cc4587SSamuel Ortiz } 46030cc4587SSamuel Ortiz 46130cc4587SSamuel Ortiz int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) 46230cc4587SSamuel Ortiz { 46330cc4587SSamuel Ortiz struct nfc_llcp_local *local; 46430cc4587SSamuel Ortiz struct sk_buff *skb; 46530cc4587SSamuel Ortiz u8 *miux_tlv = NULL, miux_tlv_length; 46630cc4587SSamuel Ortiz u8 *rw_tlv = NULL, rw_tlv_length, rw; 46730cc4587SSamuel Ortiz int err; 46830cc4587SSamuel Ortiz u16 size = 0, miux; 46930cc4587SSamuel Ortiz 47030cc4587SSamuel Ortiz pr_debug("Sending CC\n"); 47130cc4587SSamuel Ortiz 47230cc4587SSamuel Ortiz local = sock->local; 47330cc4587SSamuel Ortiz if (local == NULL) 47430cc4587SSamuel Ortiz return -ENODEV; 47530cc4587SSamuel Ortiz 47630cc4587SSamuel Ortiz /* If the socket parameters are not set, use the local ones */ 47730cc4587SSamuel Ortiz miux = be16_to_cpu(sock->miux) > LLCP_MAX_MIUX ? 47830cc4587SSamuel Ortiz local->miux : sock->miux; 47930cc4587SSamuel Ortiz rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw; 48030cc4587SSamuel Ortiz 48130cc4587SSamuel Ortiz miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, 48230cc4587SSamuel Ortiz &miux_tlv_length); 48330cc4587SSamuel Ortiz size += miux_tlv_length; 48430cc4587SSamuel Ortiz 48530cc4587SSamuel Ortiz rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); 48630cc4587SSamuel Ortiz size += rw_tlv_length; 48730cc4587SSamuel Ortiz 48830cc4587SSamuel Ortiz skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); 48930cc4587SSamuel Ortiz if (skb == NULL) { 49030cc4587SSamuel Ortiz err = -ENOMEM; 49130cc4587SSamuel Ortiz goto error_tlv; 49230cc4587SSamuel Ortiz } 49330cc4587SSamuel Ortiz 49430cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, miux_tlv, miux_tlv_length); 49530cc4587SSamuel Ortiz skb = llcp_add_tlv(skb, rw_tlv, rw_tlv_length); 49630cc4587SSamuel Ortiz 49730cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 49830cc4587SSamuel Ortiz 49930cc4587SSamuel Ortiz return 0; 50030cc4587SSamuel Ortiz 50130cc4587SSamuel Ortiz error_tlv: 50230cc4587SSamuel Ortiz pr_err("error %d\n", err); 50330cc4587SSamuel Ortiz 50430cc4587SSamuel Ortiz kfree(miux_tlv); 50530cc4587SSamuel Ortiz kfree(rw_tlv); 50630cc4587SSamuel Ortiz 50730cc4587SSamuel Ortiz return err; 50830cc4587SSamuel Ortiz } 50930cc4587SSamuel Ortiz 51030cc4587SSamuel Ortiz static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local, 51130cc4587SSamuel Ortiz size_t tlv_length) 51230cc4587SSamuel Ortiz { 51330cc4587SSamuel Ortiz struct sk_buff *skb; 51430cc4587SSamuel Ortiz struct nfc_dev *dev; 51530cc4587SSamuel Ortiz u16 size = 0; 51630cc4587SSamuel Ortiz 51730cc4587SSamuel Ortiz if (local == NULL) 51830cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 51930cc4587SSamuel Ortiz 52030cc4587SSamuel Ortiz dev = local->dev; 52130cc4587SSamuel Ortiz if (dev == NULL) 52230cc4587SSamuel Ortiz return ERR_PTR(-ENODEV); 52330cc4587SSamuel Ortiz 52430cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 52530cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 52630cc4587SSamuel Ortiz size += tlv_length; 52730cc4587SSamuel Ortiz 52830cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 52930cc4587SSamuel Ortiz if (skb == NULL) 53030cc4587SSamuel Ortiz return ERR_PTR(-ENOMEM); 53130cc4587SSamuel Ortiz 53230cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 53330cc4587SSamuel Ortiz 53430cc4587SSamuel Ortiz skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL); 53530cc4587SSamuel Ortiz 53630cc4587SSamuel Ortiz return skb; 53730cc4587SSamuel Ortiz } 53830cc4587SSamuel Ortiz 53930cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local, 54030cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 54130cc4587SSamuel Ortiz { 54230cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdp; 54330cc4587SSamuel Ortiz struct hlist_node *n; 54430cc4587SSamuel Ortiz struct sk_buff *skb; 54530cc4587SSamuel Ortiz 54630cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 54730cc4587SSamuel Ortiz if (IS_ERR(skb)) 54830cc4587SSamuel Ortiz return PTR_ERR(skb); 54930cc4587SSamuel Ortiz 55030cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdp, n, tlv_list, node) { 55130cc4587SSamuel Ortiz memcpy(skb_put(skb, sdp->tlv_len), sdp->tlv, sdp->tlv_len); 55230cc4587SSamuel Ortiz 55330cc4587SSamuel Ortiz hlist_del(&sdp->node); 55430cc4587SSamuel Ortiz 55530cc4587SSamuel Ortiz nfc_llcp_free_sdp_tlv(sdp); 55630cc4587SSamuel Ortiz } 55730cc4587SSamuel Ortiz 55830cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 55930cc4587SSamuel Ortiz 56030cc4587SSamuel Ortiz return 0; 56130cc4587SSamuel Ortiz } 56230cc4587SSamuel Ortiz 56330cc4587SSamuel Ortiz int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local, 56430cc4587SSamuel Ortiz struct hlist_head *tlv_list, size_t tlvs_len) 56530cc4587SSamuel Ortiz { 56630cc4587SSamuel Ortiz struct nfc_llcp_sdp_tlv *sdreq; 56730cc4587SSamuel Ortiz struct hlist_node *n; 56830cc4587SSamuel Ortiz struct sk_buff *skb; 56930cc4587SSamuel Ortiz 57030cc4587SSamuel Ortiz skb = nfc_llcp_allocate_snl(local, tlvs_len); 57130cc4587SSamuel Ortiz if (IS_ERR(skb)) 57230cc4587SSamuel Ortiz return PTR_ERR(skb); 57330cc4587SSamuel Ortiz 57430cc4587SSamuel Ortiz mutex_lock(&local->sdreq_lock); 57530cc4587SSamuel Ortiz 57630cc4587SSamuel Ortiz if (hlist_empty(&local->pending_sdreqs)) 57730cc4587SSamuel Ortiz mod_timer(&local->sdreq_timer, 57830cc4587SSamuel Ortiz jiffies + msecs_to_jiffies(3 * local->remote_lto)); 57930cc4587SSamuel Ortiz 58030cc4587SSamuel Ortiz hlist_for_each_entry_safe(sdreq, n, tlv_list, node) { 58130cc4587SSamuel Ortiz pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri); 58230cc4587SSamuel Ortiz 58330cc4587SSamuel Ortiz memcpy(skb_put(skb, sdreq->tlv_len), sdreq->tlv, 58430cc4587SSamuel Ortiz sdreq->tlv_len); 58530cc4587SSamuel Ortiz 58630cc4587SSamuel Ortiz hlist_del(&sdreq->node); 58730cc4587SSamuel Ortiz 58830cc4587SSamuel Ortiz hlist_add_head(&sdreq->node, &local->pending_sdreqs); 58930cc4587SSamuel Ortiz } 59030cc4587SSamuel Ortiz 59130cc4587SSamuel Ortiz mutex_unlock(&local->sdreq_lock); 59230cc4587SSamuel Ortiz 59330cc4587SSamuel Ortiz skb_queue_tail(&local->tx_queue, skb); 59430cc4587SSamuel Ortiz 59530cc4587SSamuel Ortiz return 0; 59630cc4587SSamuel Ortiz } 59730cc4587SSamuel Ortiz 59830cc4587SSamuel Ortiz int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason) 59930cc4587SSamuel Ortiz { 60030cc4587SSamuel Ortiz struct sk_buff *skb; 60130cc4587SSamuel Ortiz struct nfc_dev *dev; 60230cc4587SSamuel Ortiz u16 size = 1; /* Reason code */ 60330cc4587SSamuel Ortiz 60430cc4587SSamuel Ortiz pr_debug("Sending DM reason 0x%x\n", reason); 60530cc4587SSamuel Ortiz 60630cc4587SSamuel Ortiz if (local == NULL) 60730cc4587SSamuel Ortiz return -ENODEV; 60830cc4587SSamuel Ortiz 60930cc4587SSamuel Ortiz dev = local->dev; 61030cc4587SSamuel Ortiz if (dev == NULL) 61130cc4587SSamuel Ortiz return -ENODEV; 61230cc4587SSamuel Ortiz 61330cc4587SSamuel Ortiz size += LLCP_HEADER_SIZE; 61430cc4587SSamuel Ortiz size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; 61530cc4587SSamuel Ortiz 61630cc4587SSamuel Ortiz skb = alloc_skb(size, GFP_KERNEL); 61730cc4587SSamuel Ortiz if (skb == NULL) 61830cc4587SSamuel Ortiz return -ENOMEM; 61930cc4587SSamuel Ortiz 62030cc4587SSamuel Ortiz skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE); 62130cc4587SSamuel Ortiz 62230cc4587SSamuel Ortiz skb = llcp_add_header(skb, dsap, ssap, LLCP_PDU_DM); 62330cc4587SSamuel Ortiz 62430cc4587SSamuel Ortiz memcpy(skb_put(skb, 1), &reason, 1); 62530cc4587SSamuel Ortiz 62630cc4587SSamuel Ortiz skb_queue_head(&local->tx_queue, skb); 62730cc4587SSamuel Ortiz 62830cc4587SSamuel Ortiz return 0; 62930cc4587SSamuel Ortiz } 63030cc4587SSamuel Ortiz 63130cc4587SSamuel Ortiz int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, 63230cc4587SSamuel Ortiz struct msghdr *msg, size_t len) 63330cc4587SSamuel Ortiz { 63430cc4587SSamuel Ortiz struct sk_buff *pdu; 63530cc4587SSamuel Ortiz struct sock *sk = &sock->sk; 63630cc4587SSamuel Ortiz struct nfc_llcp_local *local; 63730cc4587SSamuel Ortiz size_t frag_len = 0, remaining_len; 63830cc4587SSamuel Ortiz u8 *msg_data, *msg_ptr; 63930cc4587SSamuel Ortiz u16 remote_miu; 64030cc4587SSamuel Ortiz 64130cc4587SSamuel Ortiz pr_debug("Send I frame len %zd\n", len); 64230cc4587SSamuel Ortiz 64330cc4587SSamuel Ortiz local = sock->local; 64430cc4587SSamuel Ortiz if (local == NULL) 64530cc4587SSamuel Ortiz return -ENODEV; 64630cc4587SSamuel Ortiz 64730cc4587SSamuel Ortiz /* Remote is ready but has not acknowledged our frames */ 64830cc4587SSamuel Ortiz if((sock->remote_ready && 64930cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw && 65030cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 65130cc4587SSamuel Ortiz pr_err("Pending queue is full %d frames\n", 65230cc4587SSamuel Ortiz skb_queue_len(&sock->tx_pending_queue)); 65330cc4587SSamuel Ortiz return -ENOBUFS; 65430cc4587SSamuel Ortiz } 65530cc4587SSamuel Ortiz 65630cc4587SSamuel Ortiz /* Remote is not ready and we've been queueing enough frames */ 65730cc4587SSamuel Ortiz if ((!sock->remote_ready && 65830cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) { 65930cc4587SSamuel Ortiz pr_err("Tx queue is full %d frames\n", 66030cc4587SSamuel Ortiz skb_queue_len(&sock->tx_queue)); 66130cc4587SSamuel Ortiz return -ENOBUFS; 66230cc4587SSamuel Ortiz } 66330cc4587SSamuel Ortiz 66430cc4587SSamuel Ortiz msg_data = kzalloc(len, GFP_KERNEL); 66530cc4587SSamuel Ortiz if (msg_data == NULL) 66630cc4587SSamuel Ortiz return -ENOMEM; 66730cc4587SSamuel Ortiz 668*6ce8e9ceSAl Viro if (memcpy_from_msg(msg_data, msg, len)) { 66930cc4587SSamuel Ortiz kfree(msg_data); 67030cc4587SSamuel Ortiz return -EFAULT; 67130cc4587SSamuel Ortiz } 67230cc4587SSamuel Ortiz 67330cc4587SSamuel Ortiz remaining_len = len; 67430cc4587SSamuel Ortiz msg_ptr = msg_data; 67530cc4587SSamuel Ortiz 67630cc4587SSamuel Ortiz do { 67730cc4587SSamuel Ortiz remote_miu = sock->remote_miu > LLCP_MAX_MIU ? 67811bfb1c4SSzymon Janc LLCP_DEFAULT_MIU : sock->remote_miu; 67930cc4587SSamuel Ortiz 68030cc4587SSamuel Ortiz frag_len = min_t(size_t, remote_miu, remaining_len); 68130cc4587SSamuel Ortiz 68230cc4587SSamuel Ortiz pr_debug("Fragment %zd bytes remaining %zd", 68330cc4587SSamuel Ortiz frag_len, remaining_len); 68430cc4587SSamuel Ortiz 68530cc4587SSamuel Ortiz pdu = llcp_allocate_pdu(sock, LLCP_PDU_I, 68630cc4587SSamuel Ortiz frag_len + LLCP_SEQUENCE_SIZE); 68743d53c29SSzymon Janc if (pdu == NULL) { 68843d53c29SSzymon Janc kfree(msg_data); 68930cc4587SSamuel Ortiz return -ENOMEM; 69043d53c29SSzymon Janc } 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 734*6ce8e9ceSAl Viro if (memcpy_from_msg(msg_data, msg, 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