18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The NFC Controller Interface is the communication protocol between an 48c2ecf20Sopenharmony_ci * NFC Controller (NFCC) and a Device Host (DH). 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 78c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell International Ltd. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Written by Ilan Elias <ilane@ti.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Acknowledgements: 128c2ecf20Sopenharmony_ci * This file is based on hci_core.c, which was written 138c2ecf20Sopenharmony_ci * by Maxim Krasnyansky. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/types.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci#include <linux/completion.h> 238c2ecf20Sopenharmony_ci#include <linux/export.h> 248c2ecf20Sopenharmony_ci#include <linux/sched.h> 258c2ecf20Sopenharmony_ci#include <linux/bitops.h> 268c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "../nfc.h" 298c2ecf20Sopenharmony_ci#include <net/nfc/nci.h> 308c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 318c2ecf20Sopenharmony_ci#include <linux/nfc.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct core_conn_create_data { 348c2ecf20Sopenharmony_ci int length; 358c2ecf20Sopenharmony_ci struct nci_core_conn_create_cmd *cmd; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void nci_cmd_work(struct work_struct *work); 398c2ecf20Sopenharmony_cistatic void nci_rx_work(struct work_struct *work); 408c2ecf20Sopenharmony_cistatic void nci_tx_work(struct work_struct *work); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, 438c2ecf20Sopenharmony_ci int conn_id) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 488c2ecf20Sopenharmony_ci if (conn_info->conn_id == conn_id) 498c2ecf20Sopenharmony_ci return conn_info; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return NULL; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciint nci_get_conn_info_by_dest_type_params(struct nci_dev *ndev, u8 dest_type, 568c2ecf20Sopenharmony_ci struct dest_spec_params *params) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 618c2ecf20Sopenharmony_ci if (conn_info->dest_type == dest_type) { 628c2ecf20Sopenharmony_ci if (!params) 638c2ecf20Sopenharmony_ci return conn_info->conn_id; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (params->id == conn_info->dest_params->id && 668c2ecf20Sopenharmony_ci params->protocol == conn_info->dest_params->protocol) 678c2ecf20Sopenharmony_ci return conn_info->conn_id; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return -EINVAL; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_get_conn_info_by_dest_type_params); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* ---- NCI requests ---- */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_civoid nci_req_complete(struct nci_dev *ndev, int result) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if (ndev->req_status == NCI_REQ_PEND) { 808c2ecf20Sopenharmony_ci ndev->req_result = result; 818c2ecf20Sopenharmony_ci ndev->req_status = NCI_REQ_DONE; 828c2ecf20Sopenharmony_ci complete(&ndev->req_completion); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_req_complete); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void nci_req_cancel(struct nci_dev *ndev, int err) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci if (ndev->req_status == NCI_REQ_PEND) { 908c2ecf20Sopenharmony_ci ndev->req_result = err; 918c2ecf20Sopenharmony_ci ndev->req_status = NCI_REQ_CANCELED; 928c2ecf20Sopenharmony_ci complete(&ndev->req_completion); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Execute request and wait for completion. */ 978c2ecf20Sopenharmony_cistatic int __nci_request(struct nci_dev *ndev, 988c2ecf20Sopenharmony_ci void (*req)(struct nci_dev *ndev, unsigned long opt), 998c2ecf20Sopenharmony_ci unsigned long opt, __u32 timeout) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int rc = 0; 1028c2ecf20Sopenharmony_ci long completion_rc; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ndev->req_status = NCI_REQ_PEND; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci reinit_completion(&ndev->req_completion); 1078c2ecf20Sopenharmony_ci req(ndev, opt); 1088c2ecf20Sopenharmony_ci completion_rc = 1098c2ecf20Sopenharmony_ci wait_for_completion_interruptible_timeout(&ndev->req_completion, 1108c2ecf20Sopenharmony_ci timeout); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pr_debug("wait_for_completion return %ld\n", completion_rc); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (completion_rc > 0) { 1158c2ecf20Sopenharmony_ci switch (ndev->req_status) { 1168c2ecf20Sopenharmony_ci case NCI_REQ_DONE: 1178c2ecf20Sopenharmony_ci rc = nci_to_errno(ndev->req_result); 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci case NCI_REQ_CANCELED: 1218c2ecf20Sopenharmony_ci rc = -ndev->req_result; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci default: 1258c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci pr_err("wait_for_completion_interruptible_timeout failed %ld\n", 1308c2ecf20Sopenharmony_ci completion_rc); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ndev->req_status = ndev->req_result = 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return rc; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciinline int nci_request(struct nci_dev *ndev, 1418c2ecf20Sopenharmony_ci void (*req)(struct nci_dev *ndev, 1428c2ecf20Sopenharmony_ci unsigned long opt), 1438c2ecf20Sopenharmony_ci unsigned long opt, __u32 timeout) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int rc; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Serialize all requests */ 1488c2ecf20Sopenharmony_ci mutex_lock(&ndev->req_lock); 1498c2ecf20Sopenharmony_ci /* check the state after obtaing the lock against any races 1508c2ecf20Sopenharmony_ci * from nci_close_device when the device gets removed. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci if (test_bit(NCI_UP, &ndev->flags)) 1538c2ecf20Sopenharmony_ci rc = __nci_request(ndev, req, opt, timeout); 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci rc = -ENETDOWN; 1568c2ecf20Sopenharmony_ci mutex_unlock(&ndev->req_lock); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return rc; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void nci_reset_req(struct nci_dev *ndev, unsigned long opt) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct nci_core_reset_cmd cmd; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; 1668c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void nci_init_req(struct nci_dev *ndev, unsigned long opt) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct nci_rf_disc_map_cmd cmd; 1778c2ecf20Sopenharmony_ci struct disc_map_config *cfg = cmd.mapping_configs; 1788c2ecf20Sopenharmony_ci __u8 *num = &cmd.num_mapping_configs; 1798c2ecf20Sopenharmony_ci int i; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* set rf mapping configurations */ 1828c2ecf20Sopenharmony_ci *num = 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ 1858c2ecf20Sopenharmony_ci for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { 1868c2ecf20Sopenharmony_ci if (ndev->supported_rf_interfaces[i] == 1878c2ecf20Sopenharmony_ci NCI_RF_INTERFACE_ISO_DEP) { 1888c2ecf20Sopenharmony_ci cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 1898c2ecf20Sopenharmony_ci cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 1908c2ecf20Sopenharmony_ci NCI_DISC_MAP_MODE_LISTEN; 1918c2ecf20Sopenharmony_ci cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; 1928c2ecf20Sopenharmony_ci (*num)++; 1938c2ecf20Sopenharmony_ci } else if (ndev->supported_rf_interfaces[i] == 1948c2ecf20Sopenharmony_ci NCI_RF_INTERFACE_NFC_DEP) { 1958c2ecf20Sopenharmony_ci cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 1968c2ecf20Sopenharmony_ci cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 1978c2ecf20Sopenharmony_ci NCI_DISC_MAP_MODE_LISTEN; 1988c2ecf20Sopenharmony_ci cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP; 1998c2ecf20Sopenharmony_ci (*num)++; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, 2078c2ecf20Sopenharmony_ci (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistruct nci_set_config_param { 2118c2ecf20Sopenharmony_ci __u8 id; 2128c2ecf20Sopenharmony_ci size_t len; 2138c2ecf20Sopenharmony_ci __u8 *val; 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct nci_set_config_param *param = (struct nci_set_config_param *)opt; 2198c2ecf20Sopenharmony_ci struct nci_core_set_config_cmd cmd; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci BUG_ON(param->len > NCI_MAX_PARAM_LEN); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci cmd.num_params = 1; 2248c2ecf20Sopenharmony_ci cmd.param.id = param->id; 2258c2ecf20Sopenharmony_ci cmd.param.len = param->len; 2268c2ecf20Sopenharmony_ci memcpy(cmd.param.val, param->val, param->len); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistruct nci_rf_discover_param { 2328c2ecf20Sopenharmony_ci __u32 im_protocols; 2338c2ecf20Sopenharmony_ci __u32 tm_protocols; 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct nci_rf_discover_param *param = 2398c2ecf20Sopenharmony_ci (struct nci_rf_discover_param *)opt; 2408c2ecf20Sopenharmony_ci struct nci_rf_disc_cmd cmd; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci cmd.num_disc_configs = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 2458c2ecf20Sopenharmony_ci (param->im_protocols & NFC_PROTO_JEWEL_MASK || 2468c2ecf20Sopenharmony_ci param->im_protocols & NFC_PROTO_MIFARE_MASK || 2478c2ecf20Sopenharmony_ci param->im_protocols & NFC_PROTO_ISO14443_MASK || 2488c2ecf20Sopenharmony_ci param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 2498c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 2508c2ecf20Sopenharmony_ci NCI_NFC_A_PASSIVE_POLL_MODE; 2518c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2528c2ecf20Sopenharmony_ci cmd.num_disc_configs++; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 2568c2ecf20Sopenharmony_ci (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { 2578c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 2588c2ecf20Sopenharmony_ci NCI_NFC_B_PASSIVE_POLL_MODE; 2598c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2608c2ecf20Sopenharmony_ci cmd.num_disc_configs++; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 2648c2ecf20Sopenharmony_ci (param->im_protocols & NFC_PROTO_FELICA_MASK || 2658c2ecf20Sopenharmony_ci param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 2668c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 2678c2ecf20Sopenharmony_ci NCI_NFC_F_PASSIVE_POLL_MODE; 2688c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2698c2ecf20Sopenharmony_ci cmd.num_disc_configs++; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 2738c2ecf20Sopenharmony_ci (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { 2748c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 2758c2ecf20Sopenharmony_ci NCI_NFC_V_PASSIVE_POLL_MODE; 2768c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2778c2ecf20Sopenharmony_ci cmd.num_disc_configs++; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && 2818c2ecf20Sopenharmony_ci (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { 2828c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 2838c2ecf20Sopenharmony_ci NCI_NFC_A_PASSIVE_LISTEN_MODE; 2848c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2858c2ecf20Sopenharmony_ci cmd.num_disc_configs++; 2868c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 2878c2ecf20Sopenharmony_ci NCI_NFC_F_PASSIVE_LISTEN_MODE; 2888c2ecf20Sopenharmony_ci cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2898c2ecf20Sopenharmony_ci cmd.num_disc_configs++; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, 2938c2ecf20Sopenharmony_ci (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), 2948c2ecf20Sopenharmony_ci &cmd); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistruct nci_rf_discover_select_param { 2988c2ecf20Sopenharmony_ci __u8 rf_discovery_id; 2998c2ecf20Sopenharmony_ci __u8 rf_protocol; 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct nci_rf_discover_select_param *param = 3058c2ecf20Sopenharmony_ci (struct nci_rf_discover_select_param *)opt; 3068c2ecf20Sopenharmony_ci struct nci_rf_discover_select_cmd cmd; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci cmd.rf_discovery_id = param->rf_discovery_id; 3098c2ecf20Sopenharmony_ci cmd.rf_protocol = param->rf_protocol; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci switch (cmd.rf_protocol) { 3128c2ecf20Sopenharmony_ci case NCI_RF_PROTOCOL_ISO_DEP: 3138c2ecf20Sopenharmony_ci cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci case NCI_RF_PROTOCOL_NFC_DEP: 3178c2ecf20Sopenharmony_ci cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci default: 3218c2ecf20Sopenharmony_ci cmd.rf_interface = NCI_RF_INTERFACE_FRAME; 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, 3268c2ecf20Sopenharmony_ci sizeof(struct nci_rf_discover_select_cmd), &cmd); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct nci_rf_deactivate_cmd cmd; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci cmd.type = opt; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, 3368c2ecf20Sopenharmony_ci sizeof(struct nci_rf_deactivate_cmd), &cmd); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistruct nci_cmd_param { 3408c2ecf20Sopenharmony_ci __u16 opcode; 3418c2ecf20Sopenharmony_ci size_t len; 3428c2ecf20Sopenharmony_ci __u8 *payload; 3438c2ecf20Sopenharmony_ci}; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void nci_generic_req(struct nci_dev *ndev, unsigned long opt) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct nci_cmd_param *param = 3488c2ecf20Sopenharmony_ci (struct nci_cmd_param *)opt; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci nci_send_cmd(ndev, param->opcode, param->len, param->payload); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciint nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct nci_cmd_param param; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid); 3588c2ecf20Sopenharmony_ci param.len = len; 3598c2ecf20Sopenharmony_ci param.payload = payload; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, 3628c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_CMD_TIMEOUT)); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_prop_cmd); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciint nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct nci_cmd_param param; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci param.opcode = opcode; 3718c2ecf20Sopenharmony_ci param.len = len; 3728c2ecf20Sopenharmony_ci param.payload = payload; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, 3758c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_CMD_TIMEOUT)); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_core_cmd); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciint nci_core_reset(struct nci_dev *ndev) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_reset_req, 0, 3828c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RESET_TIMEOUT)); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_core_reset); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint nci_core_init(struct nci_dev *ndev) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_init_req, 0, 3898c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_INIT_TIMEOUT)); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_core_init); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistruct nci_loopback_data { 3948c2ecf20Sopenharmony_ci u8 conn_id; 3958c2ecf20Sopenharmony_ci struct sk_buff *data; 3968c2ecf20Sopenharmony_ci}; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void nci_send_data_req(struct nci_dev *ndev, unsigned long opt) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct nci_loopback_data *data = (struct nci_loopback_data *)opt; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci nci_send_data(ndev, data->conn_id, data->data); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct nci_dev *ndev = (struct nci_dev *)context; 4088c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); 4118c2ecf20Sopenharmony_ci if (!conn_info) { 4128c2ecf20Sopenharmony_ci nci_req_complete(ndev, NCI_STATUS_REJECTED); 4138c2ecf20Sopenharmony_ci return; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci conn_info->rx_skb = skb; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci nci_req_complete(ndev, NCI_STATUS_OK); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ciint nci_nfcc_loopback(struct nci_dev *ndev, void *data, size_t data_len, 4228c2ecf20Sopenharmony_ci struct sk_buff **resp) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci int r; 4258c2ecf20Sopenharmony_ci struct nci_loopback_data loopback_data; 4268c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 4278c2ecf20Sopenharmony_ci struct sk_buff *skb; 4288c2ecf20Sopenharmony_ci int conn_id = nci_get_conn_info_by_dest_type_params(ndev, 4298c2ecf20Sopenharmony_ci NCI_DESTINATION_NFCC_LOOPBACK, NULL); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (conn_id < 0) { 4328c2ecf20Sopenharmony_ci r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK, 4338c2ecf20Sopenharmony_ci 0, 0, NULL); 4348c2ecf20Sopenharmony_ci if (r != NCI_STATUS_OK) 4358c2ecf20Sopenharmony_ci return r; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci conn_id = nci_get_conn_info_by_dest_type_params(ndev, 4388c2ecf20Sopenharmony_ci NCI_DESTINATION_NFCC_LOOPBACK, 4398c2ecf20Sopenharmony_ci NULL); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); 4438c2ecf20Sopenharmony_ci if (!conn_info) 4448c2ecf20Sopenharmony_ci return -EPROTO; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* store cb and context to be used on receiving data */ 4478c2ecf20Sopenharmony_ci conn_info->data_exchange_cb = nci_nfcc_loopback_cb; 4488c2ecf20Sopenharmony_ci conn_info->data_exchange_cb_context = ndev; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL); 4518c2ecf20Sopenharmony_ci if (!skb) 4528c2ecf20Sopenharmony_ci return -ENOMEM; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci skb_reserve(skb, NCI_DATA_HDR_SIZE); 4558c2ecf20Sopenharmony_ci skb_put_data(skb, data, data_len); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci loopback_data.conn_id = conn_id; 4588c2ecf20Sopenharmony_ci loopback_data.data = skb; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ndev->cur_conn_id = conn_id; 4618c2ecf20Sopenharmony_ci r = nci_request(ndev, nci_send_data_req, (unsigned long)&loopback_data, 4628c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_DATA_TIMEOUT)); 4638c2ecf20Sopenharmony_ci if (r == NCI_STATUS_OK && resp) 4648c2ecf20Sopenharmony_ci *resp = conn_info->rx_skb; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return r; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_nfcc_loopback); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int nci_open_device(struct nci_dev *ndev) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci int rc = 0; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mutex_lock(&ndev->req_lock); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (test_bit(NCI_UNREG, &ndev->flags)) { 4778c2ecf20Sopenharmony_ci rc = -ENODEV; 4788c2ecf20Sopenharmony_ci goto done; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (test_bit(NCI_UP, &ndev->flags)) { 4828c2ecf20Sopenharmony_ci rc = -EALREADY; 4838c2ecf20Sopenharmony_ci goto done; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (ndev->ops->open(ndev)) { 4878c2ecf20Sopenharmony_ci rc = -EIO; 4888c2ecf20Sopenharmony_ci goto done; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci atomic_set(&ndev->cmd_cnt, 1); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci set_bit(NCI_INIT, &ndev->flags); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (ndev->ops->init) 4968c2ecf20Sopenharmony_ci rc = ndev->ops->init(ndev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!rc) { 4998c2ecf20Sopenharmony_ci rc = __nci_request(ndev, nci_reset_req, 0, 5008c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RESET_TIMEOUT)); 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (!rc && ndev->ops->setup) { 5048c2ecf20Sopenharmony_ci rc = ndev->ops->setup(ndev); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (!rc) { 5088c2ecf20Sopenharmony_ci rc = __nci_request(ndev, nci_init_req, 0, 5098c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_INIT_TIMEOUT)); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!rc && ndev->ops->post_setup) 5138c2ecf20Sopenharmony_ci rc = ndev->ops->post_setup(ndev); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (!rc) { 5168c2ecf20Sopenharmony_ci rc = __nci_request(ndev, nci_init_complete_req, 0, 5178c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_INIT_TIMEOUT)); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci clear_bit(NCI_INIT, &ndev->flags); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!rc) { 5238c2ecf20Sopenharmony_ci set_bit(NCI_UP, &ndev->flags); 5248c2ecf20Sopenharmony_ci nci_clear_target_list(ndev); 5258c2ecf20Sopenharmony_ci atomic_set(&ndev->state, NCI_IDLE); 5268c2ecf20Sopenharmony_ci } else { 5278c2ecf20Sopenharmony_ci /* Init failed, cleanup */ 5288c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->cmd_q); 5298c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->rx_q); 5308c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->tx_q); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci ndev->ops->close(ndev); 5338c2ecf20Sopenharmony_ci ndev->flags &= BIT(NCI_UNREG); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cidone: 5378c2ecf20Sopenharmony_ci mutex_unlock(&ndev->req_lock); 5388c2ecf20Sopenharmony_ci return rc; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int nci_close_device(struct nci_dev *ndev) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci nci_req_cancel(ndev, ENODEV); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* This mutex needs to be held as a barrier for 5468c2ecf20Sopenharmony_ci * caller nci_unregister_device 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci mutex_lock(&ndev->req_lock); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { 5518c2ecf20Sopenharmony_ci /* Need to flush the cmd wq in case 5528c2ecf20Sopenharmony_ci * there is a queued/running cmd_work 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci flush_workqueue(ndev->cmd_wq); 5558c2ecf20Sopenharmony_ci del_timer_sync(&ndev->cmd_timer); 5568c2ecf20Sopenharmony_ci del_timer_sync(&ndev->data_timer); 5578c2ecf20Sopenharmony_ci mutex_unlock(&ndev->req_lock); 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Drop RX and TX queues */ 5628c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->rx_q); 5638c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->tx_q); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Flush RX and TX wq */ 5668c2ecf20Sopenharmony_ci flush_workqueue(ndev->rx_wq); 5678c2ecf20Sopenharmony_ci flush_workqueue(ndev->tx_wq); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Reset device */ 5708c2ecf20Sopenharmony_ci skb_queue_purge(&ndev->cmd_q); 5718c2ecf20Sopenharmony_ci atomic_set(&ndev->cmd_cnt, 1); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci set_bit(NCI_INIT, &ndev->flags); 5748c2ecf20Sopenharmony_ci __nci_request(ndev, nci_reset_req, 0, 5758c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RESET_TIMEOUT)); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* After this point our queues are empty 5788c2ecf20Sopenharmony_ci * and no works are scheduled. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci ndev->ops->close(ndev); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci clear_bit(NCI_INIT, &ndev->flags); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Flush cmd wq */ 5858c2ecf20Sopenharmony_ci flush_workqueue(ndev->cmd_wq); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci del_timer_sync(&ndev->cmd_timer); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Clear flags except NCI_UNREG */ 5908c2ecf20Sopenharmony_ci ndev->flags &= BIT(NCI_UNREG); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci mutex_unlock(&ndev->req_lock); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci/* NCI command timer function */ 5988c2ecf20Sopenharmony_cistatic void nci_cmd_timer(struct timer_list *t) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct nci_dev *ndev = from_timer(ndev, t, cmd_timer); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci atomic_set(&ndev->cmd_cnt, 1); 6038c2ecf20Sopenharmony_ci queue_work(ndev->cmd_wq, &ndev->cmd_work); 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci/* NCI data exchange timer function */ 6078c2ecf20Sopenharmony_cistatic void nci_data_timer(struct timer_list *t) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct nci_dev *ndev = from_timer(ndev, t, data_timer); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 6128c2ecf20Sopenharmony_ci queue_work(ndev->rx_wq, &ndev->rx_work); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int nci_dev_up(struct nfc_dev *nfc_dev) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return nci_open_device(ndev); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int nci_dev_down(struct nfc_dev *nfc_dev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return nci_close_device(ndev); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ciint nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct nci_set_config_param param; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!val || !len) 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci param.id = id; 6378c2ecf20Sopenharmony_ci param.len = len; 6388c2ecf20Sopenharmony_ci param.val = val; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 6418c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_set_config); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct nci_nfcee_discover_cmd cmd; 6488c2ecf20Sopenharmony_ci __u8 action = opt; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci cmd.discovery_action = action; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ciint nci_nfcee_discover(struct nci_dev *ndev, u8 action) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_nfcee_discover_req, action, 6588c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_CMD_TIMEOUT)); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_nfcee_discover); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct nci_nfcee_mode_set_cmd *cmd = 6658c2ecf20Sopenharmony_ci (struct nci_nfcee_mode_set_cmd *)opt; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD, 6688c2ecf20Sopenharmony_ci sizeof(struct nci_nfcee_mode_set_cmd), cmd); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciint nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct nci_nfcee_mode_set_cmd cmd; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci cmd.nfcee_id = nfcee_id; 6768c2ecf20Sopenharmony_ci cmd.nfcee_mode = nfcee_mode; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_nfcee_mode_set_req, 6798c2ecf20Sopenharmony_ci (unsigned long)&cmd, 6808c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_CMD_TIMEOUT)); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_nfcee_mode_set); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct core_conn_create_data *data = 6878c2ecf20Sopenharmony_ci (struct core_conn_create_data *)opt; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ciint nci_core_conn_create(struct nci_dev *ndev, u8 destination_type, 6938c2ecf20Sopenharmony_ci u8 number_destination_params, 6948c2ecf20Sopenharmony_ci size_t params_len, 6958c2ecf20Sopenharmony_ci struct core_conn_create_dest_spec_params *params) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci int r; 6988c2ecf20Sopenharmony_ci struct nci_core_conn_create_cmd *cmd; 6998c2ecf20Sopenharmony_ci struct core_conn_create_data data; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci data.length = params_len + sizeof(struct nci_core_conn_create_cmd); 7028c2ecf20Sopenharmony_ci cmd = kzalloc(data.length, GFP_KERNEL); 7038c2ecf20Sopenharmony_ci if (!cmd) 7048c2ecf20Sopenharmony_ci return -ENOMEM; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci cmd->destination_type = destination_type; 7078c2ecf20Sopenharmony_ci cmd->number_destination_params = number_destination_params; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci data.cmd = cmd; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (params) { 7128c2ecf20Sopenharmony_ci memcpy(cmd->params, params, params_len); 7138c2ecf20Sopenharmony_ci if (params->length > 0) 7148c2ecf20Sopenharmony_ci memcpy(&ndev->cur_params, 7158c2ecf20Sopenharmony_ci ¶ms->value[DEST_SPEC_PARAMS_ID_INDEX], 7168c2ecf20Sopenharmony_ci sizeof(struct dest_spec_params)); 7178c2ecf20Sopenharmony_ci else 7188c2ecf20Sopenharmony_ci ndev->cur_params.id = 0; 7198c2ecf20Sopenharmony_ci } else { 7208c2ecf20Sopenharmony_ci ndev->cur_params.id = 0; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci ndev->cur_dest_type = destination_type; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci r = __nci_request(ndev, nci_core_conn_create_req, (unsigned long)&data, 7258c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_CMD_TIMEOUT)); 7268c2ecf20Sopenharmony_ci kfree(cmd); 7278c2ecf20Sopenharmony_ci return r; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_core_conn_create); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci __u8 conn_id = opt; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ciint nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci ndev->cur_conn_id = conn_id; 7418c2ecf20Sopenharmony_ci return __nci_request(ndev, nci_core_conn_close_req, conn_id, 7428c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_CMD_TIMEOUT)); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_core_conn_close); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 7498c2ecf20Sopenharmony_ci struct nci_set_config_param param; 7508c2ecf20Sopenharmony_ci int rc; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); 7538c2ecf20Sopenharmony_ci if ((param.val == NULL) || (param.len == 0)) 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (param.len > NFC_MAX_GT_LEN) 7578c2ecf20Sopenharmony_ci return -EINVAL; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci param.id = NCI_PN_ATR_REQ_GEN_BYTES; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 7628c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 7638c2ecf20Sopenharmony_ci if (rc) 7648c2ecf20Sopenharmony_ci return rc; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci param.id = NCI_LN_ATR_RES_GEN_BYTES; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 7698c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int nci_set_listen_parameters(struct nfc_dev *nfc_dev) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 7758c2ecf20Sopenharmony_ci int rc; 7768c2ecf20Sopenharmony_ci __u8 val; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci val = NCI_LA_SEL_INFO_NFC_DEP_MASK; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); 7818c2ecf20Sopenharmony_ci if (rc) 7828c2ecf20Sopenharmony_ci return rc; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); 7878c2ecf20Sopenharmony_ci if (rc) 7888c2ecf20Sopenharmony_ci return rc; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int nci_start_poll(struct nfc_dev *nfc_dev, 7968c2ecf20Sopenharmony_ci __u32 im_protocols, __u32 tm_protocols) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 7998c2ecf20Sopenharmony_ci struct nci_rf_discover_param param; 8008c2ecf20Sopenharmony_ci int rc; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || 8038c2ecf20Sopenharmony_ci (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { 8048c2ecf20Sopenharmony_ci pr_err("unable to start poll, since poll is already active\n"); 8058c2ecf20Sopenharmony_ci return -EBUSY; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (ndev->target_active_prot) { 8098c2ecf20Sopenharmony_ci pr_err("there is an active target\n"); 8108c2ecf20Sopenharmony_ci return -EBUSY; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || 8148c2ecf20Sopenharmony_ci (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { 8158c2ecf20Sopenharmony_ci pr_debug("target active or w4 select, implicitly deactivate\n"); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci rc = nci_request(ndev, nci_rf_deactivate_req, 8188c2ecf20Sopenharmony_ci NCI_DEACTIVATE_TYPE_IDLE_MODE, 8198c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 8208c2ecf20Sopenharmony_ci if (rc) 8218c2ecf20Sopenharmony_ci return -EBUSY; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { 8258c2ecf20Sopenharmony_ci rc = nci_set_local_general_bytes(nfc_dev); 8268c2ecf20Sopenharmony_ci if (rc) { 8278c2ecf20Sopenharmony_ci pr_err("failed to set local general bytes\n"); 8288c2ecf20Sopenharmony_ci return rc; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { 8338c2ecf20Sopenharmony_ci rc = nci_set_listen_parameters(nfc_dev); 8348c2ecf20Sopenharmony_ci if (rc) 8358c2ecf20Sopenharmony_ci pr_err("failed to set listen parameters\n"); 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci param.im_protocols = im_protocols; 8398c2ecf20Sopenharmony_ci param.tm_protocols = tm_protocols; 8408c2ecf20Sopenharmony_ci rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m, 8418c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (!rc) 8448c2ecf20Sopenharmony_ci ndev->poll_prots = im_protocols; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return rc; 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void nci_stop_poll(struct nfc_dev *nfc_dev) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && 8548c2ecf20Sopenharmony_ci (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { 8558c2ecf20Sopenharmony_ci pr_err("unable to stop poll, since poll is not active\n"); 8568c2ecf20Sopenharmony_ci return; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE, 8608c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int nci_activate_target(struct nfc_dev *nfc_dev, 8648c2ecf20Sopenharmony_ci struct nfc_target *target, __u32 protocol) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 8678c2ecf20Sopenharmony_ci struct nci_rf_discover_select_param param; 8688c2ecf20Sopenharmony_ci struct nfc_target *nci_target = NULL; 8698c2ecf20Sopenharmony_ci int i; 8708c2ecf20Sopenharmony_ci int rc = 0; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && 8758c2ecf20Sopenharmony_ci (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { 8768c2ecf20Sopenharmony_ci pr_err("there is no available target to activate\n"); 8778c2ecf20Sopenharmony_ci return -EINVAL; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (ndev->target_active_prot) { 8818c2ecf20Sopenharmony_ci pr_err("there is already an active target\n"); 8828c2ecf20Sopenharmony_ci return -EBUSY; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci for (i = 0; i < ndev->n_targets; i++) { 8868c2ecf20Sopenharmony_ci if (ndev->targets[i].idx == target->idx) { 8878c2ecf20Sopenharmony_ci nci_target = &ndev->targets[i]; 8888c2ecf20Sopenharmony_ci break; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (!nci_target) { 8938c2ecf20Sopenharmony_ci pr_err("unable to find the selected target\n"); 8948c2ecf20Sopenharmony_ci return -EINVAL; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (protocol >= NFC_PROTO_MAX) { 8988c2ecf20Sopenharmony_ci pr_err("the requested nfc protocol is invalid\n"); 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!(nci_target->supported_protocols & (1 << protocol))) { 9038c2ecf20Sopenharmony_ci pr_err("target does not support the requested protocol 0x%x\n", 9048c2ecf20Sopenharmony_ci protocol); 9058c2ecf20Sopenharmony_ci return -EINVAL; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { 9098c2ecf20Sopenharmony_ci param.rf_discovery_id = nci_target->logical_idx; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (protocol == NFC_PROTO_JEWEL) 9128c2ecf20Sopenharmony_ci param.rf_protocol = NCI_RF_PROTOCOL_T1T; 9138c2ecf20Sopenharmony_ci else if (protocol == NFC_PROTO_MIFARE) 9148c2ecf20Sopenharmony_ci param.rf_protocol = NCI_RF_PROTOCOL_T2T; 9158c2ecf20Sopenharmony_ci else if (protocol == NFC_PROTO_FELICA) 9168c2ecf20Sopenharmony_ci param.rf_protocol = NCI_RF_PROTOCOL_T3T; 9178c2ecf20Sopenharmony_ci else if (protocol == NFC_PROTO_ISO14443 || 9188c2ecf20Sopenharmony_ci protocol == NFC_PROTO_ISO14443_B) 9198c2ecf20Sopenharmony_ci param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 9208c2ecf20Sopenharmony_ci else 9218c2ecf20Sopenharmony_ci param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci rc = nci_request(ndev, nci_rf_discover_select_req, 9248c2ecf20Sopenharmony_ci (unsigned long)¶m, 9258c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (!rc) 9298c2ecf20Sopenharmony_ci ndev->target_active_prot = protocol; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return rc; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic void nci_deactivate_target(struct nfc_dev *nfc_dev, 9358c2ecf20Sopenharmony_ci struct nfc_target *target, 9368c2ecf20Sopenharmony_ci __u8 mode) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 9398c2ecf20Sopenharmony_ci u8 nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci pr_debug("entry\n"); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (!ndev->target_active_prot) { 9448c2ecf20Sopenharmony_ci pr_err("unable to deactivate target, no active target\n"); 9458c2ecf20Sopenharmony_ci return; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci ndev->target_active_prot = 0; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci switch (mode) { 9518c2ecf20Sopenharmony_ci case NFC_TARGET_MODE_SLEEP: 9528c2ecf20Sopenharmony_ci nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE; 9538c2ecf20Sopenharmony_ci break; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { 9578c2ecf20Sopenharmony_ci nci_request(ndev, nci_rf_deactivate_req, nci_mode, 9588c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, 9638c2ecf20Sopenharmony_ci __u8 comm_mode, __u8 *gb, size_t gb_len) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 9668c2ecf20Sopenharmony_ci int rc; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); 9718c2ecf20Sopenharmony_ci if (rc) 9728c2ecf20Sopenharmony_ci return rc; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, 9758c2ecf20Sopenharmony_ci ndev->remote_gb_len); 9768c2ecf20Sopenharmony_ci if (!rc) 9778c2ecf20Sopenharmony_ci rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, 9788c2ecf20Sopenharmony_ci NFC_RF_INITIATOR); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci return rc; 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic int nci_dep_link_down(struct nfc_dev *nfc_dev) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 9868c2ecf20Sopenharmony_ci int rc; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci pr_debug("entry\n"); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { 9918c2ecf20Sopenharmony_ci nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE); 9928c2ecf20Sopenharmony_ci } else { 9938c2ecf20Sopenharmony_ci if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || 9948c2ecf20Sopenharmony_ci atomic_read(&ndev->state) == NCI_DISCOVERY) { 9958c2ecf20Sopenharmony_ci nci_request(ndev, nci_rf_deactivate_req, 0, 9968c2ecf20Sopenharmony_ci msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci rc = nfc_tm_deactivated(nfc_dev); 10008c2ecf20Sopenharmony_ci if (rc) 10018c2ecf20Sopenharmony_ci pr_err("error when signaling tm deactivation\n"); 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return 0; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 10098c2ecf20Sopenharmony_ci struct sk_buff *skb, 10108c2ecf20Sopenharmony_ci data_exchange_cb_t cb, void *cb_context) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 10138c2ecf20Sopenharmony_ci int rc; 10148c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci conn_info = ndev->rf_conn_info; 10178c2ecf20Sopenharmony_ci if (!conn_info) 10188c2ecf20Sopenharmony_ci return -EPROTO; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci pr_debug("target_idx %d, len %d\n", target->idx, skb->len); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!ndev->target_active_prot) { 10238c2ecf20Sopenharmony_ci pr_err("unable to exchange data, no active target\n"); 10248c2ecf20Sopenharmony_ci return -EINVAL; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 10288c2ecf20Sopenharmony_ci return -EBUSY; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* store cb and context to be used on receiving data */ 10318c2ecf20Sopenharmony_ci conn_info->data_exchange_cb = cb; 10328c2ecf20Sopenharmony_ci conn_info->data_exchange_cb_context = cb_context; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 10358c2ecf20Sopenharmony_ci if (rc) 10368c2ecf20Sopenharmony_ci clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return rc; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 10448c2ecf20Sopenharmony_ci int rc; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 10478c2ecf20Sopenharmony_ci if (rc) 10488c2ecf20Sopenharmony_ci pr_err("unable to send data\n"); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci return rc; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (ndev->ops->enable_se) 10588c2ecf20Sopenharmony_ci return ndev->ops->enable_se(ndev, se_idx); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (ndev->ops->disable_se) 10688c2ecf20Sopenharmony_ci return ndev->ops->disable_se(ndev, se_idx); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return 0; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic int nci_discover_se(struct nfc_dev *nfc_dev) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci int r; 10768c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (ndev->ops->discover_se) { 10798c2ecf20Sopenharmony_ci r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE); 10808c2ecf20Sopenharmony_ci if (r != NCI_STATUS_OK) 10818c2ecf20Sopenharmony_ci return -EPROTO; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci return ndev->ops->discover_se(ndev); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci return 0; 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, 10908c2ecf20Sopenharmony_ci u8 *apdu, size_t apdu_length, 10918c2ecf20Sopenharmony_ci se_io_cb_t cb, void *cb_context) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (ndev->ops->se_io) 10968c2ecf20Sopenharmony_ci return ndev->ops->se_io(ndev, se_idx, apdu, 10978c2ecf20Sopenharmony_ci apdu_length, cb, cb_context); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (!ndev->ops->fw_download) 11078c2ecf20Sopenharmony_ci return -ENOTSUPP; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci return ndev->ops->fw_download(ndev, firmware_name); 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic struct nfc_ops nci_nfc_ops = { 11138c2ecf20Sopenharmony_ci .dev_up = nci_dev_up, 11148c2ecf20Sopenharmony_ci .dev_down = nci_dev_down, 11158c2ecf20Sopenharmony_ci .start_poll = nci_start_poll, 11168c2ecf20Sopenharmony_ci .stop_poll = nci_stop_poll, 11178c2ecf20Sopenharmony_ci .dep_link_up = nci_dep_link_up, 11188c2ecf20Sopenharmony_ci .dep_link_down = nci_dep_link_down, 11198c2ecf20Sopenharmony_ci .activate_target = nci_activate_target, 11208c2ecf20Sopenharmony_ci .deactivate_target = nci_deactivate_target, 11218c2ecf20Sopenharmony_ci .im_transceive = nci_transceive, 11228c2ecf20Sopenharmony_ci .tm_send = nci_tm_send, 11238c2ecf20Sopenharmony_ci .enable_se = nci_enable_se, 11248c2ecf20Sopenharmony_ci .disable_se = nci_disable_se, 11258c2ecf20Sopenharmony_ci .discover_se = nci_discover_se, 11268c2ecf20Sopenharmony_ci .se_io = nci_se_io, 11278c2ecf20Sopenharmony_ci .fw_download = nci_fw_download, 11288c2ecf20Sopenharmony_ci}; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/* ---- Interface to NCI drivers ---- */ 11318c2ecf20Sopenharmony_ci/** 11328c2ecf20Sopenharmony_ci * nci_allocate_device - allocate a new nci device 11338c2ecf20Sopenharmony_ci * 11348c2ecf20Sopenharmony_ci * @ops: device operations 11358c2ecf20Sopenharmony_ci * @supported_protocols: NFC protocols supported by the device 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_cistruct nci_dev *nci_allocate_device(struct nci_ops *ops, 11388c2ecf20Sopenharmony_ci __u32 supported_protocols, 11398c2ecf20Sopenharmony_ci int tx_headroom, int tx_tailroom) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct nci_dev *ndev; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci pr_debug("supported_protocols 0x%x\n", supported_protocols); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (!ops->open || !ops->close || !ops->send) 11468c2ecf20Sopenharmony_ci return NULL; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (!supported_protocols) 11498c2ecf20Sopenharmony_ci return NULL; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); 11528c2ecf20Sopenharmony_ci if (!ndev) 11538c2ecf20Sopenharmony_ci return NULL; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci ndev->ops = ops; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) { 11588c2ecf20Sopenharmony_ci pr_err("Too many proprietary commands: %zd\n", 11598c2ecf20Sopenharmony_ci ops->n_prop_ops); 11608c2ecf20Sopenharmony_ci ops->prop_ops = NULL; 11618c2ecf20Sopenharmony_ci ops->n_prop_ops = 0; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci ndev->tx_headroom = tx_headroom; 11658c2ecf20Sopenharmony_ci ndev->tx_tailroom = tx_tailroom; 11668c2ecf20Sopenharmony_ci init_completion(&ndev->req_completion); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, 11698c2ecf20Sopenharmony_ci supported_protocols, 11708c2ecf20Sopenharmony_ci tx_headroom + NCI_DATA_HDR_SIZE, 11718c2ecf20Sopenharmony_ci tx_tailroom); 11728c2ecf20Sopenharmony_ci if (!ndev->nfc_dev) 11738c2ecf20Sopenharmony_ci goto free_nci; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci ndev->hci_dev = nci_hci_allocate(ndev); 11768c2ecf20Sopenharmony_ci if (!ndev->hci_dev) 11778c2ecf20Sopenharmony_ci goto free_nfc; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci nfc_set_drvdata(ndev->nfc_dev, ndev); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci return ndev; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cifree_nfc: 11848c2ecf20Sopenharmony_ci nfc_free_device(ndev->nfc_dev); 11858c2ecf20Sopenharmony_cifree_nci: 11868c2ecf20Sopenharmony_ci kfree(ndev); 11878c2ecf20Sopenharmony_ci return NULL; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_allocate_device); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci/** 11928c2ecf20Sopenharmony_ci * nci_free_device - deallocate nci device 11938c2ecf20Sopenharmony_ci * 11948c2ecf20Sopenharmony_ci * @ndev: The nci device to deallocate 11958c2ecf20Sopenharmony_ci */ 11968c2ecf20Sopenharmony_civoid nci_free_device(struct nci_dev *ndev) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci nfc_free_device(ndev->nfc_dev); 11998c2ecf20Sopenharmony_ci nci_hci_deallocate(ndev); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci /* drop partial rx data packet if present */ 12028c2ecf20Sopenharmony_ci if (ndev->rx_data_reassembly) 12038c2ecf20Sopenharmony_ci kfree_skb(ndev->rx_data_reassembly); 12048c2ecf20Sopenharmony_ci kfree(ndev); 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_free_device); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/** 12098c2ecf20Sopenharmony_ci * nci_register_device - register a nci device in the nfc subsystem 12108c2ecf20Sopenharmony_ci * 12118c2ecf20Sopenharmony_ci * @ndev: The nci device to register 12128c2ecf20Sopenharmony_ci */ 12138c2ecf20Sopenharmony_ciint nci_register_device(struct nci_dev *ndev) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci int rc; 12168c2ecf20Sopenharmony_ci struct device *dev = &ndev->nfc_dev->dev; 12178c2ecf20Sopenharmony_ci char name[32]; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci ndev->flags = 0; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci INIT_WORK(&ndev->cmd_work, nci_cmd_work); 12228c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); 12238c2ecf20Sopenharmony_ci ndev->cmd_wq = create_singlethread_workqueue(name); 12248c2ecf20Sopenharmony_ci if (!ndev->cmd_wq) { 12258c2ecf20Sopenharmony_ci rc = -ENOMEM; 12268c2ecf20Sopenharmony_ci goto exit; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci INIT_WORK(&ndev->rx_work, nci_rx_work); 12308c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); 12318c2ecf20Sopenharmony_ci ndev->rx_wq = create_singlethread_workqueue(name); 12328c2ecf20Sopenharmony_ci if (!ndev->rx_wq) { 12338c2ecf20Sopenharmony_ci rc = -ENOMEM; 12348c2ecf20Sopenharmony_ci goto destroy_cmd_wq_exit; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci INIT_WORK(&ndev->tx_work, nci_tx_work); 12388c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); 12398c2ecf20Sopenharmony_ci ndev->tx_wq = create_singlethread_workqueue(name); 12408c2ecf20Sopenharmony_ci if (!ndev->tx_wq) { 12418c2ecf20Sopenharmony_ci rc = -ENOMEM; 12428c2ecf20Sopenharmony_ci goto destroy_rx_wq_exit; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci skb_queue_head_init(&ndev->cmd_q); 12468c2ecf20Sopenharmony_ci skb_queue_head_init(&ndev->rx_q); 12478c2ecf20Sopenharmony_ci skb_queue_head_init(&ndev->tx_q); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci timer_setup(&ndev->cmd_timer, nci_cmd_timer, 0); 12508c2ecf20Sopenharmony_ci timer_setup(&ndev->data_timer, nci_data_timer, 0); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci mutex_init(&ndev->req_lock); 12538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ndev->conn_info_list); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci rc = nfc_register_device(ndev->nfc_dev); 12568c2ecf20Sopenharmony_ci if (rc) 12578c2ecf20Sopenharmony_ci goto destroy_tx_wq_exit; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci goto exit; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cidestroy_tx_wq_exit: 12628c2ecf20Sopenharmony_ci destroy_workqueue(ndev->tx_wq); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cidestroy_rx_wq_exit: 12658c2ecf20Sopenharmony_ci destroy_workqueue(ndev->rx_wq); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cidestroy_cmd_wq_exit: 12688c2ecf20Sopenharmony_ci destroy_workqueue(ndev->cmd_wq); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ciexit: 12718c2ecf20Sopenharmony_ci return rc; 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_register_device); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci/** 12768c2ecf20Sopenharmony_ci * nci_unregister_device - unregister a nci device in the nfc subsystem 12778c2ecf20Sopenharmony_ci * 12788c2ecf20Sopenharmony_ci * @ndev: The nci device to unregister 12798c2ecf20Sopenharmony_ci */ 12808c2ecf20Sopenharmony_civoid nci_unregister_device(struct nci_dev *ndev) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info, *n; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* This set_bit is not protected with specialized barrier, 12858c2ecf20Sopenharmony_ci * However, it is fine because the mutex_lock(&ndev->req_lock); 12868c2ecf20Sopenharmony_ci * in nci_close_device() will help to emit one. 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_ci set_bit(NCI_UNREG, &ndev->flags); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci nci_close_device(ndev); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci destroy_workqueue(ndev->cmd_wq); 12938c2ecf20Sopenharmony_ci destroy_workqueue(ndev->rx_wq); 12948c2ecf20Sopenharmony_ci destroy_workqueue(ndev->tx_wq); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) { 12978c2ecf20Sopenharmony_ci list_del(&conn_info->list); 12988c2ecf20Sopenharmony_ci /* conn_info is allocated with devm_kzalloc */ 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci nfc_unregister_device(ndev->nfc_dev); 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_unregister_device); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/** 13068c2ecf20Sopenharmony_ci * nci_recv_frame - receive frame from NCI drivers 13078c2ecf20Sopenharmony_ci * 13088c2ecf20Sopenharmony_ci * @ndev: The nci device 13098c2ecf20Sopenharmony_ci * @skb: The sk_buff to receive 13108c2ecf20Sopenharmony_ci */ 13118c2ecf20Sopenharmony_ciint nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci pr_debug("len %d\n", skb->len); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && 13168c2ecf20Sopenharmony_ci !test_bit(NCI_INIT, &ndev->flags))) { 13178c2ecf20Sopenharmony_ci kfree_skb(skb); 13188c2ecf20Sopenharmony_ci return -ENXIO; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci /* Queue frame for rx worker thread */ 13228c2ecf20Sopenharmony_ci skb_queue_tail(&ndev->rx_q, skb); 13238c2ecf20Sopenharmony_ci queue_work(ndev->rx_wq, &ndev->rx_work); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci return 0; 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_recv_frame); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ciint nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci pr_debug("len %d\n", skb->len); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (!ndev) { 13348c2ecf20Sopenharmony_ci kfree_skb(skb); 13358c2ecf20Sopenharmony_ci return -ENODEV; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci /* Get rid of skb owner, prior to sending to the driver. */ 13398c2ecf20Sopenharmony_ci skb_orphan(skb); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* Send copy to sniffer */ 13428c2ecf20Sopenharmony_ci nfc_send_to_raw_sock(ndev->nfc_dev, skb, 13438c2ecf20Sopenharmony_ci RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci return ndev->ops->send(ndev, skb); 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_send_frame); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci/* Send NCI command */ 13508c2ecf20Sopenharmony_ciint nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct nci_ctrl_hdr *hdr; 13538c2ecf20Sopenharmony_ci struct sk_buff *skb; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci pr_debug("opcode 0x%x, plen %d\n", opcode, plen); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); 13588c2ecf20Sopenharmony_ci if (!skb) { 13598c2ecf20Sopenharmony_ci pr_err("no memory for command\n"); 13608c2ecf20Sopenharmony_ci return -ENOMEM; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci hdr = skb_put(skb, NCI_CTRL_HDR_SIZE); 13648c2ecf20Sopenharmony_ci hdr->gid = nci_opcode_gid(opcode); 13658c2ecf20Sopenharmony_ci hdr->oid = nci_opcode_oid(opcode); 13668c2ecf20Sopenharmony_ci hdr->plen = plen; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); 13698c2ecf20Sopenharmony_ci nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (plen) 13728c2ecf20Sopenharmony_ci skb_put_data(skb, payload, plen); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci skb_queue_tail(&ndev->cmd_q, skb); 13758c2ecf20Sopenharmony_ci queue_work(ndev->cmd_wq, &ndev->cmd_work); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci return 0; 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nci_send_cmd); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci/* Proprietary commands API */ 13828c2ecf20Sopenharmony_cistatic struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops, 13838c2ecf20Sopenharmony_ci size_t n_ops, 13848c2ecf20Sopenharmony_ci __u16 opcode) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci size_t i; 13878c2ecf20Sopenharmony_ci struct nci_driver_ops *op; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (!ops || !n_ops) 13908c2ecf20Sopenharmony_ci return NULL; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci for (i = 0; i < n_ops; i++) { 13938c2ecf20Sopenharmony_ci op = &ops[i]; 13948c2ecf20Sopenharmony_ci if (op->opcode == opcode) 13958c2ecf20Sopenharmony_ci return op; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci return NULL; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, 14028c2ecf20Sopenharmony_ci struct sk_buff *skb, struct nci_driver_ops *ops, 14038c2ecf20Sopenharmony_ci size_t n_ops) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci struct nci_driver_ops *op; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci op = ops_cmd_lookup(ops, n_ops, rsp_opcode); 14088c2ecf20Sopenharmony_ci if (!op || !op->rsp) 14098c2ecf20Sopenharmony_ci return -ENOTSUPP; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci return op->rsp(ndev, skb); 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cistatic int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, 14158c2ecf20Sopenharmony_ci struct sk_buff *skb, struct nci_driver_ops *ops, 14168c2ecf20Sopenharmony_ci size_t n_ops) 14178c2ecf20Sopenharmony_ci{ 14188c2ecf20Sopenharmony_ci struct nci_driver_ops *op; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci op = ops_cmd_lookup(ops, n_ops, ntf_opcode); 14218c2ecf20Sopenharmony_ci if (!op || !op->ntf) 14228c2ecf20Sopenharmony_ci return -ENOTSUPP; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci return op->ntf(ndev, skb); 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ciint nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, 14288c2ecf20Sopenharmony_ci struct sk_buff *skb) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops, 14318c2ecf20Sopenharmony_ci ndev->ops->n_prop_ops); 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ciint nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, 14358c2ecf20Sopenharmony_ci struct sk_buff *skb) 14368c2ecf20Sopenharmony_ci{ 14378c2ecf20Sopenharmony_ci return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops, 14388c2ecf20Sopenharmony_ci ndev->ops->n_prop_ops); 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ciint nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, 14428c2ecf20Sopenharmony_ci struct sk_buff *skb) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops, 14458c2ecf20Sopenharmony_ci ndev->ops->n_core_ops); 14468c2ecf20Sopenharmony_ci} 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ciint nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, 14498c2ecf20Sopenharmony_ci struct sk_buff *skb) 14508c2ecf20Sopenharmony_ci{ 14518c2ecf20Sopenharmony_ci return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops, 14528c2ecf20Sopenharmony_ci ndev->ops->n_core_ops); 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci/* ---- NCI TX Data worker thread ---- */ 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic void nci_tx_work(struct work_struct *work) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); 14608c2ecf20Sopenharmony_ci struct nci_conn_info *conn_info; 14618c2ecf20Sopenharmony_ci struct sk_buff *skb; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); 14648c2ecf20Sopenharmony_ci if (!conn_info) 14658c2ecf20Sopenharmony_ci return; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt)); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* Send queued tx data */ 14708c2ecf20Sopenharmony_ci while (atomic_read(&conn_info->credits_cnt)) { 14718c2ecf20Sopenharmony_ci skb = skb_dequeue(&ndev->tx_q); 14728c2ecf20Sopenharmony_ci if (!skb) 14738c2ecf20Sopenharmony_ci return; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* Check if data flow control is used */ 14768c2ecf20Sopenharmony_ci if (atomic_read(&conn_info->credits_cnt) != 14778c2ecf20Sopenharmony_ci NCI_DATA_FLOW_CONTROL_NOT_USED) 14788c2ecf20Sopenharmony_ci atomic_dec(&conn_info->credits_cnt); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", 14818c2ecf20Sopenharmony_ci nci_pbf(skb->data), 14828c2ecf20Sopenharmony_ci nci_conn_id(skb->data), 14838c2ecf20Sopenharmony_ci nci_plen(skb->data)); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci nci_send_frame(ndev, skb); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci mod_timer(&ndev->data_timer, 14888c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci/* ----- NCI RX worker thread (data & control) ----- */ 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic void nci_rx_work(struct work_struct *work) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); 14978c2ecf20Sopenharmony_ci struct sk_buff *skb; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&ndev->rx_q))) { 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci /* Send copy to sniffer */ 15028c2ecf20Sopenharmony_ci nfc_send_to_raw_sock(ndev->nfc_dev, skb, 15038c2ecf20Sopenharmony_ci RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci /* Process frame */ 15068c2ecf20Sopenharmony_ci switch (nci_mt(skb->data)) { 15078c2ecf20Sopenharmony_ci case NCI_MT_RSP_PKT: 15088c2ecf20Sopenharmony_ci nci_rsp_packet(ndev, skb); 15098c2ecf20Sopenharmony_ci break; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci case NCI_MT_NTF_PKT: 15128c2ecf20Sopenharmony_ci nci_ntf_packet(ndev, skb); 15138c2ecf20Sopenharmony_ci break; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci case NCI_MT_DATA_PKT: 15168c2ecf20Sopenharmony_ci nci_rx_data_packet(ndev, skb); 15178c2ecf20Sopenharmony_ci break; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci default: 15208c2ecf20Sopenharmony_ci pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); 15218c2ecf20Sopenharmony_ci kfree_skb(skb); 15228c2ecf20Sopenharmony_ci break; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* check if a data exchange timout has occurred */ 15278c2ecf20Sopenharmony_ci if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { 15288c2ecf20Sopenharmony_ci /* complete the data exchange transaction, if exists */ 15298c2ecf20Sopenharmony_ci if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 15308c2ecf20Sopenharmony_ci nci_data_exchange_complete(ndev, NULL, 15318c2ecf20Sopenharmony_ci ndev->cur_conn_id, 15328c2ecf20Sopenharmony_ci -ETIMEDOUT); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci/* ----- NCI TX CMD worker thread ----- */ 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_cistatic void nci_cmd_work(struct work_struct *work) 15418c2ecf20Sopenharmony_ci{ 15428c2ecf20Sopenharmony_ci struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); 15438c2ecf20Sopenharmony_ci struct sk_buff *skb; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* Send queued command */ 15488c2ecf20Sopenharmony_ci if (atomic_read(&ndev->cmd_cnt)) { 15498c2ecf20Sopenharmony_ci skb = skb_dequeue(&ndev->cmd_q); 15508c2ecf20Sopenharmony_ci if (!skb) 15518c2ecf20Sopenharmony_ci return; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci atomic_dec(&ndev->cmd_cnt); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 15568c2ecf20Sopenharmony_ci nci_pbf(skb->data), 15578c2ecf20Sopenharmony_ci nci_opcode_gid(nci_opcode(skb->data)), 15588c2ecf20Sopenharmony_ci nci_opcode_oid(nci_opcode(skb->data)), 15598c2ecf20Sopenharmony_ci nci_plen(skb->data)); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci nci_send_frame(ndev, skb); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci mod_timer(&ndev->cmd_timer, 15648c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1569