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 236a2968aaSIlan Elias * along with this program; if not, write to the Free Software 246a2968aaSIlan Elias * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 256a2968aaSIlan Elias * 266a2968aaSIlan Elias */ 276a2968aaSIlan Elias 28*ed1e0ad8SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 29*ed1e0ad8SJoe Perches 306a2968aaSIlan Elias #include <linux/types.h> 316a2968aaSIlan Elias #include <linux/interrupt.h> 326a2968aaSIlan Elias #include <linux/bitops.h> 336a2968aaSIlan Elias #include <linux/skbuff.h> 346a2968aaSIlan Elias 356a2968aaSIlan Elias #include "../nfc.h" 366a2968aaSIlan Elias #include <net/nfc/nci.h> 376a2968aaSIlan Elias #include <net/nfc/nci_core.h> 386a2968aaSIlan Elias 396a2968aaSIlan Elias /* Handle NCI Response packets */ 406a2968aaSIlan Elias 416a2968aaSIlan Elias static void nci_core_reset_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 426a2968aaSIlan Elias { 436a2968aaSIlan Elias struct nci_core_reset_rsp *rsp = (void *) skb->data; 446a2968aaSIlan Elias 456a2968aaSIlan Elias nfc_dbg("entry, status 0x%x", rsp->status); 466a2968aaSIlan Elias 47e8c0dacdSIlan Elias if (rsp->status == NCI_STATUS_OK) { 486a2968aaSIlan Elias ndev->nci_ver = rsp->nci_ver; 49e8c0dacdSIlan Elias nfc_dbg("nci_ver 0x%x, config_status 0x%x", 50e8c0dacdSIlan Elias rsp->nci_ver, rsp->config_status); 51e8c0dacdSIlan Elias } 526a2968aaSIlan Elias 536a2968aaSIlan Elias nci_req_complete(ndev, rsp->status); 546a2968aaSIlan Elias } 556a2968aaSIlan Elias 566a2968aaSIlan Elias static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 576a2968aaSIlan Elias { 586a2968aaSIlan Elias struct nci_core_init_rsp_1 *rsp_1 = (void *) skb->data; 596a2968aaSIlan Elias struct nci_core_init_rsp_2 *rsp_2; 606a2968aaSIlan Elias 616a2968aaSIlan Elias nfc_dbg("entry, status 0x%x", rsp_1->status); 626a2968aaSIlan Elias 636a2968aaSIlan Elias if (rsp_1->status != NCI_STATUS_OK) 64e8c0dacdSIlan Elias goto exit; 656a2968aaSIlan Elias 666a2968aaSIlan Elias ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features); 676a2968aaSIlan Elias ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces; 686a2968aaSIlan Elias 696a2968aaSIlan Elias if (ndev->num_supported_rf_interfaces > 706a2968aaSIlan Elias NCI_MAX_SUPPORTED_RF_INTERFACES) { 716a2968aaSIlan Elias ndev->num_supported_rf_interfaces = 726a2968aaSIlan Elias NCI_MAX_SUPPORTED_RF_INTERFACES; 736a2968aaSIlan Elias } 746a2968aaSIlan Elias 756a2968aaSIlan Elias memcpy(ndev->supported_rf_interfaces, 766a2968aaSIlan Elias rsp_1->supported_rf_interfaces, 776a2968aaSIlan Elias ndev->num_supported_rf_interfaces); 786a2968aaSIlan Elias 79e8c0dacdSIlan Elias rsp_2 = (void *) (skb->data + 6 + rsp_1->num_supported_rf_interfaces); 806a2968aaSIlan Elias 816a2968aaSIlan Elias ndev->max_logical_connections = 826a2968aaSIlan Elias rsp_2->max_logical_connections; 836a2968aaSIlan Elias ndev->max_routing_table_size = 846a2968aaSIlan Elias __le16_to_cpu(rsp_2->max_routing_table_size); 85e8c0dacdSIlan Elias ndev->max_ctrl_pkt_payload_len = 86e8c0dacdSIlan Elias rsp_2->max_ctrl_pkt_payload_len; 87e8c0dacdSIlan Elias ndev->max_size_for_large_params = 88e8c0dacdSIlan Elias __le16_to_cpu(rsp_2->max_size_for_large_params); 89e8c0dacdSIlan Elias ndev->max_data_pkt_payload_size = 90e8c0dacdSIlan Elias rsp_2->max_data_pkt_payload_size; 91e8c0dacdSIlan Elias ndev->initial_num_credits = 92e8c0dacdSIlan Elias rsp_2->initial_num_credits; 93e8c0dacdSIlan Elias ndev->manufact_id = 94e8c0dacdSIlan Elias rsp_2->manufact_id; 95e8c0dacdSIlan Elias ndev->manufact_specific_info = 96e8c0dacdSIlan Elias __le32_to_cpu(rsp_2->manufact_specific_info); 97e8c0dacdSIlan Elias 98e8c0dacdSIlan Elias atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); 996a2968aaSIlan Elias 1006a2968aaSIlan Elias nfc_dbg("nfcc_features 0x%x", 1016a2968aaSIlan Elias ndev->nfcc_features); 1026a2968aaSIlan Elias nfc_dbg("num_supported_rf_interfaces %d", 1036a2968aaSIlan Elias ndev->num_supported_rf_interfaces); 1046a2968aaSIlan Elias nfc_dbg("supported_rf_interfaces[0] 0x%x", 1056a2968aaSIlan Elias ndev->supported_rf_interfaces[0]); 1066a2968aaSIlan Elias nfc_dbg("supported_rf_interfaces[1] 0x%x", 1076a2968aaSIlan Elias ndev->supported_rf_interfaces[1]); 1086a2968aaSIlan Elias nfc_dbg("supported_rf_interfaces[2] 0x%x", 1096a2968aaSIlan Elias ndev->supported_rf_interfaces[2]); 1106a2968aaSIlan Elias nfc_dbg("supported_rf_interfaces[3] 0x%x", 1116a2968aaSIlan Elias ndev->supported_rf_interfaces[3]); 1126a2968aaSIlan Elias nfc_dbg("max_logical_connections %d", 1136a2968aaSIlan Elias ndev->max_logical_connections); 1146a2968aaSIlan Elias nfc_dbg("max_routing_table_size %d", 1156a2968aaSIlan Elias ndev->max_routing_table_size); 116e8c0dacdSIlan Elias nfc_dbg("max_ctrl_pkt_payload_len %d", 117e8c0dacdSIlan Elias ndev->max_ctrl_pkt_payload_len); 118e8c0dacdSIlan Elias nfc_dbg("max_size_for_large_params %d", 119e8c0dacdSIlan Elias ndev->max_size_for_large_params); 120e8c0dacdSIlan Elias nfc_dbg("max_data_pkt_payload_size %d", 121e8c0dacdSIlan Elias ndev->max_data_pkt_payload_size); 122e8c0dacdSIlan Elias nfc_dbg("initial_num_credits %d", 123e8c0dacdSIlan Elias ndev->initial_num_credits); 124e8c0dacdSIlan Elias nfc_dbg("manufact_id 0x%x", 125e8c0dacdSIlan Elias ndev->manufact_id); 126e8c0dacdSIlan Elias nfc_dbg("manufact_specific_info 0x%x", 127e8c0dacdSIlan Elias ndev->manufact_specific_info); 1286a2968aaSIlan Elias 129e8c0dacdSIlan Elias exit: 1306a2968aaSIlan Elias nci_req_complete(ndev, rsp_1->status); 1316a2968aaSIlan Elias } 1326a2968aaSIlan Elias 1336a2968aaSIlan Elias static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, 1346a2968aaSIlan Elias struct sk_buff *skb) 1356a2968aaSIlan Elias { 1366a2968aaSIlan Elias __u8 status = skb->data[0]; 1376a2968aaSIlan Elias 1386a2968aaSIlan Elias nfc_dbg("entry, status 0x%x", status); 1396a2968aaSIlan Elias 1406a2968aaSIlan Elias nci_req_complete(ndev, status); 1416a2968aaSIlan Elias } 1426a2968aaSIlan Elias 1436a2968aaSIlan Elias static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 1446a2968aaSIlan Elias { 1456a2968aaSIlan Elias __u8 status = skb->data[0]; 1466a2968aaSIlan Elias 1476a2968aaSIlan Elias nfc_dbg("entry, status 0x%x", status); 1486a2968aaSIlan Elias 1496a2968aaSIlan Elias if (status == NCI_STATUS_OK) 1506a2968aaSIlan Elias set_bit(NCI_DISCOVERY, &ndev->flags); 1516a2968aaSIlan Elias 1526a2968aaSIlan Elias nci_req_complete(ndev, status); 1536a2968aaSIlan Elias } 1546a2968aaSIlan Elias 1556a2968aaSIlan Elias static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, 1566a2968aaSIlan Elias struct sk_buff *skb) 1576a2968aaSIlan Elias { 1586a2968aaSIlan Elias __u8 status = skb->data[0]; 1596a2968aaSIlan Elias 1606a2968aaSIlan Elias nfc_dbg("entry, status 0x%x", status); 1616a2968aaSIlan Elias 1626a2968aaSIlan Elias clear_bit(NCI_DISCOVERY, &ndev->flags); 1636a2968aaSIlan Elias 1646a2968aaSIlan Elias nci_req_complete(ndev, status); 1656a2968aaSIlan Elias } 1666a2968aaSIlan Elias 1676a2968aaSIlan Elias void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) 1686a2968aaSIlan Elias { 1696a2968aaSIlan Elias __u16 rsp_opcode = nci_opcode(skb->data); 1706a2968aaSIlan Elias 1716a2968aaSIlan Elias /* we got a rsp, stop the cmd timer */ 1726a2968aaSIlan Elias del_timer(&ndev->cmd_timer); 1736a2968aaSIlan Elias 1746a2968aaSIlan Elias nfc_dbg("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", 1756a2968aaSIlan Elias nci_pbf(skb->data), 1766a2968aaSIlan Elias nci_opcode_gid(rsp_opcode), 1776a2968aaSIlan Elias nci_opcode_oid(rsp_opcode), 1786a2968aaSIlan Elias nci_plen(skb->data)); 1796a2968aaSIlan Elias 1806a2968aaSIlan Elias /* strip the nci control header */ 1816a2968aaSIlan Elias skb_pull(skb, NCI_CTRL_HDR_SIZE); 1826a2968aaSIlan Elias 1836a2968aaSIlan Elias switch (rsp_opcode) { 1846a2968aaSIlan Elias case NCI_OP_CORE_RESET_RSP: 1856a2968aaSIlan Elias nci_core_reset_rsp_packet(ndev, skb); 1866a2968aaSIlan Elias break; 1876a2968aaSIlan Elias 1886a2968aaSIlan Elias case NCI_OP_CORE_INIT_RSP: 1896a2968aaSIlan Elias nci_core_init_rsp_packet(ndev, skb); 1906a2968aaSIlan Elias break; 1916a2968aaSIlan Elias 1926a2968aaSIlan Elias case NCI_OP_RF_DISCOVER_MAP_RSP: 1936a2968aaSIlan Elias nci_rf_disc_map_rsp_packet(ndev, skb); 1946a2968aaSIlan Elias break; 1956a2968aaSIlan Elias 1966a2968aaSIlan Elias case NCI_OP_RF_DISCOVER_RSP: 1976a2968aaSIlan Elias nci_rf_disc_rsp_packet(ndev, skb); 1986a2968aaSIlan Elias break; 1996a2968aaSIlan Elias 2006a2968aaSIlan Elias case NCI_OP_RF_DEACTIVATE_RSP: 2016a2968aaSIlan Elias nci_rf_deactivate_rsp_packet(ndev, skb); 2026a2968aaSIlan Elias break; 2036a2968aaSIlan Elias 2046a2968aaSIlan Elias default: 205*ed1e0ad8SJoe Perches pr_err("unknown rsp opcode 0x%x\n", rsp_opcode); 2066a2968aaSIlan Elias break; 2076a2968aaSIlan Elias } 2086a2968aaSIlan Elias 2096a2968aaSIlan Elias kfree_skb(skb); 2106a2968aaSIlan Elias 2116a2968aaSIlan Elias /* trigger the next cmd */ 2126a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 2136a2968aaSIlan Elias if (!skb_queue_empty(&ndev->cmd_q)) 2146a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 2156a2968aaSIlan Elias } 216