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 { 1434aeee687SChristophe 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 1484aeee687SChristophe Ricard if (status == NCI_STATUS_OK) { 1498939e47fSIlan Elias atomic_set(&ndev->state, NCI_DISCOVERY); 1506a2968aaSIlan Elias 15112bdf27dSChristophe Ricard conn_info = ndev->rf_conn_info; 1524aeee687SChristophe Ricard if (!conn_info) { 1534aeee687SChristophe Ricard conn_info = devm_kzalloc(&ndev->nfc_dev->dev, 1544aeee687SChristophe Ricard sizeof(struct nci_conn_info), 1554aeee687SChristophe Ricard GFP_KERNEL); 1564aeee687SChristophe Ricard if (!conn_info) { 1574aeee687SChristophe Ricard status = NCI_STATUS_REJECTED; 1584aeee687SChristophe Ricard goto exit; 1594aeee687SChristophe Ricard } 1604aeee687SChristophe Ricard conn_info->conn_id = NCI_STATIC_RF_CONN_ID; 1614aeee687SChristophe Ricard INIT_LIST_HEAD(&conn_info->list); 1624aeee687SChristophe Ricard list_add(&conn_info->list, &ndev->conn_info_list); 16312bdf27dSChristophe Ricard ndev->rf_conn_info = conn_info; 1644aeee687SChristophe Ricard } 1654aeee687SChristophe Ricard } 1664aeee687SChristophe Ricard 1674aeee687SChristophe 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 199af9c8aa6SChristophe Ricard static void nci_nfcee_discover_rsp_packet(struct nci_dev *ndev, 200af9c8aa6SChristophe Ricard struct sk_buff *skb) 201af9c8aa6SChristophe Ricard { 202af9c8aa6SChristophe Ricard struct nci_nfcee_discover_rsp *discover_rsp; 203af9c8aa6SChristophe Ricard 204af9c8aa6SChristophe Ricard if (skb->len != 2) { 205af9c8aa6SChristophe Ricard nci_req_complete(ndev, NCI_STATUS_NFCEE_PROTOCOL_ERROR); 206af9c8aa6SChristophe Ricard return; 207af9c8aa6SChristophe Ricard } 208af9c8aa6SChristophe Ricard 209af9c8aa6SChristophe Ricard discover_rsp = (struct nci_nfcee_discover_rsp *)skb->data; 210af9c8aa6SChristophe Ricard 211af9c8aa6SChristophe Ricard if (discover_rsp->status != NCI_STATUS_OK || 212af9c8aa6SChristophe Ricard discover_rsp->num_nfcee == 0) 213af9c8aa6SChristophe Ricard nci_req_complete(ndev, discover_rsp->status); 214af9c8aa6SChristophe Ricard } 215af9c8aa6SChristophe Ricard 216f7f793f3SChristophe Ricard static void nci_nfcee_mode_set_rsp_packet(struct nci_dev *ndev, 217f7f793f3SChristophe Ricard struct sk_buff *skb) 218f7f793f3SChristophe Ricard { 219f7f793f3SChristophe Ricard __u8 status = skb->data[0]; 220f7f793f3SChristophe Ricard 221f7f793f3SChristophe Ricard pr_debug("status 0x%x\n", status); 222f7f793f3SChristophe Ricard nci_req_complete(ndev, status); 223f7f793f3SChristophe Ricard } 224f7f793f3SChristophe Ricard 225736bb957SChristophe Ricard static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev, 226736bb957SChristophe Ricard struct sk_buff *skb) 227736bb957SChristophe Ricard { 228736bb957SChristophe Ricard __u8 status = skb->data[0]; 229736bb957SChristophe Ricard struct nci_conn_info *conn_info; 230736bb957SChristophe Ricard struct nci_core_conn_create_rsp *rsp; 231736bb957SChristophe Ricard 232736bb957SChristophe Ricard pr_debug("status 0x%x\n", status); 233736bb957SChristophe Ricard 234736bb957SChristophe Ricard if (status == NCI_STATUS_OK) { 235736bb957SChristophe Ricard rsp = (struct nci_core_conn_create_rsp *)skb->data; 236736bb957SChristophe Ricard list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 237736bb957SChristophe Ricard if (conn_info->id == ndev->cur_id) 238736bb957SChristophe Ricard break; 239736bb957SChristophe Ricard } 240736bb957SChristophe Ricard 241736bb957SChristophe Ricard if (!conn_info || conn_info->id != ndev->cur_id) { 242736bb957SChristophe Ricard status = NCI_STATUS_REJECTED; 243736bb957SChristophe Ricard goto exit; 244736bb957SChristophe Ricard } 245736bb957SChristophe Ricard 246736bb957SChristophe Ricard conn_info->conn_id = rsp->conn_id; 247736bb957SChristophe Ricard conn_info->max_pkt_payload_len = rsp->max_ctrl_pkt_payload_len; 248*3ba5c846SChristophe Ricard atomic_set(&conn_info->credits_cnt, rsp->credits_cnt); 249736bb957SChristophe Ricard } 250736bb957SChristophe Ricard 251736bb957SChristophe Ricard exit: 252736bb957SChristophe Ricard nci_req_complete(ndev, status); 253736bb957SChristophe Ricard } 254736bb957SChristophe Ricard 255736bb957SChristophe Ricard static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev, 256736bb957SChristophe Ricard struct sk_buff *skb) 257736bb957SChristophe Ricard { 258736bb957SChristophe Ricard __u8 status = skb->data[0]; 259736bb957SChristophe Ricard 260736bb957SChristophe Ricard pr_debug("status 0x%x\n", status); 261736bb957SChristophe Ricard nci_req_complete(ndev, status); 262736bb957SChristophe Ricard } 263736bb957SChristophe Ricard 2646a2968aaSIlan Elias void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 2656a2968aaSIlan Elias { 2666a2968aaSIlan Elias __u16 rsp_opcode = nci_opcode(skb->data); 2676a2968aaSIlan Elias 2686a2968aaSIlan Elias /* we got a rsp, stop the cmd timer */ 2696a2968aaSIlan Elias del_timer(&ndev->cmd_timer); 2706a2968aaSIlan Elias 27120c239c1SJoe Perches pr_debug("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 2726a2968aaSIlan Elias nci_pbf(skb->data), 2736a2968aaSIlan Elias nci_opcode_gid(rsp_opcode), 2746a2968aaSIlan Elias nci_opcode_oid(rsp_opcode), 2756a2968aaSIlan Elias nci_plen(skb->data)); 2766a2968aaSIlan Elias 2776a2968aaSIlan Elias /* strip the nci control header */ 2786a2968aaSIlan Elias skb_pull(skb, NCI_CTRL_HDR_SIZE); 2796a2968aaSIlan Elias 2806a2968aaSIlan Elias switch (rsp_opcode) { 2816a2968aaSIlan Elias case NCI_OP_CORE_RESET_RSP: 2826a2968aaSIlan Elias nci_core_reset_rsp_packet(ndev, skb); 2836a2968aaSIlan Elias break; 2846a2968aaSIlan Elias 2856a2968aaSIlan Elias case NCI_OP_CORE_INIT_RSP: 2866a2968aaSIlan Elias nci_core_init_rsp_packet(ndev, skb); 2876a2968aaSIlan Elias break; 2886a2968aaSIlan Elias 2897e035230SIlan Elias case NCI_OP_CORE_SET_CONFIG_RSP: 2907e035230SIlan Elias nci_core_set_config_rsp_packet(ndev, skb); 2917e035230SIlan Elias break; 2927e035230SIlan Elias 293736bb957SChristophe Ricard case NCI_OP_CORE_CONN_CREATE_RSP: 294736bb957SChristophe Ricard nci_core_conn_create_rsp_packet(ndev, skb); 295736bb957SChristophe Ricard break; 296736bb957SChristophe Ricard 297736bb957SChristophe Ricard case NCI_OP_CORE_CONN_CLOSE_RSP: 298736bb957SChristophe Ricard nci_core_conn_close_rsp_packet(ndev, skb); 299736bb957SChristophe Ricard break; 300736bb957SChristophe Ricard 3016a2968aaSIlan Elias case NCI_OP_RF_DISCOVER_MAP_RSP: 3026a2968aaSIlan Elias nci_rf_disc_map_rsp_packet(ndev, skb); 3036a2968aaSIlan Elias break; 3046a2968aaSIlan Elias 3056a2968aaSIlan Elias case NCI_OP_RF_DISCOVER_RSP: 3066a2968aaSIlan Elias nci_rf_disc_rsp_packet(ndev, skb); 3076a2968aaSIlan Elias break; 3086a2968aaSIlan Elias 309019c4fbaSIlan Elias case NCI_OP_RF_DISCOVER_SELECT_RSP: 310019c4fbaSIlan Elias nci_rf_disc_select_rsp_packet(ndev, skb); 311019c4fbaSIlan Elias break; 312019c4fbaSIlan Elias 3136a2968aaSIlan Elias case NCI_OP_RF_DEACTIVATE_RSP: 3146a2968aaSIlan Elias nci_rf_deactivate_rsp_packet(ndev, skb); 3156a2968aaSIlan Elias break; 3166a2968aaSIlan Elias 317af9c8aa6SChristophe Ricard case NCI_OP_NFCEE_DISCOVER_RSP: 318af9c8aa6SChristophe Ricard nci_nfcee_discover_rsp_packet(ndev, skb); 319af9c8aa6SChristophe Ricard break; 320af9c8aa6SChristophe Ricard 321f7f793f3SChristophe Ricard case NCI_OP_NFCEE_MODE_SET_RSP: 322f7f793f3SChristophe Ricard nci_nfcee_mode_set_rsp_packet(ndev, skb); 323f7f793f3SChristophe Ricard break; 324f7f793f3SChristophe Ricard 3256a2968aaSIlan Elias default: 326ed1e0ad8SJoe Perches pr_err("unknown rsp opcode 0x%x\n", rsp_opcode); 3276a2968aaSIlan Elias break; 3286a2968aaSIlan Elias } 3296a2968aaSIlan Elias 3306a2968aaSIlan Elias kfree_skb(skb); 3316a2968aaSIlan Elias 3326a2968aaSIlan Elias /* trigger the next cmd */ 3336a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 3346a2968aaSIlan Elias if (!skb_queue_empty(&ndev->cmd_q)) 3356a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 3366a2968aaSIlan Elias } 337