16a2968aaSIlan Elias /* 26a2968aaSIlan Elias * The NFC Controller Interface is the communication protocol between an 36a2968aaSIlan Elias * NFC Controller (NFCC) and a Device Host (DH). 46a2968aaSIlan Elias * 56a2968aaSIlan Elias * Copyright (C) 2011 Texas Instruments, Inc. 66a2968aaSIlan Elias * 76a2968aaSIlan Elias * Written by Ilan Elias <ilane@ti.com> 86a2968aaSIlan Elias * 96a2968aaSIlan Elias * Acknowledgements: 106a2968aaSIlan Elias * This file is based on hci_event.c, which was written 116a2968aaSIlan Elias * by Maxim Krasnyansky. 126a2968aaSIlan Elias * 136a2968aaSIlan Elias * This program is free software; you can redistribute it and/or modify 146a2968aaSIlan Elias * it under the terms of the GNU General Public License version 2 156a2968aaSIlan Elias * as published by the Free Software Foundation 166a2968aaSIlan Elias * 176a2968aaSIlan Elias * This program is distributed in the hope that it will be useful, 186a2968aaSIlan Elias * but WITHOUT ANY WARRANTY; without even the implied warranty of 196a2968aaSIlan Elias * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 206a2968aaSIlan Elias * GNU General Public License for more details. 216a2968aaSIlan Elias * 226a2968aaSIlan Elias * You should have received a copy of the GNU General Public License 2398b32decSJeff Kirsher * along with this program; if not, see <http://www.gnu.org/licenses/>. 246a2968aaSIlan Elias * 256a2968aaSIlan Elias */ 266a2968aaSIlan Elias 2752858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ 28ed1e0ad8SJoe Perches 296a2968aaSIlan Elias #include <linux/types.h> 306a2968aaSIlan Elias #include <linux/interrupt.h> 316a2968aaSIlan Elias #include <linux/bitops.h> 326a2968aaSIlan Elias #include <linux/skbuff.h> 336a2968aaSIlan Elias 346a2968aaSIlan Elias #include "../nfc.h" 356a2968aaSIlan Elias #include <net/nfc/nci.h> 366a2968aaSIlan Elias #include <net/nfc/nci_core.h> 376a2968aaSIlan Elias 386a2968aaSIlan Elias /* Handle NCI Response packets */ 396a2968aaSIlan Elias 406a2968aaSIlan Elias static void nci_core_reset_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 416a2968aaSIlan Elias { 426a2968aaSIlan Elias struct nci_core_reset_rsp *rsp = (void *) skb->data; 436a2968aaSIlan Elias 4424bf3304SJoe Perches pr_debug("status 0x%x\n", rsp->status); 456a2968aaSIlan Elias 46e8c0dacdSIlan Elias if (rsp->status == NCI_STATUS_OK) { 476a2968aaSIlan Elias ndev->nci_ver = rsp->nci_ver; 4820c239c1SJoe Perches pr_debug("nci_ver 0x%x, config_status 0x%x\n", 49e8c0dacdSIlan Elias rsp->nci_ver, rsp->config_status); 50e8c0dacdSIlan Elias } 516a2968aaSIlan Elias 526a2968aaSIlan Elias nci_req_complete(ndev, rsp->status); 536a2968aaSIlan Elias } 546a2968aaSIlan Elias 556a2968aaSIlan Elias static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 566a2968aaSIlan Elias { 576a2968aaSIlan Elias struct nci_core_init_rsp_1 *rsp_1 = (void *) skb->data; 586a2968aaSIlan Elias struct nci_core_init_rsp_2 *rsp_2; 596a2968aaSIlan Elias 6024bf3304SJoe Perches pr_debug("status 0x%x\n", rsp_1->status); 616a2968aaSIlan Elias 626a2968aaSIlan Elias if (rsp_1->status != NCI_STATUS_OK) 63e8c0dacdSIlan Elias goto exit; 646a2968aaSIlan Elias 656a2968aaSIlan Elias ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features); 666a2968aaSIlan Elias ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces; 676a2968aaSIlan Elias 686a2968aaSIlan Elias if (ndev->num_supported_rf_interfaces > 696a2968aaSIlan Elias NCI_MAX_SUPPORTED_RF_INTERFACES) { 706a2968aaSIlan Elias ndev->num_supported_rf_interfaces = 716a2968aaSIlan Elias NCI_MAX_SUPPORTED_RF_INTERFACES; 726a2968aaSIlan Elias } 736a2968aaSIlan Elias 746a2968aaSIlan Elias memcpy(ndev->supported_rf_interfaces, 756a2968aaSIlan Elias rsp_1->supported_rf_interfaces, 766a2968aaSIlan Elias ndev->num_supported_rf_interfaces); 776a2968aaSIlan Elias 78e8c0dacdSIlan Elias rsp_2 = (void *) (skb->data + 6 + rsp_1->num_supported_rf_interfaces); 796a2968aaSIlan Elias 80eb9bc6e9SSamuel Ortiz ndev->max_logical_connections = rsp_2->max_logical_connections; 816a2968aaSIlan Elias ndev->max_routing_table_size = 826a2968aaSIlan Elias __le16_to_cpu(rsp_2->max_routing_table_size); 83e8c0dacdSIlan Elias ndev->max_ctrl_pkt_payload_len = 84e8c0dacdSIlan Elias rsp_2->max_ctrl_pkt_payload_len; 85e8c0dacdSIlan Elias ndev->max_size_for_large_params = 86e8c0dacdSIlan Elias __le16_to_cpu(rsp_2->max_size_for_large_params); 87e8c0dacdSIlan Elias ndev->manufact_id = 88e8c0dacdSIlan Elias rsp_2->manufact_id; 89e8c0dacdSIlan Elias ndev->manufact_specific_info = 90e8c0dacdSIlan Elias __le32_to_cpu(rsp_2->manufact_specific_info); 91e8c0dacdSIlan Elias 9220c239c1SJoe Perches pr_debug("nfcc_features 0x%x\n", 936a2968aaSIlan Elias ndev->nfcc_features); 9420c239c1SJoe Perches pr_debug("num_supported_rf_interfaces %d\n", 956a2968aaSIlan Elias ndev->num_supported_rf_interfaces); 9620c239c1SJoe Perches pr_debug("supported_rf_interfaces[0] 0x%x\n", 976a2968aaSIlan Elias ndev->supported_rf_interfaces[0]); 9820c239c1SJoe Perches pr_debug("supported_rf_interfaces[1] 0x%x\n", 996a2968aaSIlan Elias ndev->supported_rf_interfaces[1]); 10020c239c1SJoe Perches pr_debug("supported_rf_interfaces[2] 0x%x\n", 1016a2968aaSIlan Elias ndev->supported_rf_interfaces[2]); 10220c239c1SJoe Perches pr_debug("supported_rf_interfaces[3] 0x%x\n", 1036a2968aaSIlan Elias ndev->supported_rf_interfaces[3]); 10420c239c1SJoe Perches pr_debug("max_logical_connections %d\n", 1056a2968aaSIlan Elias ndev->max_logical_connections); 10620c239c1SJoe Perches pr_debug("max_routing_table_size %d\n", 1076a2968aaSIlan Elias ndev->max_routing_table_size); 10820c239c1SJoe Perches pr_debug("max_ctrl_pkt_payload_len %d\n", 109e8c0dacdSIlan Elias ndev->max_ctrl_pkt_payload_len); 11020c239c1SJoe Perches pr_debug("max_size_for_large_params %d\n", 111e8c0dacdSIlan Elias ndev->max_size_for_large_params); 11220c239c1SJoe Perches pr_debug("manufact_id 0x%x\n", 113e8c0dacdSIlan Elias ndev->manufact_id); 11420c239c1SJoe Perches pr_debug("manufact_specific_info 0x%x\n", 115e8c0dacdSIlan Elias ndev->manufact_specific_info); 1166a2968aaSIlan Elias 117e8c0dacdSIlan Elias exit: 1186a2968aaSIlan Elias nci_req_complete(ndev, rsp_1->status); 1196a2968aaSIlan Elias } 1206a2968aaSIlan Elias 1217e035230SIlan Elias static void nci_core_set_config_rsp_packet(struct nci_dev *ndev, 1227e035230SIlan Elias struct sk_buff *skb) 1237e035230SIlan Elias { 1247e035230SIlan Elias struct nci_core_set_config_rsp *rsp = (void *) skb->data; 1257e035230SIlan Elias 1267e035230SIlan Elias pr_debug("status 0x%x\n", rsp->status); 1277e035230SIlan Elias 1287e035230SIlan Elias nci_req_complete(ndev, rsp->status); 1297e035230SIlan Elias } 1307e035230SIlan Elias 1316a2968aaSIlan Elias static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, 1326a2968aaSIlan Elias struct sk_buff *skb) 1336a2968aaSIlan Elias { 1346a2968aaSIlan Elias __u8 status = skb->data[0]; 1356a2968aaSIlan Elias 13624bf3304SJoe Perches pr_debug("status 0x%x\n", status); 1376a2968aaSIlan Elias 1386a2968aaSIlan Elias nci_req_complete(ndev, status); 1396a2968aaSIlan Elias } 1406a2968aaSIlan Elias 1416a2968aaSIlan Elias static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 1426a2968aaSIlan Elias { 143*4aeee687SChristophe Ricard struct nci_conn_info *conn_info; 1446a2968aaSIlan Elias __u8 status = skb->data[0]; 1456a2968aaSIlan Elias 14624bf3304SJoe Perches pr_debug("status 0x%x\n", status); 1476a2968aaSIlan Elias 148*4aeee687SChristophe Ricard if (status == NCI_STATUS_OK) { 1498939e47fSIlan Elias atomic_set(&ndev->state, NCI_DISCOVERY); 1506a2968aaSIlan Elias 151*4aeee687SChristophe Ricard conn_info = nci_get_conn_info_by_conn_id(ndev, 152*4aeee687SChristophe Ricard NCI_STATIC_RF_CONN_ID); 153*4aeee687SChristophe Ricard if (!conn_info) { 154*4aeee687SChristophe Ricard conn_info = devm_kzalloc(&ndev->nfc_dev->dev, 155*4aeee687SChristophe Ricard sizeof(struct nci_conn_info), 156*4aeee687SChristophe Ricard GFP_KERNEL); 157*4aeee687SChristophe Ricard if (!conn_info) { 158*4aeee687SChristophe Ricard status = NCI_STATUS_REJECTED; 159*4aeee687SChristophe Ricard goto exit; 160*4aeee687SChristophe Ricard } 161*4aeee687SChristophe Ricard conn_info->conn_id = NCI_STATIC_RF_CONN_ID; 162*4aeee687SChristophe Ricard INIT_LIST_HEAD(&conn_info->list); 163*4aeee687SChristophe Ricard list_add(&conn_info->list, &ndev->conn_info_list); 164*4aeee687SChristophe Ricard } 165*4aeee687SChristophe Ricard } 166*4aeee687SChristophe Ricard 167*4aeee687SChristophe Ricard exit: 1686a2968aaSIlan Elias nci_req_complete(ndev, status); 1696a2968aaSIlan Elias } 1706a2968aaSIlan Elias 171019c4fbaSIlan Elias static void nci_rf_disc_select_rsp_packet(struct nci_dev *ndev, 172019c4fbaSIlan Elias struct sk_buff *skb) 173019c4fbaSIlan Elias { 174019c4fbaSIlan Elias __u8 status = skb->data[0]; 175019c4fbaSIlan Elias 176019c4fbaSIlan Elias pr_debug("status 0x%x\n", status); 177019c4fbaSIlan Elias 178019c4fbaSIlan Elias /* Complete the request on intf_activated_ntf or generic_error_ntf */ 179019c4fbaSIlan Elias if (status != NCI_STATUS_OK) 180019c4fbaSIlan Elias nci_req_complete(ndev, status); 181019c4fbaSIlan Elias } 182019c4fbaSIlan Elias 1836a2968aaSIlan Elias static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, 1846a2968aaSIlan Elias struct sk_buff *skb) 1856a2968aaSIlan Elias { 1866a2968aaSIlan Elias __u8 status = skb->data[0]; 1876a2968aaSIlan Elias 18824bf3304SJoe Perches pr_debug("status 0x%x\n", status); 1896a2968aaSIlan Elias 190bd7e01bcSIlan Elias /* If target was active, complete the request only in deactivate_ntf */ 191bd7e01bcSIlan Elias if ((status != NCI_STATUS_OK) || 1928939e47fSIlan Elias (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { 193019c4fbaSIlan Elias nci_clear_target_list(ndev); 1948939e47fSIlan Elias atomic_set(&ndev->state, NCI_IDLE); 1956a2968aaSIlan Elias nci_req_complete(ndev, status); 1966a2968aaSIlan Elias } 1978939e47fSIlan Elias } 1986a2968aaSIlan Elias 1996a2968aaSIlan Elias void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 2006a2968aaSIlan Elias { 2016a2968aaSIlan Elias __u16 rsp_opcode = nci_opcode(skb->data); 2026a2968aaSIlan Elias 2036a2968aaSIlan Elias /* we got a rsp, stop the cmd timer */ 2046a2968aaSIlan Elias del_timer(&ndev->cmd_timer); 2056a2968aaSIlan Elias 20620c239c1SJoe Perches pr_debug("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 2076a2968aaSIlan Elias nci_pbf(skb->data), 2086a2968aaSIlan Elias nci_opcode_gid(rsp_opcode), 2096a2968aaSIlan Elias nci_opcode_oid(rsp_opcode), 2106a2968aaSIlan Elias nci_plen(skb->data)); 2116a2968aaSIlan Elias 2126a2968aaSIlan Elias /* strip the nci control header */ 2136a2968aaSIlan Elias skb_pull(skb, NCI_CTRL_HDR_SIZE); 2146a2968aaSIlan Elias 2156a2968aaSIlan Elias switch (rsp_opcode) { 2166a2968aaSIlan Elias case NCI_OP_CORE_RESET_RSP: 2176a2968aaSIlan Elias nci_core_reset_rsp_packet(ndev, skb); 2186a2968aaSIlan Elias break; 2196a2968aaSIlan Elias 2206a2968aaSIlan Elias case NCI_OP_CORE_INIT_RSP: 2216a2968aaSIlan Elias nci_core_init_rsp_packet(ndev, skb); 2226a2968aaSIlan Elias break; 2236a2968aaSIlan Elias 2247e035230SIlan Elias case NCI_OP_CORE_SET_CONFIG_RSP: 2257e035230SIlan Elias nci_core_set_config_rsp_packet(ndev, skb); 2267e035230SIlan Elias break; 2277e035230SIlan Elias 2286a2968aaSIlan Elias case NCI_OP_RF_DISCOVER_MAP_RSP: 2296a2968aaSIlan Elias nci_rf_disc_map_rsp_packet(ndev, skb); 2306a2968aaSIlan Elias break; 2316a2968aaSIlan Elias 2326a2968aaSIlan Elias case NCI_OP_RF_DISCOVER_RSP: 2336a2968aaSIlan Elias nci_rf_disc_rsp_packet(ndev, skb); 2346a2968aaSIlan Elias break; 2356a2968aaSIlan Elias 236019c4fbaSIlan Elias case NCI_OP_RF_DISCOVER_SELECT_RSP: 237019c4fbaSIlan Elias nci_rf_disc_select_rsp_packet(ndev, skb); 238019c4fbaSIlan Elias break; 239019c4fbaSIlan Elias 2406a2968aaSIlan Elias case NCI_OP_RF_DEACTIVATE_RSP: 2416a2968aaSIlan Elias nci_rf_deactivate_rsp_packet(ndev, skb); 2426a2968aaSIlan Elias break; 2436a2968aaSIlan Elias 2446a2968aaSIlan Elias default: 245ed1e0ad8SJoe Perches pr_err("unknown rsp opcode 0x%x\n", rsp_opcode); 2466a2968aaSIlan Elias break; 2476a2968aaSIlan Elias } 2486a2968aaSIlan Elias 2496a2968aaSIlan Elias kfree_skb(skb); 2506a2968aaSIlan Elias 2516a2968aaSIlan Elias /* trigger the next cmd */ 2526a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 2536a2968aaSIlan Elias if (!skb_queue_empty(&ndev->cmd_q)) 2546a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 2556a2968aaSIlan Elias } 256