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. 6772dccf4SJulien Lefrique * Copyright (C) 2014 Marvell International Ltd. 76a2968aaSIlan Elias * 86a2968aaSIlan Elias * Written by Ilan Elias <ilane@ti.com> 96a2968aaSIlan Elias * 106a2968aaSIlan Elias * Acknowledgements: 116a2968aaSIlan Elias * This file is based on hci_core.c, which was written 126a2968aaSIlan Elias * by Maxim Krasnyansky. 136a2968aaSIlan Elias * 146a2968aaSIlan Elias * This program is free software; you can redistribute it and/or modify 156a2968aaSIlan Elias * it under the terms of the GNU General Public License version 2 166a2968aaSIlan Elias * as published by the Free Software Foundation 176a2968aaSIlan Elias * 186a2968aaSIlan Elias * This program is distributed in the hope that it will be useful, 196a2968aaSIlan Elias * but WITHOUT ANY WARRANTY; without even the implied warranty of 206a2968aaSIlan Elias * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 216a2968aaSIlan Elias * GNU General Public License for more details. 226a2968aaSIlan Elias * 236a2968aaSIlan Elias * You should have received a copy of the GNU General Public License 2498b32decSJeff Kirsher * along with this program; if not, see <http://www.gnu.org/licenses/>. 256a2968aaSIlan Elias * 266a2968aaSIlan Elias */ 276a2968aaSIlan Elias 2852858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ 29ed1e0ad8SJoe Perches 308a70e7f8SDave Jones #include <linux/module.h> 31b6355e97SSamuel Ortiz #include <linux/kernel.h> 326a2968aaSIlan Elias #include <linux/types.h> 336a2968aaSIlan Elias #include <linux/workqueue.h> 346a2968aaSIlan Elias #include <linux/completion.h> 35bc3b2d7fSPaul Gortmaker #include <linux/export.h> 366a2968aaSIlan Elias #include <linux/sched.h> 376a2968aaSIlan Elias #include <linux/bitops.h> 386a2968aaSIlan Elias #include <linux/skbuff.h> 396a2968aaSIlan Elias 406a2968aaSIlan Elias #include "../nfc.h" 416a2968aaSIlan Elias #include <net/nfc/nci.h> 426a2968aaSIlan Elias #include <net/nfc/nci_core.h> 436a2968aaSIlan Elias #include <linux/nfc.h> 446a2968aaSIlan Elias 45b16ae716SChristophe Ricard struct core_conn_create_data { 46b16ae716SChristophe Ricard int length; 47b16ae716SChristophe Ricard struct nci_core_conn_create_cmd *cmd; 48b16ae716SChristophe Ricard }; 49b16ae716SChristophe Ricard 506a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work); 516a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work); 526a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work); 536a2968aaSIlan Elias 544aeee687SChristophe Ricard struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, 554aeee687SChristophe Ricard int conn_id) 564aeee687SChristophe Ricard { 574aeee687SChristophe Ricard struct nci_conn_info *conn_info; 584aeee687SChristophe Ricard 594aeee687SChristophe Ricard list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 604aeee687SChristophe Ricard if (conn_info->conn_id == conn_id) 614aeee687SChristophe Ricard return conn_info; 624aeee687SChristophe Ricard } 634aeee687SChristophe Ricard 644aeee687SChristophe Ricard return NULL; 654aeee687SChristophe Ricard } 664aeee687SChristophe Ricard 679b8d1a4cSChristophe Ricard int nci_get_conn_info_by_dest_type_params(struct nci_dev *ndev, u8 dest_type, 689b8d1a4cSChristophe Ricard struct dest_spec_params *params) 6985b9ce9aSRobert Dolca { 7085b9ce9aSRobert Dolca struct nci_conn_info *conn_info; 7185b9ce9aSRobert Dolca 7285b9ce9aSRobert Dolca list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 739b8d1a4cSChristophe Ricard if (conn_info->dest_type == dest_type) { 749b8d1a4cSChristophe Ricard if (!params) 7585b9ce9aSRobert Dolca return conn_info->conn_id; 769b8d1a4cSChristophe Ricard if (conn_info) { 779b8d1a4cSChristophe Ricard if (params->id == conn_info->dest_params->id && 789b8d1a4cSChristophe Ricard params->protocol == conn_info->dest_params->protocol) 799b8d1a4cSChristophe Ricard return conn_info->conn_id; 809b8d1a4cSChristophe Ricard } 819b8d1a4cSChristophe Ricard } 8285b9ce9aSRobert Dolca } 8385b9ce9aSRobert Dolca 8485b9ce9aSRobert Dolca return -EINVAL; 8585b9ce9aSRobert Dolca } 869b8d1a4cSChristophe Ricard EXPORT_SYMBOL(nci_get_conn_info_by_dest_type_params); 8785b9ce9aSRobert Dolca 886a2968aaSIlan Elias /* ---- NCI requests ---- */ 896a2968aaSIlan Elias 906a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result) 916a2968aaSIlan Elias { 926a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 936a2968aaSIlan Elias ndev->req_result = result; 946a2968aaSIlan Elias ndev->req_status = NCI_REQ_DONE; 956a2968aaSIlan Elias complete(&ndev->req_completion); 966a2968aaSIlan Elias } 976a2968aaSIlan Elias } 982df7f8c6SSamuel Ortiz EXPORT_SYMBOL(nci_req_complete); 996a2968aaSIlan Elias 1006a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err) 1016a2968aaSIlan Elias { 1026a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 1036a2968aaSIlan Elias ndev->req_result = err; 1046a2968aaSIlan Elias ndev->req_status = NCI_REQ_CANCELED; 1056a2968aaSIlan Elias complete(&ndev->req_completion); 1066a2968aaSIlan Elias } 1076a2968aaSIlan Elias } 1086a2968aaSIlan Elias 1096a2968aaSIlan Elias /* Execute request and wait for completion. */ 1106a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev, 1116a2968aaSIlan Elias void (*req)(struct nci_dev *ndev, unsigned long opt), 112eb9bc6e9SSamuel Ortiz unsigned long opt, __u32 timeout) 1136a2968aaSIlan Elias { 1146a2968aaSIlan Elias int rc = 0; 115f8c141c3SDan Carpenter long completion_rc; 1166a2968aaSIlan Elias 1176a2968aaSIlan Elias ndev->req_status = NCI_REQ_PEND; 1186a2968aaSIlan Elias 1199bec44bfSAxel Lin reinit_completion(&ndev->req_completion); 1206a2968aaSIlan Elias req(ndev, opt); 121eb9bc6e9SSamuel Ortiz completion_rc = 122eb9bc6e9SSamuel Ortiz wait_for_completion_interruptible_timeout(&ndev->req_completion, 1236a2968aaSIlan Elias timeout); 1246a2968aaSIlan Elias 12520c239c1SJoe Perches pr_debug("wait_for_completion return %ld\n", completion_rc); 1266a2968aaSIlan Elias 1276a2968aaSIlan Elias if (completion_rc > 0) { 1286a2968aaSIlan Elias switch (ndev->req_status) { 1296a2968aaSIlan Elias case NCI_REQ_DONE: 1306a2968aaSIlan Elias rc = nci_to_errno(ndev->req_result); 1316a2968aaSIlan Elias break; 1326a2968aaSIlan Elias 1336a2968aaSIlan Elias case NCI_REQ_CANCELED: 1346a2968aaSIlan Elias rc = -ndev->req_result; 1356a2968aaSIlan Elias break; 1366a2968aaSIlan Elias 1376a2968aaSIlan Elias default: 1386a2968aaSIlan Elias rc = -ETIMEDOUT; 1396a2968aaSIlan Elias break; 1406a2968aaSIlan Elias } 1416a2968aaSIlan Elias } else { 142ed1e0ad8SJoe Perches pr_err("wait_for_completion_interruptible_timeout failed %ld\n", 1436a2968aaSIlan Elias completion_rc); 1446a2968aaSIlan Elias 1456a2968aaSIlan Elias rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); 1466a2968aaSIlan Elias } 1476a2968aaSIlan Elias 1486a2968aaSIlan Elias ndev->req_status = ndev->req_result = 0; 1496a2968aaSIlan Elias 1506a2968aaSIlan Elias return rc; 1516a2968aaSIlan Elias } 1526a2968aaSIlan Elias 15311f54f22SChristophe Ricard inline int nci_request(struct nci_dev *ndev, 154eb9bc6e9SSamuel Ortiz void (*req)(struct nci_dev *ndev, 155eb9bc6e9SSamuel Ortiz unsigned long opt), 1566a2968aaSIlan Elias unsigned long opt, __u32 timeout) 1576a2968aaSIlan Elias { 1586a2968aaSIlan Elias int rc; 1596a2968aaSIlan Elias 1606a2968aaSIlan Elias if (!test_bit(NCI_UP, &ndev->flags)) 1616a2968aaSIlan Elias return -ENETDOWN; 1626a2968aaSIlan Elias 1636a2968aaSIlan Elias /* Serialize all requests */ 1646a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 1656a2968aaSIlan Elias rc = __nci_request(ndev, req, opt, timeout); 1666a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 1676a2968aaSIlan Elias 1686a2968aaSIlan Elias return rc; 1696a2968aaSIlan Elias } 1706a2968aaSIlan Elias 1716a2968aaSIlan Elias static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) 1726a2968aaSIlan Elias { 173e8c0dacdSIlan Elias struct nci_core_reset_cmd cmd; 174e8c0dacdSIlan Elias 175e8c0dacdSIlan Elias cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; 176e8c0dacdSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); 1776a2968aaSIlan Elias } 1786a2968aaSIlan Elias 1796a2968aaSIlan Elias static void nci_init_req(struct nci_dev *ndev, unsigned long opt) 1806a2968aaSIlan Elias { 1816a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1826a2968aaSIlan Elias } 1836a2968aaSIlan Elias 1846a2968aaSIlan Elias static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) 1856a2968aaSIlan Elias { 1862eb1dc10SIlan Elias struct nci_rf_disc_map_cmd cmd; 1872eb1dc10SIlan Elias struct disc_map_config *cfg = cmd.mapping_configs; 1882eb1dc10SIlan Elias __u8 *num = &cmd.num_mapping_configs; 1896a2968aaSIlan Elias int i; 1906a2968aaSIlan Elias 1916a2968aaSIlan Elias /* set rf mapping configurations */ 1922eb1dc10SIlan Elias *num = 0; 1936a2968aaSIlan Elias 1946a2968aaSIlan Elias /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ 1956a2968aaSIlan Elias for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { 1966a2968aaSIlan Elias if (ndev->supported_rf_interfaces[i] == 1976a2968aaSIlan Elias NCI_RF_INTERFACE_ISO_DEP) { 1982eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 199637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 200637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 201637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; 2022eb1dc10SIlan Elias (*num)++; 2036a2968aaSIlan Elias } else if (ndev->supported_rf_interfaces[i] == 2046a2968aaSIlan Elias NCI_RF_INTERFACE_NFC_DEP) { 2052eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 206637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 207637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 208637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP; 2092eb1dc10SIlan Elias (*num)++; 2106a2968aaSIlan Elias } 2116a2968aaSIlan Elias 2122eb1dc10SIlan Elias if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) 2136a2968aaSIlan Elias break; 2146a2968aaSIlan Elias } 2156a2968aaSIlan Elias 2166a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, 217eb9bc6e9SSamuel Ortiz (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); 2186a2968aaSIlan Elias } 2196a2968aaSIlan Elias 2207e035230SIlan Elias struct nci_set_config_param { 2217e035230SIlan Elias __u8 id; 2227e035230SIlan Elias size_t len; 2237e035230SIlan Elias __u8 *val; 2247e035230SIlan Elias }; 2257e035230SIlan Elias 2267e035230SIlan Elias static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) 2277e035230SIlan Elias { 2287e035230SIlan Elias struct nci_set_config_param *param = (struct nci_set_config_param *)opt; 2297e035230SIlan Elias struct nci_core_set_config_cmd cmd; 2307e035230SIlan Elias 2317e035230SIlan Elias BUG_ON(param->len > NCI_MAX_PARAM_LEN); 2327e035230SIlan Elias 2337e035230SIlan Elias cmd.num_params = 1; 2347e035230SIlan Elias cmd.param.id = param->id; 2357e035230SIlan Elias cmd.param.len = param->len; 2367e035230SIlan Elias memcpy(cmd.param.val, param->val, param->len); 2377e035230SIlan Elias 2387e035230SIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); 2397e035230SIlan Elias } 2407e035230SIlan Elias 241772dccf4SJulien Lefrique struct nci_rf_discover_param { 242772dccf4SJulien Lefrique __u32 im_protocols; 243772dccf4SJulien Lefrique __u32 tm_protocols; 244772dccf4SJulien Lefrique }; 245772dccf4SJulien Lefrique 2466a2968aaSIlan Elias static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) 2476a2968aaSIlan Elias { 248772dccf4SJulien Lefrique struct nci_rf_discover_param *param = 249772dccf4SJulien Lefrique (struct nci_rf_discover_param *)opt; 2506a2968aaSIlan Elias struct nci_rf_disc_cmd cmd; 2516a2968aaSIlan Elias 2526a2968aaSIlan Elias cmd.num_disc_configs = 0; 2536a2968aaSIlan Elias 2546a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 255772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_JEWEL_MASK || 256772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_MIFARE_MASK || 257772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_ISO14443_MASK || 258772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 259637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 260637d85a7SIlan Elias NCI_NFC_A_PASSIVE_POLL_MODE; 2616a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2626a2968aaSIlan Elias cmd.num_disc_configs++; 2636a2968aaSIlan Elias } 2646a2968aaSIlan Elias 2656a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 266772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { 267637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 268637d85a7SIlan Elias NCI_NFC_B_PASSIVE_POLL_MODE; 2696a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2706a2968aaSIlan Elias cmd.num_disc_configs++; 2716a2968aaSIlan Elias } 2726a2968aaSIlan Elias 2736a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 274772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_FELICA_MASK || 275772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 276637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 277637d85a7SIlan Elias NCI_NFC_F_PASSIVE_POLL_MODE; 2786a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2796a2968aaSIlan Elias cmd.num_disc_configs++; 2806a2968aaSIlan Elias } 2816a2968aaSIlan Elias 282cfdbeeafSVincent Cuissard if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 283772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { 284cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 285cfdbeeafSVincent Cuissard NCI_NFC_V_PASSIVE_POLL_MODE; 286cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 287cfdbeeafSVincent Cuissard cmd.num_disc_configs++; 288cfdbeeafSVincent Cuissard } 289cfdbeeafSVincent Cuissard 290772dccf4SJulien Lefrique if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && 291772dccf4SJulien Lefrique (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { 292772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 293772dccf4SJulien Lefrique NCI_NFC_A_PASSIVE_LISTEN_MODE; 294772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 295772dccf4SJulien Lefrique cmd.num_disc_configs++; 296772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 297772dccf4SJulien Lefrique NCI_NFC_F_PASSIVE_LISTEN_MODE; 298772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 299772dccf4SJulien Lefrique cmd.num_disc_configs++; 300772dccf4SJulien Lefrique } 301772dccf4SJulien Lefrique 3026a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, 3036a2968aaSIlan Elias (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), 3046a2968aaSIlan Elias &cmd); 3056a2968aaSIlan Elias } 3066a2968aaSIlan Elias 307019c4fbaSIlan Elias struct nci_rf_discover_select_param { 308019c4fbaSIlan Elias __u8 rf_discovery_id; 309019c4fbaSIlan Elias __u8 rf_protocol; 310019c4fbaSIlan Elias }; 311019c4fbaSIlan Elias 312019c4fbaSIlan Elias static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) 313019c4fbaSIlan Elias { 314019c4fbaSIlan Elias struct nci_rf_discover_select_param *param = 315019c4fbaSIlan Elias (struct nci_rf_discover_select_param *)opt; 316019c4fbaSIlan Elias struct nci_rf_discover_select_cmd cmd; 317019c4fbaSIlan Elias 318019c4fbaSIlan Elias cmd.rf_discovery_id = param->rf_discovery_id; 319019c4fbaSIlan Elias cmd.rf_protocol = param->rf_protocol; 320019c4fbaSIlan Elias 321019c4fbaSIlan Elias switch (cmd.rf_protocol) { 322019c4fbaSIlan Elias case NCI_RF_PROTOCOL_ISO_DEP: 323019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; 324019c4fbaSIlan Elias break; 325019c4fbaSIlan Elias 326019c4fbaSIlan Elias case NCI_RF_PROTOCOL_NFC_DEP: 327019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; 328019c4fbaSIlan Elias break; 329019c4fbaSIlan Elias 330019c4fbaSIlan Elias default: 331019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_FRAME; 332019c4fbaSIlan Elias break; 333019c4fbaSIlan Elias } 334019c4fbaSIlan Elias 335019c4fbaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, 336eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_discover_select_cmd), &cmd); 337019c4fbaSIlan Elias } 338019c4fbaSIlan Elias 3396a2968aaSIlan Elias static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) 3406a2968aaSIlan Elias { 3416a2968aaSIlan Elias struct nci_rf_deactivate_cmd cmd; 3426a2968aaSIlan Elias 3439295b5b5SChristophe Ricard cmd.type = opt; 3446a2968aaSIlan Elias 3456a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, 346eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_deactivate_cmd), &cmd); 3476a2968aaSIlan Elias } 3486a2968aaSIlan Elias 3497bc4824eSRobert Dolca struct nci_cmd_param { 350759afb8dSChristophe Ricard __u16 opcode; 351759afb8dSChristophe Ricard size_t len; 352759afb8dSChristophe Ricard __u8 *payload; 353759afb8dSChristophe Ricard }; 354759afb8dSChristophe Ricard 3557bc4824eSRobert Dolca static void nci_generic_req(struct nci_dev *ndev, unsigned long opt) 356759afb8dSChristophe Ricard { 3577bc4824eSRobert Dolca struct nci_cmd_param *param = 3587bc4824eSRobert Dolca (struct nci_cmd_param *)opt; 359759afb8dSChristophe Ricard 360759afb8dSChristophe Ricard nci_send_cmd(ndev, param->opcode, param->len, param->payload); 361759afb8dSChristophe Ricard } 362759afb8dSChristophe Ricard 363759afb8dSChristophe Ricard int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload) 364759afb8dSChristophe Ricard { 3657bc4824eSRobert Dolca struct nci_cmd_param param; 366759afb8dSChristophe Ricard 367759afb8dSChristophe Ricard param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid); 368759afb8dSChristophe Ricard param.len = len; 369759afb8dSChristophe Ricard param.payload = payload; 370759afb8dSChristophe Ricard 3717bc4824eSRobert Dolca return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, 372759afb8dSChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 373759afb8dSChristophe Ricard } 374759afb8dSChristophe Ricard EXPORT_SYMBOL(nci_prop_cmd); 375759afb8dSChristophe Ricard 3767bc4824eSRobert Dolca int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload) 3777bc4824eSRobert Dolca { 3787bc4824eSRobert Dolca struct nci_cmd_param param; 3797bc4824eSRobert Dolca 3807bc4824eSRobert Dolca param.opcode = opcode; 3817bc4824eSRobert Dolca param.len = len; 3827bc4824eSRobert Dolca param.payload = payload; 3837bc4824eSRobert Dolca 3847bc4824eSRobert Dolca return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, 3857bc4824eSRobert Dolca msecs_to_jiffies(NCI_CMD_TIMEOUT)); 3867bc4824eSRobert Dolca } 3877bc4824eSRobert Dolca EXPORT_SYMBOL(nci_core_cmd); 3887bc4824eSRobert Dolca 389025a0cb8SRobert Baldyga int nci_core_reset(struct nci_dev *ndev) 390025a0cb8SRobert Baldyga { 391025a0cb8SRobert Baldyga return __nci_request(ndev, nci_reset_req, 0, 392025a0cb8SRobert Baldyga msecs_to_jiffies(NCI_RESET_TIMEOUT)); 393025a0cb8SRobert Baldyga } 394025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_reset); 395025a0cb8SRobert Baldyga 396025a0cb8SRobert Baldyga int nci_core_init(struct nci_dev *ndev) 397025a0cb8SRobert Baldyga { 398025a0cb8SRobert Baldyga return __nci_request(ndev, nci_init_req, 0, 399025a0cb8SRobert Baldyga msecs_to_jiffies(NCI_INIT_TIMEOUT)); 400025a0cb8SRobert Baldyga } 401025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_init); 402025a0cb8SRobert Baldyga 4031c53855fSChristophe Ricard struct nci_loopback_data { 4041c53855fSChristophe Ricard u8 conn_id; 4051c53855fSChristophe Ricard struct sk_buff *data; 4061c53855fSChristophe Ricard }; 4071c53855fSChristophe Ricard 4081c53855fSChristophe Ricard static void nci_send_data_req(struct nci_dev *ndev, unsigned long opt) 4091c53855fSChristophe Ricard { 4101c53855fSChristophe Ricard struct nci_loopback_data *data = (struct nci_loopback_data *)opt; 4111c53855fSChristophe Ricard 4121c53855fSChristophe Ricard nci_send_data(ndev, data->conn_id, data->data); 4131c53855fSChristophe Ricard } 4141c53855fSChristophe Ricard 4151c53855fSChristophe Ricard static void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err) 4161c53855fSChristophe Ricard { 4171c53855fSChristophe Ricard struct nci_dev *ndev = (struct nci_dev *)context; 4181c53855fSChristophe Ricard struct nci_conn_info *conn_info; 4191c53855fSChristophe Ricard 4201c53855fSChristophe Ricard conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); 4211c53855fSChristophe Ricard if (!conn_info) { 4221c53855fSChristophe Ricard nci_req_complete(ndev, NCI_STATUS_REJECTED); 4231c53855fSChristophe Ricard return; 4241c53855fSChristophe Ricard } 4251c53855fSChristophe Ricard 4261c53855fSChristophe Ricard conn_info->rx_skb = skb; 4271c53855fSChristophe Ricard 4281c53855fSChristophe Ricard nci_req_complete(ndev, NCI_STATUS_OK); 4291c53855fSChristophe Ricard } 4301c53855fSChristophe Ricard 4311c53855fSChristophe Ricard int nci_nfcc_loopback(struct nci_dev *ndev, void *data, size_t data_len, 4321c53855fSChristophe Ricard struct sk_buff **resp) 4331c53855fSChristophe Ricard { 4341c53855fSChristophe Ricard int r; 4351c53855fSChristophe Ricard struct nci_loopback_data loopback_data; 4361c53855fSChristophe Ricard struct nci_conn_info *conn_info; 4371c53855fSChristophe Ricard struct sk_buff *skb; 4381c53855fSChristophe Ricard int conn_id = nci_get_conn_info_by_dest_type_params(ndev, 4391c53855fSChristophe Ricard NCI_DESTINATION_NFCC_LOOPBACK, NULL); 4401c53855fSChristophe Ricard 4411c53855fSChristophe Ricard if (conn_id < 0) { 4421c53855fSChristophe Ricard r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK, 4431c53855fSChristophe Ricard 0, 0, NULL); 4441c53855fSChristophe Ricard if (r != NCI_STATUS_OK) 4451c53855fSChristophe Ricard return r; 4461c53855fSChristophe Ricard 4471c53855fSChristophe Ricard conn_id = nci_get_conn_info_by_dest_type_params(ndev, 4481c53855fSChristophe Ricard NCI_DESTINATION_NFCC_LOOPBACK, 4491c53855fSChristophe Ricard NULL); 4501c53855fSChristophe Ricard } 4511c53855fSChristophe Ricard 4521c53855fSChristophe Ricard conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); 4531c53855fSChristophe Ricard if (!conn_info) 4541c53855fSChristophe Ricard return -EPROTO; 4551c53855fSChristophe Ricard 4561c53855fSChristophe Ricard /* store cb and context to be used on receiving data */ 4571c53855fSChristophe Ricard conn_info->data_exchange_cb = nci_nfcc_loopback_cb; 4581c53855fSChristophe Ricard conn_info->data_exchange_cb_context = ndev; 4591c53855fSChristophe Ricard 4601c53855fSChristophe Ricard skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL); 4611c53855fSChristophe Ricard if (!skb) 4621c53855fSChristophe Ricard return -ENOMEM; 4631c53855fSChristophe Ricard 4641c53855fSChristophe Ricard skb_reserve(skb, NCI_DATA_HDR_SIZE); 46559ae1d12SJohannes Berg skb_put_data(skb, data, data_len); 4661c53855fSChristophe Ricard 4671c53855fSChristophe Ricard loopback_data.conn_id = conn_id; 4681c53855fSChristophe Ricard loopback_data.data = skb; 4691c53855fSChristophe Ricard 4701c53855fSChristophe Ricard ndev->cur_conn_id = conn_id; 4711c53855fSChristophe Ricard r = nci_request(ndev, nci_send_data_req, (unsigned long)&loopback_data, 4721c53855fSChristophe Ricard msecs_to_jiffies(NCI_DATA_TIMEOUT)); 4731c53855fSChristophe Ricard if (r == NCI_STATUS_OK && resp) 4741c53855fSChristophe Ricard *resp = conn_info->rx_skb; 4751c53855fSChristophe Ricard 4761c53855fSChristophe Ricard return r; 4771c53855fSChristophe Ricard } 4781c53855fSChristophe Ricard EXPORT_SYMBOL(nci_nfcc_loopback); 4791c53855fSChristophe Ricard 4806a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev) 4816a2968aaSIlan Elias { 4826a2968aaSIlan Elias int rc = 0; 4836a2968aaSIlan Elias 4846a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 4856a2968aaSIlan Elias 4866a2968aaSIlan Elias if (test_bit(NCI_UP, &ndev->flags)) { 4876a2968aaSIlan Elias rc = -EALREADY; 4886a2968aaSIlan Elias goto done; 4896a2968aaSIlan Elias } 4906a2968aaSIlan Elias 4916a2968aaSIlan Elias if (ndev->ops->open(ndev)) { 4926a2968aaSIlan Elias rc = -EIO; 4936a2968aaSIlan Elias goto done; 4946a2968aaSIlan Elias } 4956a2968aaSIlan Elias 4966a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 4976a2968aaSIlan Elias 4986a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 4996a2968aaSIlan Elias 500c39daeeeSChristophe Ricard if (ndev->ops->init) 501c39daeeeSChristophe Ricard rc = ndev->ops->init(ndev); 502c39daeeeSChristophe Ricard 503c39daeeeSChristophe Ricard if (!rc) { 5046a2968aaSIlan Elias rc = __nci_request(ndev, nci_reset_req, 0, 5056a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 506c39daeeeSChristophe Ricard } 5076a2968aaSIlan Elias 50881859ab8SChristophe Ricard if (!rc && ndev->ops->setup) { 50981859ab8SChristophe Ricard rc = ndev->ops->setup(ndev); 51081859ab8SChristophe Ricard } 51186e8586eSAmitkumar Karwar 5126a2968aaSIlan Elias if (!rc) { 5136a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_req, 0, 5146a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 5156a2968aaSIlan Elias } 5166a2968aaSIlan Elias 517e4dbd625SRobert Dolca if (!rc && ndev->ops->post_setup) 518fdf79bd4SRobert Baldyga rc = ndev->ops->post_setup(ndev); 519fdf79bd4SRobert Baldyga 5206a2968aaSIlan Elias if (!rc) { 5216a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_complete_req, 0, 5226a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 5236a2968aaSIlan Elias } 5246a2968aaSIlan Elias 5256a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 5266a2968aaSIlan Elias 5276a2968aaSIlan Elias if (!rc) { 5286a2968aaSIlan Elias set_bit(NCI_UP, &ndev->flags); 529019c4fbaSIlan Elias nci_clear_target_list(ndev); 5308939e47fSIlan Elias atomic_set(&ndev->state, NCI_IDLE); 5316a2968aaSIlan Elias } else { 5326a2968aaSIlan Elias /* Init failed, cleanup */ 5336a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 5346a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 5356a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 5366a2968aaSIlan Elias 5376a2968aaSIlan Elias ndev->ops->close(ndev); 5386a2968aaSIlan Elias ndev->flags = 0; 5396a2968aaSIlan Elias } 5406a2968aaSIlan Elias 5416a2968aaSIlan Elias done: 5426a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 5436a2968aaSIlan Elias return rc; 5446a2968aaSIlan Elias } 5456a2968aaSIlan Elias 5466a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev) 5476a2968aaSIlan Elias { 5486a2968aaSIlan Elias nci_req_cancel(ndev, ENODEV); 5496a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 5506a2968aaSIlan Elias 5516a2968aaSIlan Elias if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { 5526a2968aaSIlan Elias del_timer_sync(&ndev->cmd_timer); 553c4bf98b2SIlan Elias del_timer_sync(&ndev->data_timer); 5546a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 5556a2968aaSIlan Elias return 0; 5566a2968aaSIlan Elias } 5576a2968aaSIlan Elias 5586a2968aaSIlan Elias /* Drop RX and TX queues */ 5596a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 5606a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 5616a2968aaSIlan Elias 5626a2968aaSIlan Elias /* Flush RX and TX wq */ 5636a2968aaSIlan Elias flush_workqueue(ndev->rx_wq); 5646a2968aaSIlan Elias flush_workqueue(ndev->tx_wq); 5656a2968aaSIlan Elias 5666a2968aaSIlan Elias /* Reset device */ 5676a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 5686a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 5696a2968aaSIlan Elias 5706a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 5716a2968aaSIlan Elias __nci_request(ndev, nci_reset_req, 0, 5726a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 5730e70cba7SChristophe Ricard 5740e70cba7SChristophe Ricard /* After this point our queues are empty 5750e70cba7SChristophe Ricard * and no works are scheduled. 5760e70cba7SChristophe Ricard */ 5770e70cba7SChristophe Ricard ndev->ops->close(ndev); 5780e70cba7SChristophe Ricard 5796a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 5806a2968aaSIlan Elias 581fa9be5f0SAmitkumar Karwar del_timer_sync(&ndev->cmd_timer); 582fa9be5f0SAmitkumar Karwar 5836a2968aaSIlan Elias /* Flush cmd wq */ 5846a2968aaSIlan Elias flush_workqueue(ndev->cmd_wq); 5856a2968aaSIlan Elias 5866a2968aaSIlan Elias /* Clear flags */ 5876a2968aaSIlan Elias ndev->flags = 0; 5886a2968aaSIlan Elias 5896a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 5906a2968aaSIlan Elias 5916a2968aaSIlan Elias return 0; 5926a2968aaSIlan Elias } 5936a2968aaSIlan Elias 5946a2968aaSIlan Elias /* NCI command timer function */ 5956a2968aaSIlan Elias static void nci_cmd_timer(unsigned long arg) 5966a2968aaSIlan Elias { 5976a2968aaSIlan Elias struct nci_dev *ndev = (void *) arg; 5986a2968aaSIlan Elias 5996a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 6006a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 6016a2968aaSIlan Elias } 6026a2968aaSIlan Elias 603c4bf98b2SIlan Elias /* NCI data exchange timer function */ 604c4bf98b2SIlan Elias static void nci_data_timer(unsigned long arg) 605c4bf98b2SIlan Elias { 606c4bf98b2SIlan Elias struct nci_dev *ndev = (void *) arg; 607c4bf98b2SIlan Elias 608c4bf98b2SIlan Elias set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 609c4bf98b2SIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 610c4bf98b2SIlan Elias } 611c4bf98b2SIlan Elias 6126a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev) 6136a2968aaSIlan Elias { 6146a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6156a2968aaSIlan Elias 6166a2968aaSIlan Elias return nci_open_device(ndev); 6176a2968aaSIlan Elias } 6186a2968aaSIlan Elias 6196a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev) 6206a2968aaSIlan Elias { 6216a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6226a2968aaSIlan Elias 6236a2968aaSIlan Elias return nci_close_device(ndev); 6246a2968aaSIlan Elias } 6256a2968aaSIlan Elias 62622c15bf3SAmitkumar Karwar int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) 62722c15bf3SAmitkumar Karwar { 62822c15bf3SAmitkumar Karwar struct nci_set_config_param param; 62922c15bf3SAmitkumar Karwar 63022c15bf3SAmitkumar Karwar if (!val || !len) 63122c15bf3SAmitkumar Karwar return 0; 63222c15bf3SAmitkumar Karwar 63322c15bf3SAmitkumar Karwar param.id = id; 63422c15bf3SAmitkumar Karwar param.len = len; 63522c15bf3SAmitkumar Karwar param.val = val; 63622c15bf3SAmitkumar Karwar 63722c15bf3SAmitkumar Karwar return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 63822c15bf3SAmitkumar Karwar msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 63922c15bf3SAmitkumar Karwar } 64022c15bf3SAmitkumar Karwar EXPORT_SYMBOL(nci_set_config); 64122c15bf3SAmitkumar Karwar 642af9c8aa6SChristophe Ricard static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt) 643af9c8aa6SChristophe Ricard { 644af9c8aa6SChristophe Ricard struct nci_nfcee_discover_cmd cmd; 645af9c8aa6SChristophe Ricard __u8 action = opt; 646af9c8aa6SChristophe Ricard 647af9c8aa6SChristophe Ricard cmd.discovery_action = action; 648af9c8aa6SChristophe Ricard 649af9c8aa6SChristophe Ricard nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd); 650af9c8aa6SChristophe Ricard } 651af9c8aa6SChristophe Ricard 652af9c8aa6SChristophe Ricard int nci_nfcee_discover(struct nci_dev *ndev, u8 action) 653af9c8aa6SChristophe Ricard { 65421d19f87SSamuel Ortiz return __nci_request(ndev, nci_nfcee_discover_req, action, 655af9c8aa6SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 656af9c8aa6SChristophe Ricard } 657af9c8aa6SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_discover); 658af9c8aa6SChristophe Ricard 659f7f793f3SChristophe Ricard static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt) 660f7f793f3SChristophe Ricard { 661f7f793f3SChristophe Ricard struct nci_nfcee_mode_set_cmd *cmd = 662f7f793f3SChristophe Ricard (struct nci_nfcee_mode_set_cmd *)opt; 663f7f793f3SChristophe Ricard 664f7f793f3SChristophe Ricard nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD, 665f7f793f3SChristophe Ricard sizeof(struct nci_nfcee_mode_set_cmd), cmd); 666f7f793f3SChristophe Ricard } 667f7f793f3SChristophe Ricard 668f7f793f3SChristophe Ricard int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) 669f7f793f3SChristophe Ricard { 670f7f793f3SChristophe Ricard struct nci_nfcee_mode_set_cmd cmd; 671f7f793f3SChristophe Ricard 672f7f793f3SChristophe Ricard cmd.nfcee_id = nfcee_id; 673f7f793f3SChristophe Ricard cmd.nfcee_mode = nfcee_mode; 674f7f793f3SChristophe Ricard 67521d19f87SSamuel Ortiz return __nci_request(ndev, nci_nfcee_mode_set_req, 67621d19f87SSamuel Ortiz (unsigned long)&cmd, 677f7f793f3SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 678f7f793f3SChristophe Ricard } 679f7f793f3SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_mode_set); 680f7f793f3SChristophe Ricard 681736bb957SChristophe Ricard static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt) 682736bb957SChristophe Ricard { 683b16ae716SChristophe Ricard struct core_conn_create_data *data = 684b16ae716SChristophe Ricard (struct core_conn_create_data *)opt; 685736bb957SChristophe Ricard 686b16ae716SChristophe Ricard nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd); 687736bb957SChristophe Ricard } 688736bb957SChristophe Ricard 689b16ae716SChristophe Ricard int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type, 690b16ae716SChristophe Ricard u8 number_destination_params, 691b16ae716SChristophe Ricard size_t params_len, 692736bb957SChristophe Ricard struct core_conn_create_dest_spec_params *params) 693736bb957SChristophe Ricard { 694b16ae716SChristophe Ricard int r; 695b16ae716SChristophe Ricard struct nci_core_conn_create_cmd *cmd; 696b16ae716SChristophe Ricard struct core_conn_create_data data; 697b16ae716SChristophe Ricard 698b16ae716SChristophe Ricard data.length = params_len + sizeof(struct nci_core_conn_create_cmd); 699b16ae716SChristophe Ricard cmd = kzalloc(data.length, GFP_KERNEL); 700b16ae716SChristophe Ricard if (!cmd) 701b16ae716SChristophe Ricard return -ENOMEM; 702b16ae716SChristophe Ricard 703b16ae716SChristophe Ricard cmd->destination_type = destination_type; 704b16ae716SChristophe Ricard cmd->number_destination_params = number_destination_params; 705b16ae716SChristophe Ricard 706b16ae716SChristophe Ricard data.cmd = cmd; 707caa575a8SRobert Dolca 70818836029SChristophe Ricard if (params) { 70918836029SChristophe Ricard memcpy(cmd->params, params, params_len); 710caa575a8SRobert Dolca if (params->length > 0) 7119b8d1a4cSChristophe Ricard memcpy(&ndev->cur_params, 7129b8d1a4cSChristophe Ricard ¶ms->value[DEST_SPEC_PARAMS_ID_INDEX], 7139b8d1a4cSChristophe Ricard sizeof(struct dest_spec_params)); 714caa575a8SRobert Dolca else 7159b8d1a4cSChristophe Ricard ndev->cur_params.id = 0; 71618836029SChristophe Ricard } else { 7179b8d1a4cSChristophe Ricard ndev->cur_params.id = 0; 71818836029SChristophe Ricard } 7199b8d1a4cSChristophe Ricard ndev->cur_dest_type = destination_type; 720b16ae716SChristophe Ricard 72118836029SChristophe Ricard r = __nci_request(ndev, nci_core_conn_create_req, (unsigned long)&data, 722736bb957SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 723b16ae716SChristophe Ricard kfree(cmd); 724b16ae716SChristophe Ricard return r; 725736bb957SChristophe Ricard } 726736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_create); 727736bb957SChristophe Ricard 728736bb957SChristophe Ricard static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt) 729736bb957SChristophe Ricard { 730736bb957SChristophe Ricard __u8 conn_id = opt; 731736bb957SChristophe Ricard 732736bb957SChristophe Ricard nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id); 733736bb957SChristophe Ricard } 734736bb957SChristophe Ricard 735736bb957SChristophe Ricard int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) 736736bb957SChristophe Ricard { 737de5ea851SChristophe Ricard ndev->cur_conn_id = conn_id; 73821d19f87SSamuel Ortiz return __nci_request(ndev, nci_core_conn_close_req, conn_id, 739736bb957SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 740736bb957SChristophe Ricard } 741736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_close); 742736bb957SChristophe Ricard 7437e035230SIlan Elias static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) 7447e035230SIlan Elias { 7457e035230SIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 7467e035230SIlan Elias struct nci_set_config_param param; 747529ee066SJulien Lefrique int rc; 7487e035230SIlan Elias 7497e035230SIlan Elias param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); 7507e035230SIlan Elias if ((param.val == NULL) || (param.len == 0)) 751f9fc36f4SSzymon Janc return 0; 7527e035230SIlan Elias 753460d8f97SSzymon Janc if (param.len > NFC_MAX_GT_LEN) 7547e035230SIlan Elias return -EINVAL; 7557e035230SIlan Elias 7567e035230SIlan Elias param.id = NCI_PN_ATR_REQ_GEN_BYTES; 7577e035230SIlan Elias 758529ee066SJulien Lefrique rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 759529ee066SJulien Lefrique msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 760529ee066SJulien Lefrique if (rc) 761529ee066SJulien Lefrique return rc; 762529ee066SJulien Lefrique 763529ee066SJulien Lefrique param.id = NCI_LN_ATR_RES_GEN_BYTES; 764529ee066SJulien Lefrique 765f9fc36f4SSzymon Janc return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 7667e035230SIlan Elias msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 7677e035230SIlan Elias } 7687e035230SIlan Elias 76990d78c13SJulien Lefrique static int nci_set_listen_parameters(struct nfc_dev *nfc_dev) 77090d78c13SJulien Lefrique { 77190d78c13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 77290d78c13SJulien Lefrique int rc; 77390d78c13SJulien Lefrique __u8 val; 77490d78c13SJulien Lefrique 77590d78c13SJulien Lefrique val = NCI_LA_SEL_INFO_NFC_DEP_MASK; 77690d78c13SJulien Lefrique 77790d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); 77890d78c13SJulien Lefrique if (rc) 77990d78c13SJulien Lefrique return rc; 78090d78c13SJulien Lefrique 78190d78c13SJulien Lefrique val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; 78290d78c13SJulien Lefrique 78390d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); 78490d78c13SJulien Lefrique if (rc) 78590d78c13SJulien Lefrique return rc; 78690d78c13SJulien Lefrique 78790d78c13SJulien Lefrique val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; 78890d78c13SJulien Lefrique 78990d78c13SJulien Lefrique return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); 79090d78c13SJulien Lefrique } 79190d78c13SJulien Lefrique 792fe7c5800SSamuel Ortiz static int nci_start_poll(struct nfc_dev *nfc_dev, 793fe7c5800SSamuel Ortiz __u32 im_protocols, __u32 tm_protocols) 7946a2968aaSIlan Elias { 7956a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 796772dccf4SJulien Lefrique struct nci_rf_discover_param param; 7976a2968aaSIlan Elias int rc; 7986a2968aaSIlan Elias 799019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || 800019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { 801ed1e0ad8SJoe Perches pr_err("unable to start poll, since poll is already active\n"); 8026a2968aaSIlan Elias return -EBUSY; 8036a2968aaSIlan Elias } 8046a2968aaSIlan Elias 805de054799SIlan Elias if (ndev->target_active_prot) { 806ed1e0ad8SJoe Perches pr_err("there is an active target\n"); 807de054799SIlan Elias return -EBUSY; 808de054799SIlan Elias } 809de054799SIlan Elias 810019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || 811019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { 812019c4fbaSIlan Elias pr_debug("target active or w4 select, implicitly deactivate\n"); 8136a2968aaSIlan Elias 8149295b5b5SChristophe Ricard rc = nci_request(ndev, nci_rf_deactivate_req, 8159295b5b5SChristophe Ricard NCI_DEACTIVATE_TYPE_IDLE_MODE, 8166a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 8176a2968aaSIlan Elias if (rc) 8186a2968aaSIlan Elias return -EBUSY; 8196a2968aaSIlan Elias } 8206a2968aaSIlan Elias 821529ee066SJulien Lefrique if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { 8227e035230SIlan Elias rc = nci_set_local_general_bytes(nfc_dev); 8237e035230SIlan Elias if (rc) { 8247e035230SIlan Elias pr_err("failed to set local general bytes\n"); 8257e035230SIlan Elias return rc; 8267e035230SIlan Elias } 8277e035230SIlan Elias } 8287e035230SIlan Elias 82990d78c13SJulien Lefrique if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { 83090d78c13SJulien Lefrique rc = nci_set_listen_parameters(nfc_dev); 83190d78c13SJulien Lefrique if (rc) 83290d78c13SJulien Lefrique pr_err("failed to set listen parameters\n"); 83390d78c13SJulien Lefrique } 83490d78c13SJulien Lefrique 835772dccf4SJulien Lefrique param.im_protocols = im_protocols; 836772dccf4SJulien Lefrique param.tm_protocols = tm_protocols; 837772dccf4SJulien Lefrique rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m, 8386a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); 8396a2968aaSIlan Elias 8406a2968aaSIlan Elias if (!rc) 841fe7c5800SSamuel Ortiz ndev->poll_prots = im_protocols; 8426a2968aaSIlan Elias 8436a2968aaSIlan Elias return rc; 8446a2968aaSIlan Elias } 8456a2968aaSIlan Elias 8466a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev) 8476a2968aaSIlan Elias { 8486a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 8496a2968aaSIlan Elias 850019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && 851019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { 852ed1e0ad8SJoe Perches pr_err("unable to stop poll, since poll is not active\n"); 8536a2968aaSIlan Elias return; 8546a2968aaSIlan Elias } 8556a2968aaSIlan Elias 8569295b5b5SChristophe Ricard nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE, 8576a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 8586a2968aaSIlan Elias } 8596a2968aaSIlan Elias 86090099433SEric Lapuyade static int nci_activate_target(struct nfc_dev *nfc_dev, 86190099433SEric Lapuyade struct nfc_target *target, __u32 protocol) 8626a2968aaSIlan Elias { 8636a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 864019c4fbaSIlan Elias struct nci_rf_discover_select_param param; 86590099433SEric Lapuyade struct nfc_target *nci_target = NULL; 866019c4fbaSIlan Elias int i; 867019c4fbaSIlan Elias int rc = 0; 8686a2968aaSIlan Elias 86990099433SEric Lapuyade pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol); 8706a2968aaSIlan Elias 871019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && 872019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { 873ed1e0ad8SJoe Perches pr_err("there is no available target to activate\n"); 8746a2968aaSIlan Elias return -EINVAL; 8756a2968aaSIlan Elias } 8766a2968aaSIlan Elias 8776a2968aaSIlan Elias if (ndev->target_active_prot) { 878ed1e0ad8SJoe Perches pr_err("there is already an active target\n"); 8796a2968aaSIlan Elias return -EBUSY; 8806a2968aaSIlan Elias } 8816a2968aaSIlan Elias 882019c4fbaSIlan Elias for (i = 0; i < ndev->n_targets; i++) { 88390099433SEric Lapuyade if (ndev->targets[i].idx == target->idx) { 88490099433SEric Lapuyade nci_target = &ndev->targets[i]; 885019c4fbaSIlan Elias break; 886019c4fbaSIlan Elias } 887019c4fbaSIlan Elias } 888019c4fbaSIlan Elias 88990099433SEric Lapuyade if (!nci_target) { 890019c4fbaSIlan Elias pr_err("unable to find the selected target\n"); 891019c4fbaSIlan Elias return -EINVAL; 892019c4fbaSIlan Elias } 893019c4fbaSIlan Elias 89490099433SEric Lapuyade if (!(nci_target->supported_protocols & (1 << protocol))) { 895ed1e0ad8SJoe Perches pr_err("target does not support the requested protocol 0x%x\n", 8966a2968aaSIlan Elias protocol); 8976a2968aaSIlan Elias return -EINVAL; 8986a2968aaSIlan Elias } 8996a2968aaSIlan Elias 900019c4fbaSIlan Elias if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { 90190099433SEric Lapuyade param.rf_discovery_id = nci_target->logical_idx; 9026a2968aaSIlan Elias 903019c4fbaSIlan Elias if (protocol == NFC_PROTO_JEWEL) 904019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T1T; 905019c4fbaSIlan Elias else if (protocol == NFC_PROTO_MIFARE) 906019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T2T; 907019c4fbaSIlan Elias else if (protocol == NFC_PROTO_FELICA) 908019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T3T; 90901d719a2SSamuel Ortiz else if (protocol == NFC_PROTO_ISO14443 || 91001d719a2SSamuel Ortiz protocol == NFC_PROTO_ISO14443_B) 911019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 912019c4fbaSIlan Elias else 913019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 914019c4fbaSIlan Elias 915019c4fbaSIlan Elias rc = nci_request(ndev, nci_rf_discover_select_req, 916019c4fbaSIlan Elias (unsigned long)¶m, 917019c4fbaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); 918019c4fbaSIlan Elias } 919019c4fbaSIlan Elias 920019c4fbaSIlan Elias if (!rc) 921019c4fbaSIlan Elias ndev->target_active_prot = protocol; 922019c4fbaSIlan Elias 923019c4fbaSIlan Elias return rc; 9246a2968aaSIlan Elias } 9256a2968aaSIlan Elias 92690099433SEric Lapuyade static void nci_deactivate_target(struct nfc_dev *nfc_dev, 92796d4581fSChristophe Ricard struct nfc_target *target, 92896d4581fSChristophe Ricard __u8 mode) 9296a2968aaSIlan Elias { 9306a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 93196d4581fSChristophe Ricard u8 nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE; 9326a2968aaSIlan Elias 933767f19aeSIlan Elias pr_debug("entry\n"); 9346a2968aaSIlan Elias 9356a2968aaSIlan Elias if (!ndev->target_active_prot) { 936ed1e0ad8SJoe Perches pr_err("unable to deactivate target, no active target\n"); 9376a2968aaSIlan Elias return; 9386a2968aaSIlan Elias } 9396a2968aaSIlan Elias 9406a2968aaSIlan Elias ndev->target_active_prot = 0; 9416a2968aaSIlan Elias 94296d4581fSChristophe Ricard switch (mode) { 94396d4581fSChristophe Ricard case NFC_TARGET_MODE_SLEEP: 94496d4581fSChristophe Ricard nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE; 94596d4581fSChristophe Ricard break; 94696d4581fSChristophe Ricard } 94796d4581fSChristophe Ricard 9488939e47fSIlan Elias if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { 94996d4581fSChristophe Ricard nci_request(ndev, nci_rf_deactivate_req, nci_mode, 9506a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 9516a2968aaSIlan Elias } 9526a2968aaSIlan Elias } 9536a2968aaSIlan Elias 954767f19aeSIlan Elias static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, 955767f19aeSIlan Elias __u8 comm_mode, __u8 *gb, size_t gb_len) 956767f19aeSIlan Elias { 957767f19aeSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 958767f19aeSIlan Elias int rc; 959767f19aeSIlan Elias 960767f19aeSIlan Elias pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); 961767f19aeSIlan Elias 962767f19aeSIlan Elias rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); 963767f19aeSIlan Elias if (rc) 964767f19aeSIlan Elias return rc; 965767f19aeSIlan Elias 966767f19aeSIlan Elias rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, 967767f19aeSIlan Elias ndev->remote_gb_len); 968767f19aeSIlan Elias if (!rc) 969767f19aeSIlan Elias rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, 970767f19aeSIlan Elias NFC_RF_INITIATOR); 971767f19aeSIlan Elias 972767f19aeSIlan Elias return rc; 973767f19aeSIlan Elias } 974767f19aeSIlan Elias 975767f19aeSIlan Elias static int nci_dep_link_down(struct nfc_dev *nfc_dev) 976767f19aeSIlan Elias { 977d7979e13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 978d7979e13SJulien Lefrique int rc; 979d7979e13SJulien Lefrique 980767f19aeSIlan Elias pr_debug("entry\n"); 981767f19aeSIlan Elias 982d7979e13SJulien Lefrique if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { 98396d4581fSChristophe Ricard nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE); 984d7979e13SJulien Lefrique } else { 985d7979e13SJulien Lefrique if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || 986d7979e13SJulien Lefrique atomic_read(&ndev->state) == NCI_DISCOVERY) { 987d7979e13SJulien Lefrique nci_request(ndev, nci_rf_deactivate_req, 0, 988d7979e13SJulien Lefrique msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 989d7979e13SJulien Lefrique } 990d7979e13SJulien Lefrique 991d7979e13SJulien Lefrique rc = nfc_tm_deactivated(nfc_dev); 992d7979e13SJulien Lefrique if (rc) 993d7979e13SJulien Lefrique pr_err("error when signaling tm deactivation\n"); 994d7979e13SJulien Lefrique } 995767f19aeSIlan Elias 996767f19aeSIlan Elias return 0; 997767f19aeSIlan Elias } 998767f19aeSIlan Elias 999767f19aeSIlan Elias 1000be9ae4ceSSamuel Ortiz static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 10016a2968aaSIlan Elias struct sk_buff *skb, 1002eb9bc6e9SSamuel Ortiz data_exchange_cb_t cb, void *cb_context) 10036a2968aaSIlan Elias { 10046a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 100538f04c6bSIlan Elias int rc; 10064aeee687SChristophe Ricard struct nci_conn_info *conn_info; 10074aeee687SChristophe Ricard 100812bdf27dSChristophe Ricard conn_info = ndev->rf_conn_info; 10094aeee687SChristophe Ricard if (!conn_info) 10104aeee687SChristophe Ricard return -EPROTO; 10116a2968aaSIlan Elias 101290099433SEric Lapuyade pr_debug("target_idx %d, len %d\n", target->idx, skb->len); 10136a2968aaSIlan Elias 10146a2968aaSIlan Elias if (!ndev->target_active_prot) { 1015ed1e0ad8SJoe Perches pr_err("unable to exchange data, no active target\n"); 10166a2968aaSIlan Elias return -EINVAL; 10176a2968aaSIlan Elias } 10186a2968aaSIlan Elias 101938f04c6bSIlan Elias if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 102038f04c6bSIlan Elias return -EBUSY; 102138f04c6bSIlan Elias 10226a2968aaSIlan Elias /* store cb and context to be used on receiving data */ 10234aeee687SChristophe Ricard conn_info->data_exchange_cb = cb; 10244aeee687SChristophe Ricard conn_info->data_exchange_cb_context = cb_context; 10256a2968aaSIlan Elias 1026e8c0dacdSIlan Elias rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 102738f04c6bSIlan Elias if (rc) 102838f04c6bSIlan Elias clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); 102938f04c6bSIlan Elias 103038f04c6bSIlan Elias return rc; 10316a2968aaSIlan Elias } 10326a2968aaSIlan Elias 1033485f442fSJulien Lefrique static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) 1034485f442fSJulien Lefrique { 1035485f442fSJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 1036485f442fSJulien Lefrique int rc; 1037485f442fSJulien Lefrique 1038485f442fSJulien Lefrique rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 1039485f442fSJulien Lefrique if (rc) 1040485f442fSJulien Lefrique pr_err("unable to send data\n"); 1041485f442fSJulien Lefrique 1042485f442fSJulien Lefrique return rc; 1043485f442fSJulien Lefrique } 1044485f442fSJulien Lefrique 10450a946301SSamuel Ortiz static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) 10460a946301SSamuel Ortiz { 104793bca2bfSChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 104893bca2bfSChristophe Ricard 104993bca2bfSChristophe Ricard if (ndev->ops->enable_se) 105093bca2bfSChristophe Ricard return ndev->ops->enable_se(ndev, se_idx); 105193bca2bfSChristophe Ricard 10520a946301SSamuel Ortiz return 0; 10530a946301SSamuel Ortiz } 10540a946301SSamuel Ortiz 10550a946301SSamuel Ortiz static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) 10560a946301SSamuel Ortiz { 1057e9ef9431SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 1058e9ef9431SChristophe Ricard 1059e9ef9431SChristophe Ricard if (ndev->ops->disable_se) 1060e9ef9431SChristophe Ricard return ndev->ops->disable_se(ndev, se_idx); 1061e9ef9431SChristophe Ricard 10620a946301SSamuel Ortiz return 0; 10630a946301SSamuel Ortiz } 10640a946301SSamuel Ortiz 10650a946301SSamuel Ortiz static int nci_discover_se(struct nfc_dev *nfc_dev) 10660a946301SSamuel Ortiz { 1067fa00e8feSChristophe Ricard int r; 1068ba4db551SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 1069ba4db551SChristophe Ricard 1070fa00e8feSChristophe Ricard if (ndev->ops->discover_se) { 1071fa00e8feSChristophe Ricard r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE); 1072fa00e8feSChristophe Ricard if (r != NCI_STATUS_OK) 1073fa00e8feSChristophe Ricard return -EPROTO; 1074fa00e8feSChristophe Ricard 1075ba4db551SChristophe Ricard return ndev->ops->discover_se(ndev); 1076fa00e8feSChristophe Ricard } 1077ba4db551SChristophe Ricard 10780a946301SSamuel Ortiz return 0; 10790a946301SSamuel Ortiz } 10800a946301SSamuel Ortiz 1081a688bf55SChristophe Ricard static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, 1082a688bf55SChristophe Ricard u8 *apdu, size_t apdu_length, 1083a688bf55SChristophe Ricard se_io_cb_t cb, void *cb_context) 1084a688bf55SChristophe Ricard { 1085a688bf55SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 1086a688bf55SChristophe Ricard 1087a688bf55SChristophe Ricard if (ndev->ops->se_io) 1088a688bf55SChristophe Ricard return ndev->ops->se_io(ndev, se_idx, apdu, 1089a688bf55SChristophe Ricard apdu_length, cb, cb_context); 1090a688bf55SChristophe Ricard 1091a688bf55SChristophe Ricard return 0; 1092a688bf55SChristophe Ricard } 1093a688bf55SChristophe Ricard 109425af01edSClément Perrochaud static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) 109525af01edSClément Perrochaud { 109625af01edSClément Perrochaud struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 109725af01edSClément Perrochaud 109825af01edSClément Perrochaud if (!ndev->ops->fw_download) 109925af01edSClément Perrochaud return -ENOTSUPP; 110025af01edSClément Perrochaud 110125af01edSClément Perrochaud return ndev->ops->fw_download(ndev, firmware_name); 110225af01edSClément Perrochaud } 110325af01edSClément Perrochaud 11046a2968aaSIlan Elias static struct nfc_ops nci_nfc_ops = { 11056a2968aaSIlan Elias .dev_up = nci_dev_up, 11066a2968aaSIlan Elias .dev_down = nci_dev_down, 11076a2968aaSIlan Elias .start_poll = nci_start_poll, 11086a2968aaSIlan Elias .stop_poll = nci_stop_poll, 1109767f19aeSIlan Elias .dep_link_up = nci_dep_link_up, 1110767f19aeSIlan Elias .dep_link_down = nci_dep_link_down, 11116a2968aaSIlan Elias .activate_target = nci_activate_target, 11126a2968aaSIlan Elias .deactivate_target = nci_deactivate_target, 1113be9ae4ceSSamuel Ortiz .im_transceive = nci_transceive, 1114485f442fSJulien Lefrique .tm_send = nci_tm_send, 11150a946301SSamuel Ortiz .enable_se = nci_enable_se, 11160a946301SSamuel Ortiz .disable_se = nci_disable_se, 11170a946301SSamuel Ortiz .discover_se = nci_discover_se, 1118a688bf55SChristophe Ricard .se_io = nci_se_io, 111925af01edSClément Perrochaud .fw_download = nci_fw_download, 11206a2968aaSIlan Elias }; 11216a2968aaSIlan Elias 11226a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */ 11236a2968aaSIlan Elias /** 11246a2968aaSIlan Elias * nci_allocate_device - allocate a new nci device 11256a2968aaSIlan Elias * 11266a2968aaSIlan Elias * @ops: device operations 11276a2968aaSIlan Elias * @supported_protocols: NFC protocols supported by the device 11286a2968aaSIlan Elias */ 11296a2968aaSIlan Elias struct nci_dev *nci_allocate_device(struct nci_ops *ops, 11306a2968aaSIlan Elias __u32 supported_protocols, 1131eb9bc6e9SSamuel Ortiz int tx_headroom, int tx_tailroom) 11326a2968aaSIlan Elias { 11338ebafde0SDan Carpenter struct nci_dev *ndev; 11346a2968aaSIlan Elias 113524bf3304SJoe Perches pr_debug("supported_protocols 0x%x\n", supported_protocols); 11366a2968aaSIlan Elias 11376a2968aaSIlan Elias if (!ops->open || !ops->close || !ops->send) 11388ebafde0SDan Carpenter return NULL; 11396a2968aaSIlan Elias 11406a2968aaSIlan Elias if (!supported_protocols) 11418ebafde0SDan Carpenter return NULL; 11426a2968aaSIlan Elias 11436a2968aaSIlan Elias ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); 11446a2968aaSIlan Elias if (!ndev) 11458ebafde0SDan Carpenter return NULL; 11466a2968aaSIlan Elias 11476a2968aaSIlan Elias ndev->ops = ops; 1148b6355e97SSamuel Ortiz 1149b6355e97SSamuel Ortiz if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) { 1150b6355e97SSamuel Ortiz pr_err("Too many proprietary commands: %zd\n", 1151b6355e97SSamuel Ortiz ops->n_prop_ops); 1152b6355e97SSamuel Ortiz ops->prop_ops = NULL; 1153b6355e97SSamuel Ortiz ops->n_prop_ops = 0; 1154b6355e97SSamuel Ortiz } 1155b6355e97SSamuel Ortiz 11566a2968aaSIlan Elias ndev->tx_headroom = tx_headroom; 11576a2968aaSIlan Elias ndev->tx_tailroom = tx_tailroom; 11589bec44bfSAxel Lin init_completion(&ndev->req_completion); 11596a2968aaSIlan Elias 11606a2968aaSIlan Elias ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, 11616a2968aaSIlan Elias supported_protocols, 11626a2968aaSIlan Elias tx_headroom + NCI_DATA_HDR_SIZE, 11636a2968aaSIlan Elias tx_tailroom); 11646a2968aaSIlan Elias if (!ndev->nfc_dev) 116511f54f22SChristophe Ricard goto free_nci; 116611f54f22SChristophe Ricard 116711f54f22SChristophe Ricard ndev->hci_dev = nci_hci_allocate(ndev); 116811f54f22SChristophe Ricard if (!ndev->hci_dev) 116911f54f22SChristophe Ricard goto free_nfc; 11706a2968aaSIlan Elias 11716a2968aaSIlan Elias nfc_set_drvdata(ndev->nfc_dev, ndev); 11726a2968aaSIlan Elias 11738ebafde0SDan Carpenter return ndev; 11746a2968aaSIlan Elias 117511f54f22SChristophe Ricard free_nfc: 117611f54f22SChristophe Ricard kfree(ndev->nfc_dev); 117711f54f22SChristophe Ricard 117811f54f22SChristophe Ricard free_nci: 11796a2968aaSIlan Elias kfree(ndev); 11808ebafde0SDan Carpenter return NULL; 11816a2968aaSIlan Elias } 11826a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device); 11836a2968aaSIlan Elias 11846a2968aaSIlan Elias /** 11856a2968aaSIlan Elias * nci_free_device - deallocate nci device 11866a2968aaSIlan Elias * 11876a2968aaSIlan Elias * @ndev: The nci device to deallocate 11886a2968aaSIlan Elias */ 11896a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev) 11906a2968aaSIlan Elias { 11916a2968aaSIlan Elias nfc_free_device(ndev->nfc_dev); 11926a2968aaSIlan Elias kfree(ndev); 11936a2968aaSIlan Elias } 11946a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device); 11956a2968aaSIlan Elias 11966a2968aaSIlan Elias /** 11976a2968aaSIlan Elias * nci_register_device - register a nci device in the nfc subsystem 11986a2968aaSIlan Elias * 11996a2968aaSIlan Elias * @dev: The nci device to register 12006a2968aaSIlan Elias */ 12016a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev) 12026a2968aaSIlan Elias { 12036a2968aaSIlan Elias int rc; 12046a2968aaSIlan Elias struct device *dev = &ndev->nfc_dev->dev; 12056a2968aaSIlan Elias char name[32]; 12066a2968aaSIlan Elias 12076a2968aaSIlan Elias ndev->flags = 0; 12086a2968aaSIlan Elias 12096a2968aaSIlan Elias INIT_WORK(&ndev->cmd_work, nci_cmd_work); 12106a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); 12116a2968aaSIlan Elias ndev->cmd_wq = create_singlethread_workqueue(name); 12126a2968aaSIlan Elias if (!ndev->cmd_wq) { 12136a2968aaSIlan Elias rc = -ENOMEM; 12143c1c0f5dSVincent Cuissard goto exit; 12156a2968aaSIlan Elias } 12166a2968aaSIlan Elias 12176a2968aaSIlan Elias INIT_WORK(&ndev->rx_work, nci_rx_work); 12186a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); 12196a2968aaSIlan Elias ndev->rx_wq = create_singlethread_workqueue(name); 12206a2968aaSIlan Elias if (!ndev->rx_wq) { 12216a2968aaSIlan Elias rc = -ENOMEM; 12226a2968aaSIlan Elias goto destroy_cmd_wq_exit; 12236a2968aaSIlan Elias } 12246a2968aaSIlan Elias 12256a2968aaSIlan Elias INIT_WORK(&ndev->tx_work, nci_tx_work); 12266a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); 12276a2968aaSIlan Elias ndev->tx_wq = create_singlethread_workqueue(name); 12286a2968aaSIlan Elias if (!ndev->tx_wq) { 12296a2968aaSIlan Elias rc = -ENOMEM; 12306a2968aaSIlan Elias goto destroy_rx_wq_exit; 12316a2968aaSIlan Elias } 12326a2968aaSIlan Elias 12336a2968aaSIlan Elias skb_queue_head_init(&ndev->cmd_q); 12346a2968aaSIlan Elias skb_queue_head_init(&ndev->rx_q); 12356a2968aaSIlan Elias skb_queue_head_init(&ndev->tx_q); 12366a2968aaSIlan Elias 12376a2968aaSIlan Elias setup_timer(&ndev->cmd_timer, nci_cmd_timer, 12386a2968aaSIlan Elias (unsigned long) ndev); 1239c4bf98b2SIlan Elias setup_timer(&ndev->data_timer, nci_data_timer, 1240c4bf98b2SIlan Elias (unsigned long) ndev); 12416a2968aaSIlan Elias 12426a2968aaSIlan Elias mutex_init(&ndev->req_lock); 12434aeee687SChristophe Ricard INIT_LIST_HEAD(&ndev->conn_info_list); 12446a2968aaSIlan Elias 12453c1c0f5dSVincent Cuissard rc = nfc_register_device(ndev->nfc_dev); 12463c1c0f5dSVincent Cuissard if (rc) 12473c1c0f5dSVincent Cuissard goto destroy_rx_wq_exit; 12483c1c0f5dSVincent Cuissard 12496a2968aaSIlan Elias goto exit; 12506a2968aaSIlan Elias 12516a2968aaSIlan Elias destroy_rx_wq_exit: 12526a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 12536a2968aaSIlan Elias 12546a2968aaSIlan Elias destroy_cmd_wq_exit: 12556a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 12566a2968aaSIlan Elias 12576a2968aaSIlan Elias exit: 12586a2968aaSIlan Elias return rc; 12596a2968aaSIlan Elias } 12606a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device); 12616a2968aaSIlan Elias 12626a2968aaSIlan Elias /** 12636a2968aaSIlan Elias * nci_unregister_device - unregister a nci device in the nfc subsystem 12646a2968aaSIlan Elias * 12656a2968aaSIlan Elias * @dev: The nci device to unregister 12666a2968aaSIlan Elias */ 12676a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev) 12686a2968aaSIlan Elias { 12694aeee687SChristophe Ricard struct nci_conn_info *conn_info, *n; 12704aeee687SChristophe Ricard 12716a2968aaSIlan Elias nci_close_device(ndev); 12726a2968aaSIlan Elias 12736a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 12746a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 12756a2968aaSIlan Elias destroy_workqueue(ndev->tx_wq); 12766a2968aaSIlan Elias 12774aeee687SChristophe Ricard list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) { 12784aeee687SChristophe Ricard list_del(&conn_info->list); 12794aeee687SChristophe Ricard /* conn_info is allocated with devm_kzalloc */ 12804aeee687SChristophe Ricard } 12814aeee687SChristophe Ricard 12826a2968aaSIlan Elias nfc_unregister_device(ndev->nfc_dev); 12836a2968aaSIlan Elias } 12846a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device); 12856a2968aaSIlan Elias 12866a2968aaSIlan Elias /** 12876a2968aaSIlan Elias * nci_recv_frame - receive frame from NCI drivers 12886a2968aaSIlan Elias * 12891095e69fSFrederic Danis * @ndev: The nci device 12906a2968aaSIlan Elias * @skb: The sk_buff to receive 12916a2968aaSIlan Elias */ 12921095e69fSFrederic Danis int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 12936a2968aaSIlan Elias { 129424bf3304SJoe Perches pr_debug("len %d\n", skb->len); 12956a2968aaSIlan Elias 1296874934f4SSzymon Janc if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && 1297874934f4SSzymon Janc !test_bit(NCI_INIT, &ndev->flags))) { 12986a2968aaSIlan Elias kfree_skb(skb); 12996a2968aaSIlan Elias return -ENXIO; 13006a2968aaSIlan Elias } 13016a2968aaSIlan Elias 13026a2968aaSIlan Elias /* Queue frame for rx worker thread */ 13036a2968aaSIlan Elias skb_queue_tail(&ndev->rx_q, skb); 13046a2968aaSIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 13056a2968aaSIlan Elias 13066a2968aaSIlan Elias return 0; 13076a2968aaSIlan Elias } 13086a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame); 13096a2968aaSIlan Elias 1310e5629d29SVincent Cuissard int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) 13116a2968aaSIlan Elias { 131224bf3304SJoe Perches pr_debug("len %d\n", skb->len); 13136a2968aaSIlan Elias 13146a2968aaSIlan Elias if (!ndev) { 13156a2968aaSIlan Elias kfree_skb(skb); 13166a2968aaSIlan Elias return -ENODEV; 13176a2968aaSIlan Elias } 13186a2968aaSIlan Elias 13196a2968aaSIlan Elias /* Get rid of skb owner, prior to sending to the driver. */ 13206a2968aaSIlan Elias skb_orphan(skb); 13216a2968aaSIlan Elias 132205158296SHiren Tandel /* Send copy to sniffer */ 132305158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 132405158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); 132505158296SHiren Tandel 13261095e69fSFrederic Danis return ndev->ops->send(ndev, skb); 13276a2968aaSIlan Elias } 1328e5629d29SVincent Cuissard EXPORT_SYMBOL(nci_send_frame); 13296a2968aaSIlan Elias 13306a2968aaSIlan Elias /* Send NCI command */ 13316a2968aaSIlan Elias int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) 13326a2968aaSIlan Elias { 13336a2968aaSIlan Elias struct nci_ctrl_hdr *hdr; 13346a2968aaSIlan Elias struct sk_buff *skb; 13356a2968aaSIlan Elias 133624bf3304SJoe Perches pr_debug("opcode 0x%x, plen %d\n", opcode, plen); 13376a2968aaSIlan Elias 13386a2968aaSIlan Elias skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); 13396a2968aaSIlan Elias if (!skb) { 1340ed1e0ad8SJoe Perches pr_err("no memory for command\n"); 13416a2968aaSIlan Elias return -ENOMEM; 13426a2968aaSIlan Elias } 13436a2968aaSIlan Elias 13444df864c1SJohannes Berg hdr = skb_put(skb, NCI_CTRL_HDR_SIZE); 13456a2968aaSIlan Elias hdr->gid = nci_opcode_gid(opcode); 13466a2968aaSIlan Elias hdr->oid = nci_opcode_oid(opcode); 13476a2968aaSIlan Elias hdr->plen = plen; 13486a2968aaSIlan Elias 13496a2968aaSIlan Elias nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); 13506a2968aaSIlan Elias nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 13516a2968aaSIlan Elias 13526a2968aaSIlan Elias if (plen) 135359ae1d12SJohannes Berg skb_put_data(skb, payload, plen); 13546a2968aaSIlan Elias 13556a2968aaSIlan Elias skb_queue_tail(&ndev->cmd_q, skb); 13566a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 13576a2968aaSIlan Elias 13586a2968aaSIlan Elias return 0; 13596a2968aaSIlan Elias } 1360e5629d29SVincent Cuissard EXPORT_SYMBOL(nci_send_cmd); 13616a2968aaSIlan Elias 1362b6355e97SSamuel Ortiz /* Proprietary commands API */ 136322e4bd09SRobert Dolca static struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops, 13640a97a3cbSRobert Dolca size_t n_ops, 1365b6355e97SSamuel Ortiz __u16 opcode) 1366b6355e97SSamuel Ortiz { 1367b6355e97SSamuel Ortiz size_t i; 136822e4bd09SRobert Dolca struct nci_driver_ops *op; 1369b6355e97SSamuel Ortiz 13700a97a3cbSRobert Dolca if (!ops || !n_ops) 1371b6355e97SSamuel Ortiz return NULL; 1372b6355e97SSamuel Ortiz 13730a97a3cbSRobert Dolca for (i = 0; i < n_ops; i++) { 13740a97a3cbSRobert Dolca op = &ops[i]; 13750a97a3cbSRobert Dolca if (op->opcode == opcode) 13760a97a3cbSRobert Dolca return op; 1377b6355e97SSamuel Ortiz } 1378b6355e97SSamuel Ortiz 1379b6355e97SSamuel Ortiz return NULL; 1380b6355e97SSamuel Ortiz } 1381b6355e97SSamuel Ortiz 13820a97a3cbSRobert Dolca static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, 138322e4bd09SRobert Dolca struct sk_buff *skb, struct nci_driver_ops *ops, 13840a97a3cbSRobert Dolca size_t n_ops) 1385b6355e97SSamuel Ortiz { 138622e4bd09SRobert Dolca struct nci_driver_ops *op; 1387b6355e97SSamuel Ortiz 13880a97a3cbSRobert Dolca op = ops_cmd_lookup(ops, n_ops, rsp_opcode); 13890a97a3cbSRobert Dolca if (!op || !op->rsp) 1390b6355e97SSamuel Ortiz return -ENOTSUPP; 1391b6355e97SSamuel Ortiz 13920a97a3cbSRobert Dolca return op->rsp(ndev, skb); 1393b6355e97SSamuel Ortiz } 1394b6355e97SSamuel Ortiz 13950a97a3cbSRobert Dolca static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, 139622e4bd09SRobert Dolca struct sk_buff *skb, struct nci_driver_ops *ops, 13970a97a3cbSRobert Dolca size_t n_ops) 1398b6355e97SSamuel Ortiz { 139922e4bd09SRobert Dolca struct nci_driver_ops *op; 1400b6355e97SSamuel Ortiz 14010a97a3cbSRobert Dolca op = ops_cmd_lookup(ops, n_ops, ntf_opcode); 14020a97a3cbSRobert Dolca if (!op || !op->ntf) 1403b6355e97SSamuel Ortiz return -ENOTSUPP; 1404b6355e97SSamuel Ortiz 14050a97a3cbSRobert Dolca return op->ntf(ndev, skb); 14060a97a3cbSRobert Dolca } 14070a97a3cbSRobert Dolca 1408f1163174SRobert Dolca int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, 14090a97a3cbSRobert Dolca struct sk_buff *skb) 14100a97a3cbSRobert Dolca { 14110a97a3cbSRobert Dolca return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops, 14120a97a3cbSRobert Dolca ndev->ops->n_prop_ops); 14130a97a3cbSRobert Dolca } 14140a97a3cbSRobert Dolca 1415f1163174SRobert Dolca int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, 14160a97a3cbSRobert Dolca struct sk_buff *skb) 14170a97a3cbSRobert Dolca { 14180a97a3cbSRobert Dolca return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops, 14190a97a3cbSRobert Dolca ndev->ops->n_prop_ops); 14200a97a3cbSRobert Dolca } 14210a97a3cbSRobert Dolca 1422f1163174SRobert Dolca int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, 14230a97a3cbSRobert Dolca struct sk_buff *skb) 14240a97a3cbSRobert Dolca { 14250a97a3cbSRobert Dolca return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops, 14260a97a3cbSRobert Dolca ndev->ops->n_core_ops); 14270a97a3cbSRobert Dolca } 14280a97a3cbSRobert Dolca 1429f1163174SRobert Dolca int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, 14300a97a3cbSRobert Dolca struct sk_buff *skb) 14310a97a3cbSRobert Dolca { 14320a97a3cbSRobert Dolca return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops, 14330a97a3cbSRobert Dolca ndev->ops->n_core_ops); 1434b6355e97SSamuel Ortiz } 1435b6355e97SSamuel Ortiz 14366a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */ 14376a2968aaSIlan Elias 14386a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work) 14396a2968aaSIlan Elias { 14406a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); 14414aeee687SChristophe Ricard struct nci_conn_info *conn_info; 14426a2968aaSIlan Elias struct sk_buff *skb; 14436a2968aaSIlan Elias 14444aeee687SChristophe Ricard conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); 14454aeee687SChristophe Ricard if (!conn_info) 14464aeee687SChristophe Ricard return; 14474aeee687SChristophe Ricard 14484aeee687SChristophe Ricard pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt)); 14496a2968aaSIlan Elias 14506a2968aaSIlan Elias /* Send queued tx data */ 14514aeee687SChristophe Ricard while (atomic_read(&conn_info->credits_cnt)) { 14526a2968aaSIlan Elias skb = skb_dequeue(&ndev->tx_q); 14536a2968aaSIlan Elias if (!skb) 14546a2968aaSIlan Elias return; 14556a2968aaSIlan Elias 1456db98c829SIlan Elias /* Check if data flow control is used */ 14574aeee687SChristophe Ricard if (atomic_read(&conn_info->credits_cnt) != 1458db98c829SIlan Elias NCI_DATA_FLOW_CONTROL_NOT_USED) 14594aeee687SChristophe Ricard atomic_dec(&conn_info->credits_cnt); 14606a2968aaSIlan Elias 146120c239c1SJoe Perches pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", 14626a2968aaSIlan Elias nci_pbf(skb->data), 14636a2968aaSIlan Elias nci_conn_id(skb->data), 14646a2968aaSIlan Elias nci_plen(skb->data)); 14656a2968aaSIlan Elias 14661095e69fSFrederic Danis nci_send_frame(ndev, skb); 1467c4bf98b2SIlan Elias 1468c4bf98b2SIlan Elias mod_timer(&ndev->data_timer, 1469c4bf98b2SIlan Elias jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); 14706a2968aaSIlan Elias } 14716a2968aaSIlan Elias } 14726a2968aaSIlan Elias 14736a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */ 14746a2968aaSIlan Elias 14756a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work) 14766a2968aaSIlan Elias { 14776a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); 14786a2968aaSIlan Elias struct sk_buff *skb; 14796a2968aaSIlan Elias 14806a2968aaSIlan Elias while ((skb = skb_dequeue(&ndev->rx_q))) { 148105158296SHiren Tandel 148205158296SHiren Tandel /* Send copy to sniffer */ 148305158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 148405158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); 148505158296SHiren Tandel 14866a2968aaSIlan Elias /* Process frame */ 14876a2968aaSIlan Elias switch (nci_mt(skb->data)) { 14886a2968aaSIlan Elias case NCI_MT_RSP_PKT: 14896a2968aaSIlan Elias nci_rsp_packet(ndev, skb); 14906a2968aaSIlan Elias break; 14916a2968aaSIlan Elias 14926a2968aaSIlan Elias case NCI_MT_NTF_PKT: 14936a2968aaSIlan Elias nci_ntf_packet(ndev, skb); 14946a2968aaSIlan Elias break; 14956a2968aaSIlan Elias 14966a2968aaSIlan Elias case NCI_MT_DATA_PKT: 14976a2968aaSIlan Elias nci_rx_data_packet(ndev, skb); 14986a2968aaSIlan Elias break; 14996a2968aaSIlan Elias 15006a2968aaSIlan Elias default: 1501ed1e0ad8SJoe Perches pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); 15026a2968aaSIlan Elias kfree_skb(skb); 15036a2968aaSIlan Elias break; 15046a2968aaSIlan Elias } 15056a2968aaSIlan Elias } 1506c4bf98b2SIlan Elias 1507c4bf98b2SIlan Elias /* check if a data exchange timout has occurred */ 1508c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { 1509c4bf98b2SIlan Elias /* complete the data exchange transaction, if exists */ 1510c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 15114aeee687SChristophe Ricard nci_data_exchange_complete(ndev, NULL, 15124aeee687SChristophe Ricard ndev->cur_conn_id, 15134aeee687SChristophe Ricard -ETIMEDOUT); 1514c4bf98b2SIlan Elias 1515c4bf98b2SIlan Elias clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 1516c4bf98b2SIlan Elias } 15176a2968aaSIlan Elias } 15186a2968aaSIlan Elias 15196a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */ 15206a2968aaSIlan Elias 15216a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work) 15226a2968aaSIlan Elias { 15236a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); 15246a2968aaSIlan Elias struct sk_buff *skb; 15256a2968aaSIlan Elias 152624bf3304SJoe Perches pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); 15276a2968aaSIlan Elias 15286a2968aaSIlan Elias /* Send queued command */ 15296a2968aaSIlan Elias if (atomic_read(&ndev->cmd_cnt)) { 15306a2968aaSIlan Elias skb = skb_dequeue(&ndev->cmd_q); 15316a2968aaSIlan Elias if (!skb) 15326a2968aaSIlan Elias return; 15336a2968aaSIlan Elias 15346a2968aaSIlan Elias atomic_dec(&ndev->cmd_cnt); 15356a2968aaSIlan Elias 153620c239c1SJoe Perches pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 15376a2968aaSIlan Elias nci_pbf(skb->data), 15386a2968aaSIlan Elias nci_opcode_gid(nci_opcode(skb->data)), 15396a2968aaSIlan Elias nci_opcode_oid(nci_opcode(skb->data)), 15406a2968aaSIlan Elias nci_plen(skb->data)); 15416a2968aaSIlan Elias 15421095e69fSFrederic Danis nci_send_frame(ndev, skb); 15436a2968aaSIlan Elias 15446a2968aaSIlan Elias mod_timer(&ndev->cmd_timer, 15456a2968aaSIlan Elias jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); 15466a2968aaSIlan Elias } 15476a2968aaSIlan Elias } 15488a70e7f8SDave Jones 15498a70e7f8SDave Jones MODULE_LICENSE("GPL"); 1550