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 6785b9ce9aSRobert Dolca int nci_get_conn_info_by_id(struct nci_dev *ndev, u8 id) 6885b9ce9aSRobert Dolca { 6985b9ce9aSRobert Dolca struct nci_conn_info *conn_info; 7085b9ce9aSRobert Dolca 7185b9ce9aSRobert Dolca list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 7285b9ce9aSRobert Dolca if (conn_info->id == id) 7385b9ce9aSRobert Dolca return conn_info->conn_id; 7485b9ce9aSRobert Dolca } 7585b9ce9aSRobert Dolca 7685b9ce9aSRobert Dolca return -EINVAL; 7785b9ce9aSRobert Dolca } 7885b9ce9aSRobert Dolca EXPORT_SYMBOL(nci_get_conn_info_by_id); 7985b9ce9aSRobert Dolca 806a2968aaSIlan Elias /* ---- NCI requests ---- */ 816a2968aaSIlan Elias 826a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result) 836a2968aaSIlan Elias { 846a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 856a2968aaSIlan Elias ndev->req_result = result; 866a2968aaSIlan Elias ndev->req_status = NCI_REQ_DONE; 876a2968aaSIlan Elias complete(&ndev->req_completion); 886a2968aaSIlan Elias } 896a2968aaSIlan Elias } 902df7f8c6SSamuel Ortiz EXPORT_SYMBOL(nci_req_complete); 916a2968aaSIlan Elias 926a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err) 936a2968aaSIlan Elias { 946a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 956a2968aaSIlan Elias ndev->req_result = err; 966a2968aaSIlan Elias ndev->req_status = NCI_REQ_CANCELED; 976a2968aaSIlan Elias complete(&ndev->req_completion); 986a2968aaSIlan Elias } 996a2968aaSIlan Elias } 1006a2968aaSIlan Elias 1016a2968aaSIlan Elias /* Execute request and wait for completion. */ 1026a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev, 1036a2968aaSIlan Elias void (*req)(struct nci_dev *ndev, unsigned long opt), 104eb9bc6e9SSamuel Ortiz unsigned long opt, __u32 timeout) 1056a2968aaSIlan Elias { 1066a2968aaSIlan Elias int rc = 0; 107f8c141c3SDan Carpenter long completion_rc; 1086a2968aaSIlan Elias 1096a2968aaSIlan Elias ndev->req_status = NCI_REQ_PEND; 1106a2968aaSIlan Elias 1119bec44bfSAxel Lin reinit_completion(&ndev->req_completion); 1126a2968aaSIlan Elias req(ndev, opt); 113eb9bc6e9SSamuel Ortiz completion_rc = 114eb9bc6e9SSamuel Ortiz wait_for_completion_interruptible_timeout(&ndev->req_completion, 1156a2968aaSIlan Elias timeout); 1166a2968aaSIlan Elias 11720c239c1SJoe Perches pr_debug("wait_for_completion return %ld\n", completion_rc); 1186a2968aaSIlan Elias 1196a2968aaSIlan Elias if (completion_rc > 0) { 1206a2968aaSIlan Elias switch (ndev->req_status) { 1216a2968aaSIlan Elias case NCI_REQ_DONE: 1226a2968aaSIlan Elias rc = nci_to_errno(ndev->req_result); 1236a2968aaSIlan Elias break; 1246a2968aaSIlan Elias 1256a2968aaSIlan Elias case NCI_REQ_CANCELED: 1266a2968aaSIlan Elias rc = -ndev->req_result; 1276a2968aaSIlan Elias break; 1286a2968aaSIlan Elias 1296a2968aaSIlan Elias default: 1306a2968aaSIlan Elias rc = -ETIMEDOUT; 1316a2968aaSIlan Elias break; 1326a2968aaSIlan Elias } 1336a2968aaSIlan Elias } else { 134ed1e0ad8SJoe Perches pr_err("wait_for_completion_interruptible_timeout failed %ld\n", 1356a2968aaSIlan Elias completion_rc); 1366a2968aaSIlan Elias 1376a2968aaSIlan Elias rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); 1386a2968aaSIlan Elias } 1396a2968aaSIlan Elias 1406a2968aaSIlan Elias ndev->req_status = ndev->req_result = 0; 1416a2968aaSIlan Elias 1426a2968aaSIlan Elias return rc; 1436a2968aaSIlan Elias } 1446a2968aaSIlan Elias 14511f54f22SChristophe Ricard inline int nci_request(struct nci_dev *ndev, 146eb9bc6e9SSamuel Ortiz void (*req)(struct nci_dev *ndev, 147eb9bc6e9SSamuel Ortiz unsigned long opt), 1486a2968aaSIlan Elias unsigned long opt, __u32 timeout) 1496a2968aaSIlan Elias { 1506a2968aaSIlan Elias int rc; 1516a2968aaSIlan Elias 1526a2968aaSIlan Elias if (!test_bit(NCI_UP, &ndev->flags)) 1536a2968aaSIlan Elias return -ENETDOWN; 1546a2968aaSIlan Elias 1556a2968aaSIlan Elias /* Serialize all requests */ 1566a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 1576a2968aaSIlan Elias rc = __nci_request(ndev, req, opt, timeout); 1586a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 1596a2968aaSIlan Elias 1606a2968aaSIlan Elias return rc; 1616a2968aaSIlan Elias } 1626a2968aaSIlan Elias 1636a2968aaSIlan Elias static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) 1646a2968aaSIlan Elias { 165e8c0dacdSIlan Elias struct nci_core_reset_cmd cmd; 166e8c0dacdSIlan Elias 167e8c0dacdSIlan Elias cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; 168e8c0dacdSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); 1696a2968aaSIlan Elias } 1706a2968aaSIlan Elias 1716a2968aaSIlan Elias static void nci_init_req(struct nci_dev *ndev, unsigned long opt) 1726a2968aaSIlan Elias { 1736a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1746a2968aaSIlan Elias } 1756a2968aaSIlan Elias 1766a2968aaSIlan Elias static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) 1776a2968aaSIlan Elias { 1782eb1dc10SIlan Elias struct nci_rf_disc_map_cmd cmd; 1792eb1dc10SIlan Elias struct disc_map_config *cfg = cmd.mapping_configs; 1802eb1dc10SIlan Elias __u8 *num = &cmd.num_mapping_configs; 1816a2968aaSIlan Elias int i; 1826a2968aaSIlan Elias 1836a2968aaSIlan Elias /* set rf mapping configurations */ 1842eb1dc10SIlan Elias *num = 0; 1856a2968aaSIlan Elias 1866a2968aaSIlan Elias /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ 1876a2968aaSIlan Elias for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { 1886a2968aaSIlan Elias if (ndev->supported_rf_interfaces[i] == 1896a2968aaSIlan Elias NCI_RF_INTERFACE_ISO_DEP) { 1902eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 191637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 192637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 193637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; 1942eb1dc10SIlan Elias (*num)++; 1956a2968aaSIlan Elias } else if (ndev->supported_rf_interfaces[i] == 1966a2968aaSIlan Elias NCI_RF_INTERFACE_NFC_DEP) { 1972eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 198637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 199637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 200637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP; 2012eb1dc10SIlan Elias (*num)++; 2026a2968aaSIlan Elias } 2036a2968aaSIlan Elias 2042eb1dc10SIlan Elias if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) 2056a2968aaSIlan Elias break; 2066a2968aaSIlan Elias } 2076a2968aaSIlan Elias 2086a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, 209eb9bc6e9SSamuel Ortiz (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); 2106a2968aaSIlan Elias } 2116a2968aaSIlan Elias 2127e035230SIlan Elias struct nci_set_config_param { 2137e035230SIlan Elias __u8 id; 2147e035230SIlan Elias size_t len; 2157e035230SIlan Elias __u8 *val; 2167e035230SIlan Elias }; 2177e035230SIlan Elias 2187e035230SIlan Elias static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) 2197e035230SIlan Elias { 2207e035230SIlan Elias struct nci_set_config_param *param = (struct nci_set_config_param *)opt; 2217e035230SIlan Elias struct nci_core_set_config_cmd cmd; 2227e035230SIlan Elias 2237e035230SIlan Elias BUG_ON(param->len > NCI_MAX_PARAM_LEN); 2247e035230SIlan Elias 2257e035230SIlan Elias cmd.num_params = 1; 2267e035230SIlan Elias cmd.param.id = param->id; 2277e035230SIlan Elias cmd.param.len = param->len; 2287e035230SIlan Elias memcpy(cmd.param.val, param->val, param->len); 2297e035230SIlan Elias 2307e035230SIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); 2317e035230SIlan Elias } 2327e035230SIlan Elias 233772dccf4SJulien Lefrique struct nci_rf_discover_param { 234772dccf4SJulien Lefrique __u32 im_protocols; 235772dccf4SJulien Lefrique __u32 tm_protocols; 236772dccf4SJulien Lefrique }; 237772dccf4SJulien Lefrique 2386a2968aaSIlan Elias static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) 2396a2968aaSIlan Elias { 240772dccf4SJulien Lefrique struct nci_rf_discover_param *param = 241772dccf4SJulien Lefrique (struct nci_rf_discover_param *)opt; 2426a2968aaSIlan Elias struct nci_rf_disc_cmd cmd; 2436a2968aaSIlan Elias 2446a2968aaSIlan Elias cmd.num_disc_configs = 0; 2456a2968aaSIlan Elias 2466a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 247772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_JEWEL_MASK || 248772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_MIFARE_MASK || 249772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_ISO14443_MASK || 250772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 251637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 252637d85a7SIlan Elias NCI_NFC_A_PASSIVE_POLL_MODE; 2536a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2546a2968aaSIlan Elias cmd.num_disc_configs++; 2556a2968aaSIlan Elias } 2566a2968aaSIlan Elias 2576a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 258772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { 259637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 260637d85a7SIlan Elias NCI_NFC_B_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_FELICA_MASK || 267772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 268637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 269637d85a7SIlan Elias NCI_NFC_F_PASSIVE_POLL_MODE; 2706a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2716a2968aaSIlan Elias cmd.num_disc_configs++; 2726a2968aaSIlan Elias } 2736a2968aaSIlan Elias 274cfdbeeafSVincent Cuissard if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 275772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { 276cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 277cfdbeeafSVincent Cuissard NCI_NFC_V_PASSIVE_POLL_MODE; 278cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 279cfdbeeafSVincent Cuissard cmd.num_disc_configs++; 280cfdbeeafSVincent Cuissard } 281cfdbeeafSVincent Cuissard 282772dccf4SJulien Lefrique if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && 283772dccf4SJulien Lefrique (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { 284772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 285772dccf4SJulien Lefrique NCI_NFC_A_PASSIVE_LISTEN_MODE; 286772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 287772dccf4SJulien Lefrique cmd.num_disc_configs++; 288772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 289772dccf4SJulien Lefrique NCI_NFC_F_PASSIVE_LISTEN_MODE; 290772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 291772dccf4SJulien Lefrique cmd.num_disc_configs++; 292772dccf4SJulien Lefrique } 293772dccf4SJulien Lefrique 2946a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, 2956a2968aaSIlan Elias (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), 2966a2968aaSIlan Elias &cmd); 2976a2968aaSIlan Elias } 2986a2968aaSIlan Elias 299019c4fbaSIlan Elias struct nci_rf_discover_select_param { 300019c4fbaSIlan Elias __u8 rf_discovery_id; 301019c4fbaSIlan Elias __u8 rf_protocol; 302019c4fbaSIlan Elias }; 303019c4fbaSIlan Elias 304019c4fbaSIlan Elias static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) 305019c4fbaSIlan Elias { 306019c4fbaSIlan Elias struct nci_rf_discover_select_param *param = 307019c4fbaSIlan Elias (struct nci_rf_discover_select_param *)opt; 308019c4fbaSIlan Elias struct nci_rf_discover_select_cmd cmd; 309019c4fbaSIlan Elias 310019c4fbaSIlan Elias cmd.rf_discovery_id = param->rf_discovery_id; 311019c4fbaSIlan Elias cmd.rf_protocol = param->rf_protocol; 312019c4fbaSIlan Elias 313019c4fbaSIlan Elias switch (cmd.rf_protocol) { 314019c4fbaSIlan Elias case NCI_RF_PROTOCOL_ISO_DEP: 315019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; 316019c4fbaSIlan Elias break; 317019c4fbaSIlan Elias 318019c4fbaSIlan Elias case NCI_RF_PROTOCOL_NFC_DEP: 319019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; 320019c4fbaSIlan Elias break; 321019c4fbaSIlan Elias 322019c4fbaSIlan Elias default: 323019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_FRAME; 324019c4fbaSIlan Elias break; 325019c4fbaSIlan Elias } 326019c4fbaSIlan Elias 327019c4fbaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, 328eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_discover_select_cmd), &cmd); 329019c4fbaSIlan Elias } 330019c4fbaSIlan Elias 3316a2968aaSIlan Elias static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) 3326a2968aaSIlan Elias { 3336a2968aaSIlan Elias struct nci_rf_deactivate_cmd cmd; 3346a2968aaSIlan Elias 3359295b5b5SChristophe Ricard cmd.type = opt; 3366a2968aaSIlan Elias 3376a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, 338eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_deactivate_cmd), &cmd); 3396a2968aaSIlan Elias } 3406a2968aaSIlan Elias 3417bc4824eSRobert Dolca struct nci_cmd_param { 342759afb8dSChristophe Ricard __u16 opcode; 343759afb8dSChristophe Ricard size_t len; 344759afb8dSChristophe Ricard __u8 *payload; 345759afb8dSChristophe Ricard }; 346759afb8dSChristophe Ricard 3477bc4824eSRobert Dolca static void nci_generic_req(struct nci_dev *ndev, unsigned long opt) 348759afb8dSChristophe Ricard { 3497bc4824eSRobert Dolca struct nci_cmd_param *param = 3507bc4824eSRobert Dolca (struct nci_cmd_param *)opt; 351759afb8dSChristophe Ricard 352759afb8dSChristophe Ricard nci_send_cmd(ndev, param->opcode, param->len, param->payload); 353759afb8dSChristophe Ricard } 354759afb8dSChristophe Ricard 355759afb8dSChristophe Ricard int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload) 356759afb8dSChristophe Ricard { 3577bc4824eSRobert Dolca struct nci_cmd_param param; 358759afb8dSChristophe Ricard 359759afb8dSChristophe Ricard param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid); 360759afb8dSChristophe Ricard param.len = len; 361759afb8dSChristophe Ricard param.payload = payload; 362759afb8dSChristophe Ricard 3637bc4824eSRobert Dolca return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, 364759afb8dSChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 365759afb8dSChristophe Ricard } 366759afb8dSChristophe Ricard EXPORT_SYMBOL(nci_prop_cmd); 367759afb8dSChristophe Ricard 3687bc4824eSRobert Dolca int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload) 3697bc4824eSRobert Dolca { 3707bc4824eSRobert Dolca struct nci_cmd_param param; 3717bc4824eSRobert Dolca 3727bc4824eSRobert Dolca param.opcode = opcode; 3737bc4824eSRobert Dolca param.len = len; 3747bc4824eSRobert Dolca param.payload = payload; 3757bc4824eSRobert Dolca 3767bc4824eSRobert Dolca return __nci_request(ndev, nci_generic_req, (unsigned long)¶m, 3777bc4824eSRobert Dolca msecs_to_jiffies(NCI_CMD_TIMEOUT)); 3787bc4824eSRobert Dolca } 3797bc4824eSRobert Dolca EXPORT_SYMBOL(nci_core_cmd); 3807bc4824eSRobert Dolca 381025a0cb8SRobert Baldyga int nci_core_reset(struct nci_dev *ndev) 382025a0cb8SRobert Baldyga { 383025a0cb8SRobert Baldyga return __nci_request(ndev, nci_reset_req, 0, 384025a0cb8SRobert Baldyga msecs_to_jiffies(NCI_RESET_TIMEOUT)); 385025a0cb8SRobert Baldyga } 386025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_reset); 387025a0cb8SRobert Baldyga 388025a0cb8SRobert Baldyga int nci_core_init(struct nci_dev *ndev) 389025a0cb8SRobert Baldyga { 390025a0cb8SRobert Baldyga return __nci_request(ndev, nci_init_req, 0, 391025a0cb8SRobert Baldyga msecs_to_jiffies(NCI_INIT_TIMEOUT)); 392025a0cb8SRobert Baldyga } 393025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_init); 394025a0cb8SRobert Baldyga 3956a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev) 3966a2968aaSIlan Elias { 3976a2968aaSIlan Elias int rc = 0; 3986a2968aaSIlan Elias 3996a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 4006a2968aaSIlan Elias 4016a2968aaSIlan Elias if (test_bit(NCI_UP, &ndev->flags)) { 4026a2968aaSIlan Elias rc = -EALREADY; 4036a2968aaSIlan Elias goto done; 4046a2968aaSIlan Elias } 4056a2968aaSIlan Elias 4066a2968aaSIlan Elias if (ndev->ops->open(ndev)) { 4076a2968aaSIlan Elias rc = -EIO; 4086a2968aaSIlan Elias goto done; 4096a2968aaSIlan Elias } 4106a2968aaSIlan Elias 4116a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 4126a2968aaSIlan Elias 4136a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 4146a2968aaSIlan Elias 415c39daeeeSChristophe Ricard if (ndev->ops->init) 416c39daeeeSChristophe Ricard rc = ndev->ops->init(ndev); 417c39daeeeSChristophe Ricard 418c39daeeeSChristophe Ricard if (!rc) { 4196a2968aaSIlan Elias rc = __nci_request(ndev, nci_reset_req, 0, 4206a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 421c39daeeeSChristophe Ricard } 4226a2968aaSIlan Elias 42381859ab8SChristophe Ricard if (!rc && ndev->ops->setup) { 42481859ab8SChristophe Ricard rc = ndev->ops->setup(ndev); 42581859ab8SChristophe Ricard } 42686e8586eSAmitkumar Karwar 4276a2968aaSIlan Elias if (!rc) { 4286a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_req, 0, 4296a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 4306a2968aaSIlan Elias } 4316a2968aaSIlan Elias 432e4dbd625SRobert Dolca if (!rc && ndev->ops->post_setup) 433fdf79bd4SRobert Baldyga rc = ndev->ops->post_setup(ndev); 434fdf79bd4SRobert Baldyga 4356a2968aaSIlan Elias if (!rc) { 4366a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_complete_req, 0, 4376a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 4386a2968aaSIlan Elias } 4396a2968aaSIlan Elias 4406a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 4416a2968aaSIlan Elias 4426a2968aaSIlan Elias if (!rc) { 4436a2968aaSIlan Elias set_bit(NCI_UP, &ndev->flags); 444019c4fbaSIlan Elias nci_clear_target_list(ndev); 4458939e47fSIlan Elias atomic_set(&ndev->state, NCI_IDLE); 4466a2968aaSIlan Elias } else { 4476a2968aaSIlan Elias /* Init failed, cleanup */ 4486a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 4496a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 4506a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 4516a2968aaSIlan Elias 4526a2968aaSIlan Elias ndev->ops->close(ndev); 4536a2968aaSIlan Elias ndev->flags = 0; 4546a2968aaSIlan Elias } 4556a2968aaSIlan Elias 4566a2968aaSIlan Elias done: 4576a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 4586a2968aaSIlan Elias return rc; 4596a2968aaSIlan Elias } 4606a2968aaSIlan Elias 4616a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev) 4626a2968aaSIlan Elias { 4636a2968aaSIlan Elias nci_req_cancel(ndev, ENODEV); 4646a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 4656a2968aaSIlan Elias 4666a2968aaSIlan Elias if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { 4676a2968aaSIlan Elias del_timer_sync(&ndev->cmd_timer); 468c4bf98b2SIlan Elias del_timer_sync(&ndev->data_timer); 4696a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 4706a2968aaSIlan Elias return 0; 4716a2968aaSIlan Elias } 4726a2968aaSIlan Elias 4736a2968aaSIlan Elias /* Drop RX and TX queues */ 4746a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 4756a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 4766a2968aaSIlan Elias 4776a2968aaSIlan Elias /* Flush RX and TX wq */ 4786a2968aaSIlan Elias flush_workqueue(ndev->rx_wq); 4796a2968aaSIlan Elias flush_workqueue(ndev->tx_wq); 4806a2968aaSIlan Elias 4816a2968aaSIlan Elias /* Reset device */ 4826a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 4836a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 4846a2968aaSIlan Elias 4856a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 4866a2968aaSIlan Elias __nci_request(ndev, nci_reset_req, 0, 4876a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 4880e70cba7SChristophe Ricard 4890e70cba7SChristophe Ricard /* After this point our queues are empty 4900e70cba7SChristophe Ricard * and no works are scheduled. 4910e70cba7SChristophe Ricard */ 4920e70cba7SChristophe Ricard ndev->ops->close(ndev); 4930e70cba7SChristophe Ricard 4946a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 4956a2968aaSIlan Elias 496fa9be5f0SAmitkumar Karwar del_timer_sync(&ndev->cmd_timer); 497fa9be5f0SAmitkumar Karwar 4986a2968aaSIlan Elias /* Flush cmd wq */ 4996a2968aaSIlan Elias flush_workqueue(ndev->cmd_wq); 5006a2968aaSIlan Elias 5016a2968aaSIlan Elias /* Clear flags */ 5026a2968aaSIlan Elias ndev->flags = 0; 5036a2968aaSIlan Elias 5046a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 5056a2968aaSIlan Elias 5066a2968aaSIlan Elias return 0; 5076a2968aaSIlan Elias } 5086a2968aaSIlan Elias 5096a2968aaSIlan Elias /* NCI command timer function */ 5106a2968aaSIlan Elias static void nci_cmd_timer(unsigned long arg) 5116a2968aaSIlan Elias { 5126a2968aaSIlan Elias struct nci_dev *ndev = (void *) arg; 5136a2968aaSIlan Elias 5146a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 5156a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 5166a2968aaSIlan Elias } 5176a2968aaSIlan Elias 518c4bf98b2SIlan Elias /* NCI data exchange timer function */ 519c4bf98b2SIlan Elias static void nci_data_timer(unsigned long arg) 520c4bf98b2SIlan Elias { 521c4bf98b2SIlan Elias struct nci_dev *ndev = (void *) arg; 522c4bf98b2SIlan Elias 523c4bf98b2SIlan Elias set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 524c4bf98b2SIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 525c4bf98b2SIlan Elias } 526c4bf98b2SIlan Elias 5276a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev) 5286a2968aaSIlan Elias { 5296a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 5306a2968aaSIlan Elias 5316a2968aaSIlan Elias return nci_open_device(ndev); 5326a2968aaSIlan Elias } 5336a2968aaSIlan Elias 5346a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev) 5356a2968aaSIlan Elias { 5366a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 5376a2968aaSIlan Elias 5386a2968aaSIlan Elias return nci_close_device(ndev); 5396a2968aaSIlan Elias } 5406a2968aaSIlan Elias 54122c15bf3SAmitkumar Karwar int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) 54222c15bf3SAmitkumar Karwar { 54322c15bf3SAmitkumar Karwar struct nci_set_config_param param; 54422c15bf3SAmitkumar Karwar 54522c15bf3SAmitkumar Karwar if (!val || !len) 54622c15bf3SAmitkumar Karwar return 0; 54722c15bf3SAmitkumar Karwar 54822c15bf3SAmitkumar Karwar param.id = id; 54922c15bf3SAmitkumar Karwar param.len = len; 55022c15bf3SAmitkumar Karwar param.val = val; 55122c15bf3SAmitkumar Karwar 55222c15bf3SAmitkumar Karwar return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 55322c15bf3SAmitkumar Karwar msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 55422c15bf3SAmitkumar Karwar } 55522c15bf3SAmitkumar Karwar EXPORT_SYMBOL(nci_set_config); 55622c15bf3SAmitkumar Karwar 557af9c8aa6SChristophe Ricard static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt) 558af9c8aa6SChristophe Ricard { 559af9c8aa6SChristophe Ricard struct nci_nfcee_discover_cmd cmd; 560af9c8aa6SChristophe Ricard __u8 action = opt; 561af9c8aa6SChristophe Ricard 562af9c8aa6SChristophe Ricard cmd.discovery_action = action; 563af9c8aa6SChristophe Ricard 564af9c8aa6SChristophe Ricard nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd); 565af9c8aa6SChristophe Ricard } 566af9c8aa6SChristophe Ricard 567af9c8aa6SChristophe Ricard int nci_nfcee_discover(struct nci_dev *ndev, u8 action) 568af9c8aa6SChristophe Ricard { 56921d19f87SSamuel Ortiz return __nci_request(ndev, nci_nfcee_discover_req, action, 570af9c8aa6SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 571af9c8aa6SChristophe Ricard } 572af9c8aa6SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_discover); 573af9c8aa6SChristophe Ricard 574f7f793f3SChristophe Ricard static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt) 575f7f793f3SChristophe Ricard { 576f7f793f3SChristophe Ricard struct nci_nfcee_mode_set_cmd *cmd = 577f7f793f3SChristophe Ricard (struct nci_nfcee_mode_set_cmd *)opt; 578f7f793f3SChristophe Ricard 579f7f793f3SChristophe Ricard nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD, 580f7f793f3SChristophe Ricard sizeof(struct nci_nfcee_mode_set_cmd), cmd); 581f7f793f3SChristophe Ricard } 582f7f793f3SChristophe Ricard 583f7f793f3SChristophe Ricard int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) 584f7f793f3SChristophe Ricard { 585f7f793f3SChristophe Ricard struct nci_nfcee_mode_set_cmd cmd; 586f7f793f3SChristophe Ricard 587f7f793f3SChristophe Ricard cmd.nfcee_id = nfcee_id; 588f7f793f3SChristophe Ricard cmd.nfcee_mode = nfcee_mode; 589f7f793f3SChristophe Ricard 59021d19f87SSamuel Ortiz return __nci_request(ndev, nci_nfcee_mode_set_req, 59121d19f87SSamuel Ortiz (unsigned long)&cmd, 592f7f793f3SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 593f7f793f3SChristophe Ricard } 594f7f793f3SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_mode_set); 595f7f793f3SChristophe Ricard 596736bb957SChristophe Ricard static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt) 597736bb957SChristophe Ricard { 598b16ae716SChristophe Ricard struct core_conn_create_data *data = 599b16ae716SChristophe Ricard (struct core_conn_create_data *)opt; 600736bb957SChristophe Ricard 601b16ae716SChristophe Ricard nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd); 602736bb957SChristophe Ricard } 603736bb957SChristophe Ricard 604b16ae716SChristophe Ricard int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type, 605b16ae716SChristophe Ricard u8 number_destination_params, 606b16ae716SChristophe Ricard size_t params_len, 607736bb957SChristophe Ricard struct core_conn_create_dest_spec_params *params) 608736bb957SChristophe Ricard { 609b16ae716SChristophe Ricard int r; 610b16ae716SChristophe Ricard struct nci_core_conn_create_cmd *cmd; 611b16ae716SChristophe Ricard struct core_conn_create_data data; 612b16ae716SChristophe Ricard 613b16ae716SChristophe Ricard data.length = params_len + sizeof(struct nci_core_conn_create_cmd); 614b16ae716SChristophe Ricard cmd = kzalloc(data.length, GFP_KERNEL); 615b16ae716SChristophe Ricard if (!cmd) 616b16ae716SChristophe Ricard return -ENOMEM; 617b16ae716SChristophe Ricard 618caa575a8SRobert Dolca if (!number_destination_params) 619caa575a8SRobert Dolca return -EINVAL; 620caa575a8SRobert Dolca 621b16ae716SChristophe Ricard cmd->destination_type = destination_type; 622b16ae716SChristophe Ricard cmd->number_destination_params = number_destination_params; 623b16ae716SChristophe Ricard memcpy(cmd->params, params, params_len); 624b16ae716SChristophe Ricard 625b16ae716SChristophe Ricard data.cmd = cmd; 626caa575a8SRobert Dolca 627caa575a8SRobert Dolca if (params->length > 0) 628b16ae716SChristophe Ricard ndev->cur_id = params->value[DEST_SPEC_PARAMS_ID_INDEX]; 629caa575a8SRobert Dolca else 630caa575a8SRobert Dolca ndev->cur_id = 0; 631b16ae716SChristophe Ricard 632b16ae716SChristophe Ricard r = __nci_request(ndev, nci_core_conn_create_req, 633b16ae716SChristophe Ricard (unsigned long)&data, 634736bb957SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 635b16ae716SChristophe Ricard kfree(cmd); 636b16ae716SChristophe Ricard return r; 637736bb957SChristophe Ricard } 638736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_create); 639736bb957SChristophe Ricard 640736bb957SChristophe Ricard static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt) 641736bb957SChristophe Ricard { 642736bb957SChristophe Ricard __u8 conn_id = opt; 643736bb957SChristophe Ricard 644736bb957SChristophe Ricard nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id); 645736bb957SChristophe Ricard } 646736bb957SChristophe Ricard 647736bb957SChristophe Ricard int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) 648736bb957SChristophe Ricard { 64921d19f87SSamuel Ortiz return __nci_request(ndev, nci_core_conn_close_req, conn_id, 650736bb957SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 651736bb957SChristophe Ricard } 652736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_close); 653736bb957SChristophe Ricard 6547e035230SIlan Elias static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) 6557e035230SIlan Elias { 6567e035230SIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6577e035230SIlan Elias struct nci_set_config_param param; 658529ee066SJulien Lefrique int rc; 6597e035230SIlan Elias 6607e035230SIlan Elias param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); 6617e035230SIlan Elias if ((param.val == NULL) || (param.len == 0)) 662f9fc36f4SSzymon Janc return 0; 6637e035230SIlan Elias 664460d8f97SSzymon Janc if (param.len > NFC_MAX_GT_LEN) 6657e035230SIlan Elias return -EINVAL; 6667e035230SIlan Elias 6677e035230SIlan Elias param.id = NCI_PN_ATR_REQ_GEN_BYTES; 6687e035230SIlan Elias 669529ee066SJulien Lefrique rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 670529ee066SJulien Lefrique msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 671529ee066SJulien Lefrique if (rc) 672529ee066SJulien Lefrique return rc; 673529ee066SJulien Lefrique 674529ee066SJulien Lefrique param.id = NCI_LN_ATR_RES_GEN_BYTES; 675529ee066SJulien Lefrique 676f9fc36f4SSzymon Janc return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 6777e035230SIlan Elias msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 6787e035230SIlan Elias } 6797e035230SIlan Elias 68090d78c13SJulien Lefrique static int nci_set_listen_parameters(struct nfc_dev *nfc_dev) 68190d78c13SJulien Lefrique { 68290d78c13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 68390d78c13SJulien Lefrique int rc; 68490d78c13SJulien Lefrique __u8 val; 68590d78c13SJulien Lefrique 68690d78c13SJulien Lefrique val = NCI_LA_SEL_INFO_NFC_DEP_MASK; 68790d78c13SJulien Lefrique 68890d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); 68990d78c13SJulien Lefrique if (rc) 69090d78c13SJulien Lefrique return rc; 69190d78c13SJulien Lefrique 69290d78c13SJulien Lefrique val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; 69390d78c13SJulien Lefrique 69490d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); 69590d78c13SJulien Lefrique if (rc) 69690d78c13SJulien Lefrique return rc; 69790d78c13SJulien Lefrique 69890d78c13SJulien Lefrique val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; 69990d78c13SJulien Lefrique 70090d78c13SJulien Lefrique return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); 70190d78c13SJulien Lefrique } 70290d78c13SJulien Lefrique 703fe7c5800SSamuel Ortiz static int nci_start_poll(struct nfc_dev *nfc_dev, 704fe7c5800SSamuel Ortiz __u32 im_protocols, __u32 tm_protocols) 7056a2968aaSIlan Elias { 7066a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 707772dccf4SJulien Lefrique struct nci_rf_discover_param param; 7086a2968aaSIlan Elias int rc; 7096a2968aaSIlan Elias 710019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || 711019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { 712ed1e0ad8SJoe Perches pr_err("unable to start poll, since poll is already active\n"); 7136a2968aaSIlan Elias return -EBUSY; 7146a2968aaSIlan Elias } 7156a2968aaSIlan Elias 716de054799SIlan Elias if (ndev->target_active_prot) { 717ed1e0ad8SJoe Perches pr_err("there is an active target\n"); 718de054799SIlan Elias return -EBUSY; 719de054799SIlan Elias } 720de054799SIlan Elias 721019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || 722019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { 723019c4fbaSIlan Elias pr_debug("target active or w4 select, implicitly deactivate\n"); 7246a2968aaSIlan Elias 7259295b5b5SChristophe Ricard rc = nci_request(ndev, nci_rf_deactivate_req, 7269295b5b5SChristophe Ricard NCI_DEACTIVATE_TYPE_IDLE_MODE, 7276a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 7286a2968aaSIlan Elias if (rc) 7296a2968aaSIlan Elias return -EBUSY; 7306a2968aaSIlan Elias } 7316a2968aaSIlan Elias 732529ee066SJulien Lefrique if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { 7337e035230SIlan Elias rc = nci_set_local_general_bytes(nfc_dev); 7347e035230SIlan Elias if (rc) { 7357e035230SIlan Elias pr_err("failed to set local general bytes\n"); 7367e035230SIlan Elias return rc; 7377e035230SIlan Elias } 7387e035230SIlan Elias } 7397e035230SIlan Elias 74090d78c13SJulien Lefrique if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { 74190d78c13SJulien Lefrique rc = nci_set_listen_parameters(nfc_dev); 74290d78c13SJulien Lefrique if (rc) 74390d78c13SJulien Lefrique pr_err("failed to set listen parameters\n"); 74490d78c13SJulien Lefrique } 74590d78c13SJulien Lefrique 746772dccf4SJulien Lefrique param.im_protocols = im_protocols; 747772dccf4SJulien Lefrique param.tm_protocols = tm_protocols; 748772dccf4SJulien Lefrique rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m, 7496a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); 7506a2968aaSIlan Elias 7516a2968aaSIlan Elias if (!rc) 752fe7c5800SSamuel Ortiz ndev->poll_prots = im_protocols; 7536a2968aaSIlan Elias 7546a2968aaSIlan Elias return rc; 7556a2968aaSIlan Elias } 7566a2968aaSIlan Elias 7576a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev) 7586a2968aaSIlan Elias { 7596a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 7606a2968aaSIlan Elias 761019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && 762019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { 763ed1e0ad8SJoe Perches pr_err("unable to stop poll, since poll is not active\n"); 7646a2968aaSIlan Elias return; 7656a2968aaSIlan Elias } 7666a2968aaSIlan Elias 7679295b5b5SChristophe Ricard nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE, 7686a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 7696a2968aaSIlan Elias } 7706a2968aaSIlan Elias 77190099433SEric Lapuyade static int nci_activate_target(struct nfc_dev *nfc_dev, 77290099433SEric Lapuyade struct nfc_target *target, __u32 protocol) 7736a2968aaSIlan Elias { 7746a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 775019c4fbaSIlan Elias struct nci_rf_discover_select_param param; 77690099433SEric Lapuyade struct nfc_target *nci_target = NULL; 777019c4fbaSIlan Elias int i; 778019c4fbaSIlan Elias int rc = 0; 7796a2968aaSIlan Elias 78090099433SEric Lapuyade pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol); 7816a2968aaSIlan Elias 782019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && 783019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { 784ed1e0ad8SJoe Perches pr_err("there is no available target to activate\n"); 7856a2968aaSIlan Elias return -EINVAL; 7866a2968aaSIlan Elias } 7876a2968aaSIlan Elias 7886a2968aaSIlan Elias if (ndev->target_active_prot) { 789ed1e0ad8SJoe Perches pr_err("there is already an active target\n"); 7906a2968aaSIlan Elias return -EBUSY; 7916a2968aaSIlan Elias } 7926a2968aaSIlan Elias 793019c4fbaSIlan Elias for (i = 0; i < ndev->n_targets; i++) { 79490099433SEric Lapuyade if (ndev->targets[i].idx == target->idx) { 79590099433SEric Lapuyade nci_target = &ndev->targets[i]; 796019c4fbaSIlan Elias break; 797019c4fbaSIlan Elias } 798019c4fbaSIlan Elias } 799019c4fbaSIlan Elias 80090099433SEric Lapuyade if (!nci_target) { 801019c4fbaSIlan Elias pr_err("unable to find the selected target\n"); 802019c4fbaSIlan Elias return -EINVAL; 803019c4fbaSIlan Elias } 804019c4fbaSIlan Elias 80590099433SEric Lapuyade if (!(nci_target->supported_protocols & (1 << protocol))) { 806ed1e0ad8SJoe Perches pr_err("target does not support the requested protocol 0x%x\n", 8076a2968aaSIlan Elias protocol); 8086a2968aaSIlan Elias return -EINVAL; 8096a2968aaSIlan Elias } 8106a2968aaSIlan Elias 811019c4fbaSIlan Elias if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { 81290099433SEric Lapuyade param.rf_discovery_id = nci_target->logical_idx; 8136a2968aaSIlan Elias 814019c4fbaSIlan Elias if (protocol == NFC_PROTO_JEWEL) 815019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T1T; 816019c4fbaSIlan Elias else if (protocol == NFC_PROTO_MIFARE) 817019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T2T; 818019c4fbaSIlan Elias else if (protocol == NFC_PROTO_FELICA) 819019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T3T; 82001d719a2SSamuel Ortiz else if (protocol == NFC_PROTO_ISO14443 || 82101d719a2SSamuel Ortiz protocol == NFC_PROTO_ISO14443_B) 822019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 823019c4fbaSIlan Elias else 824019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 825019c4fbaSIlan Elias 826019c4fbaSIlan Elias rc = nci_request(ndev, nci_rf_discover_select_req, 827019c4fbaSIlan Elias (unsigned long)¶m, 828019c4fbaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); 829019c4fbaSIlan Elias } 830019c4fbaSIlan Elias 831019c4fbaSIlan Elias if (!rc) 832019c4fbaSIlan Elias ndev->target_active_prot = protocol; 833019c4fbaSIlan Elias 834019c4fbaSIlan Elias return rc; 8356a2968aaSIlan Elias } 8366a2968aaSIlan Elias 83790099433SEric Lapuyade static void nci_deactivate_target(struct nfc_dev *nfc_dev, 83890099433SEric Lapuyade struct nfc_target *target) 8396a2968aaSIlan Elias { 8406a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 8416a2968aaSIlan Elias 842767f19aeSIlan Elias pr_debug("entry\n"); 8436a2968aaSIlan Elias 8446a2968aaSIlan Elias if (!ndev->target_active_prot) { 845ed1e0ad8SJoe Perches pr_err("unable to deactivate target, no active target\n"); 8466a2968aaSIlan Elias return; 8476a2968aaSIlan Elias } 8486a2968aaSIlan Elias 8496a2968aaSIlan Elias ndev->target_active_prot = 0; 8506a2968aaSIlan Elias 8518939e47fSIlan Elias if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { 8529295b5b5SChristophe Ricard nci_request(ndev, nci_rf_deactivate_req, 85334ac4966SVincent Cuissard NCI_DEACTIVATE_TYPE_IDLE_MODE, 8546a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 8556a2968aaSIlan Elias } 8566a2968aaSIlan Elias } 8576a2968aaSIlan Elias 858767f19aeSIlan Elias static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, 859767f19aeSIlan Elias __u8 comm_mode, __u8 *gb, size_t gb_len) 860767f19aeSIlan Elias { 861767f19aeSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 862767f19aeSIlan Elias int rc; 863767f19aeSIlan Elias 864767f19aeSIlan Elias pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); 865767f19aeSIlan Elias 866767f19aeSIlan Elias rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); 867767f19aeSIlan Elias if (rc) 868767f19aeSIlan Elias return rc; 869767f19aeSIlan Elias 870767f19aeSIlan Elias rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, 871767f19aeSIlan Elias ndev->remote_gb_len); 872767f19aeSIlan Elias if (!rc) 873767f19aeSIlan Elias rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, 874767f19aeSIlan Elias NFC_RF_INITIATOR); 875767f19aeSIlan Elias 876767f19aeSIlan Elias return rc; 877767f19aeSIlan Elias } 878767f19aeSIlan Elias 879767f19aeSIlan Elias static int nci_dep_link_down(struct nfc_dev *nfc_dev) 880767f19aeSIlan Elias { 881d7979e13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 882d7979e13SJulien Lefrique int rc; 883d7979e13SJulien Lefrique 884767f19aeSIlan Elias pr_debug("entry\n"); 885767f19aeSIlan Elias 886d7979e13SJulien Lefrique if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { 887767f19aeSIlan Elias nci_deactivate_target(nfc_dev, NULL); 888d7979e13SJulien Lefrique } else { 889d7979e13SJulien Lefrique if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || 890d7979e13SJulien Lefrique atomic_read(&ndev->state) == NCI_DISCOVERY) { 891d7979e13SJulien Lefrique nci_request(ndev, nci_rf_deactivate_req, 0, 892d7979e13SJulien Lefrique msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 893d7979e13SJulien Lefrique } 894d7979e13SJulien Lefrique 895d7979e13SJulien Lefrique rc = nfc_tm_deactivated(nfc_dev); 896d7979e13SJulien Lefrique if (rc) 897d7979e13SJulien Lefrique pr_err("error when signaling tm deactivation\n"); 898d7979e13SJulien Lefrique } 899767f19aeSIlan Elias 900767f19aeSIlan Elias return 0; 901767f19aeSIlan Elias } 902767f19aeSIlan Elias 903767f19aeSIlan Elias 904be9ae4ceSSamuel Ortiz static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 9056a2968aaSIlan Elias struct sk_buff *skb, 906eb9bc6e9SSamuel Ortiz data_exchange_cb_t cb, void *cb_context) 9076a2968aaSIlan Elias { 9086a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 90938f04c6bSIlan Elias int rc; 9104aeee687SChristophe Ricard struct nci_conn_info *conn_info; 9114aeee687SChristophe Ricard 91212bdf27dSChristophe Ricard conn_info = ndev->rf_conn_info; 9134aeee687SChristophe Ricard if (!conn_info) 9144aeee687SChristophe Ricard return -EPROTO; 9156a2968aaSIlan Elias 91690099433SEric Lapuyade pr_debug("target_idx %d, len %d\n", target->idx, skb->len); 9176a2968aaSIlan Elias 9186a2968aaSIlan Elias if (!ndev->target_active_prot) { 919ed1e0ad8SJoe Perches pr_err("unable to exchange data, no active target\n"); 9206a2968aaSIlan Elias return -EINVAL; 9216a2968aaSIlan Elias } 9226a2968aaSIlan Elias 92338f04c6bSIlan Elias if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 92438f04c6bSIlan Elias return -EBUSY; 92538f04c6bSIlan Elias 9266a2968aaSIlan Elias /* store cb and context to be used on receiving data */ 9274aeee687SChristophe Ricard conn_info->data_exchange_cb = cb; 9284aeee687SChristophe Ricard conn_info->data_exchange_cb_context = cb_context; 9296a2968aaSIlan Elias 930e8c0dacdSIlan Elias rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 93138f04c6bSIlan Elias if (rc) 93238f04c6bSIlan Elias clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); 93338f04c6bSIlan Elias 93438f04c6bSIlan Elias return rc; 9356a2968aaSIlan Elias } 9366a2968aaSIlan Elias 937485f442fSJulien Lefrique static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) 938485f442fSJulien Lefrique { 939485f442fSJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 940485f442fSJulien Lefrique int rc; 941485f442fSJulien Lefrique 942485f442fSJulien Lefrique rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 943485f442fSJulien Lefrique if (rc) 944485f442fSJulien Lefrique pr_err("unable to send data\n"); 945485f442fSJulien Lefrique 946485f442fSJulien Lefrique return rc; 947485f442fSJulien Lefrique } 948485f442fSJulien Lefrique 9490a946301SSamuel Ortiz static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) 9500a946301SSamuel Ortiz { 95193bca2bfSChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 95293bca2bfSChristophe Ricard 95393bca2bfSChristophe Ricard if (ndev->ops->enable_se) 95493bca2bfSChristophe Ricard return ndev->ops->enable_se(ndev, se_idx); 95593bca2bfSChristophe Ricard 9560a946301SSamuel Ortiz return 0; 9570a946301SSamuel Ortiz } 9580a946301SSamuel Ortiz 9590a946301SSamuel Ortiz static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) 9600a946301SSamuel Ortiz { 961e9ef9431SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 962e9ef9431SChristophe Ricard 963e9ef9431SChristophe Ricard if (ndev->ops->disable_se) 964e9ef9431SChristophe Ricard return ndev->ops->disable_se(ndev, se_idx); 965e9ef9431SChristophe Ricard 9660a946301SSamuel Ortiz return 0; 9670a946301SSamuel Ortiz } 9680a946301SSamuel Ortiz 9690a946301SSamuel Ortiz static int nci_discover_se(struct nfc_dev *nfc_dev) 9700a946301SSamuel Ortiz { 971fa00e8feSChristophe Ricard int r; 972ba4db551SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 973ba4db551SChristophe Ricard 974fa00e8feSChristophe Ricard if (ndev->ops->discover_se) { 975fa00e8feSChristophe Ricard r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE); 976fa00e8feSChristophe Ricard if (r != NCI_STATUS_OK) 977fa00e8feSChristophe Ricard return -EPROTO; 978fa00e8feSChristophe Ricard 979ba4db551SChristophe Ricard return ndev->ops->discover_se(ndev); 980fa00e8feSChristophe Ricard } 981ba4db551SChristophe Ricard 9820a946301SSamuel Ortiz return 0; 9830a946301SSamuel Ortiz } 9840a946301SSamuel Ortiz 985a688bf55SChristophe Ricard static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, 986a688bf55SChristophe Ricard u8 *apdu, size_t apdu_length, 987a688bf55SChristophe Ricard se_io_cb_t cb, void *cb_context) 988a688bf55SChristophe Ricard { 989a688bf55SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 990a688bf55SChristophe Ricard 991a688bf55SChristophe Ricard if (ndev->ops->se_io) 992a688bf55SChristophe Ricard return ndev->ops->se_io(ndev, se_idx, apdu, 993a688bf55SChristophe Ricard apdu_length, cb, cb_context); 994a688bf55SChristophe Ricard 995a688bf55SChristophe Ricard return 0; 996a688bf55SChristophe Ricard } 997a688bf55SChristophe Ricard 99825af01edSClément Perrochaud static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) 99925af01edSClément Perrochaud { 100025af01edSClément Perrochaud struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 100125af01edSClément Perrochaud 100225af01edSClément Perrochaud if (!ndev->ops->fw_download) 100325af01edSClément Perrochaud return -ENOTSUPP; 100425af01edSClément Perrochaud 100525af01edSClément Perrochaud return ndev->ops->fw_download(ndev, firmware_name); 100625af01edSClément Perrochaud } 100725af01edSClément Perrochaud 10086a2968aaSIlan Elias static struct nfc_ops nci_nfc_ops = { 10096a2968aaSIlan Elias .dev_up = nci_dev_up, 10106a2968aaSIlan Elias .dev_down = nci_dev_down, 10116a2968aaSIlan Elias .start_poll = nci_start_poll, 10126a2968aaSIlan Elias .stop_poll = nci_stop_poll, 1013767f19aeSIlan Elias .dep_link_up = nci_dep_link_up, 1014767f19aeSIlan Elias .dep_link_down = nci_dep_link_down, 10156a2968aaSIlan Elias .activate_target = nci_activate_target, 10166a2968aaSIlan Elias .deactivate_target = nci_deactivate_target, 1017be9ae4ceSSamuel Ortiz .im_transceive = nci_transceive, 1018485f442fSJulien Lefrique .tm_send = nci_tm_send, 10190a946301SSamuel Ortiz .enable_se = nci_enable_se, 10200a946301SSamuel Ortiz .disable_se = nci_disable_se, 10210a946301SSamuel Ortiz .discover_se = nci_discover_se, 1022a688bf55SChristophe Ricard .se_io = nci_se_io, 102325af01edSClément Perrochaud .fw_download = nci_fw_download, 10246a2968aaSIlan Elias }; 10256a2968aaSIlan Elias 10266a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */ 10276a2968aaSIlan Elias /** 10286a2968aaSIlan Elias * nci_allocate_device - allocate a new nci device 10296a2968aaSIlan Elias * 10306a2968aaSIlan Elias * @ops: device operations 10316a2968aaSIlan Elias * @supported_protocols: NFC protocols supported by the device 10326a2968aaSIlan Elias */ 10336a2968aaSIlan Elias struct nci_dev *nci_allocate_device(struct nci_ops *ops, 10346a2968aaSIlan Elias __u32 supported_protocols, 1035eb9bc6e9SSamuel Ortiz int tx_headroom, int tx_tailroom) 10366a2968aaSIlan Elias { 10378ebafde0SDan Carpenter struct nci_dev *ndev; 10386a2968aaSIlan Elias 103924bf3304SJoe Perches pr_debug("supported_protocols 0x%x\n", supported_protocols); 10406a2968aaSIlan Elias 10416a2968aaSIlan Elias if (!ops->open || !ops->close || !ops->send) 10428ebafde0SDan Carpenter return NULL; 10436a2968aaSIlan Elias 10446a2968aaSIlan Elias if (!supported_protocols) 10458ebafde0SDan Carpenter return NULL; 10466a2968aaSIlan Elias 10476a2968aaSIlan Elias ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); 10486a2968aaSIlan Elias if (!ndev) 10498ebafde0SDan Carpenter return NULL; 10506a2968aaSIlan Elias 10516a2968aaSIlan Elias ndev->ops = ops; 1052b6355e97SSamuel Ortiz 1053b6355e97SSamuel Ortiz if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) { 1054b6355e97SSamuel Ortiz pr_err("Too many proprietary commands: %zd\n", 1055b6355e97SSamuel Ortiz ops->n_prop_ops); 1056b6355e97SSamuel Ortiz ops->prop_ops = NULL; 1057b6355e97SSamuel Ortiz ops->n_prop_ops = 0; 1058b6355e97SSamuel Ortiz } 1059b6355e97SSamuel Ortiz 10606a2968aaSIlan Elias ndev->tx_headroom = tx_headroom; 10616a2968aaSIlan Elias ndev->tx_tailroom = tx_tailroom; 10629bec44bfSAxel Lin init_completion(&ndev->req_completion); 10636a2968aaSIlan Elias 10646a2968aaSIlan Elias ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, 10656a2968aaSIlan Elias supported_protocols, 10666a2968aaSIlan Elias tx_headroom + NCI_DATA_HDR_SIZE, 10676a2968aaSIlan Elias tx_tailroom); 10686a2968aaSIlan Elias if (!ndev->nfc_dev) 106911f54f22SChristophe Ricard goto free_nci; 107011f54f22SChristophe Ricard 107111f54f22SChristophe Ricard ndev->hci_dev = nci_hci_allocate(ndev); 107211f54f22SChristophe Ricard if (!ndev->hci_dev) 107311f54f22SChristophe Ricard goto free_nfc; 10746a2968aaSIlan Elias 10756a2968aaSIlan Elias nfc_set_drvdata(ndev->nfc_dev, ndev); 10766a2968aaSIlan Elias 10778ebafde0SDan Carpenter return ndev; 10786a2968aaSIlan Elias 107911f54f22SChristophe Ricard free_nfc: 108011f54f22SChristophe Ricard kfree(ndev->nfc_dev); 108111f54f22SChristophe Ricard 108211f54f22SChristophe Ricard free_nci: 10836a2968aaSIlan Elias kfree(ndev); 10848ebafde0SDan Carpenter return NULL; 10856a2968aaSIlan Elias } 10866a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device); 10876a2968aaSIlan Elias 10886a2968aaSIlan Elias /** 10896a2968aaSIlan Elias * nci_free_device - deallocate nci device 10906a2968aaSIlan Elias * 10916a2968aaSIlan Elias * @ndev: The nci device to deallocate 10926a2968aaSIlan Elias */ 10936a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev) 10946a2968aaSIlan Elias { 10956a2968aaSIlan Elias nfc_free_device(ndev->nfc_dev); 10966a2968aaSIlan Elias kfree(ndev); 10976a2968aaSIlan Elias } 10986a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device); 10996a2968aaSIlan Elias 11006a2968aaSIlan Elias /** 11016a2968aaSIlan Elias * nci_register_device - register a nci device in the nfc subsystem 11026a2968aaSIlan Elias * 11036a2968aaSIlan Elias * @dev: The nci device to register 11046a2968aaSIlan Elias */ 11056a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev) 11066a2968aaSIlan Elias { 11076a2968aaSIlan Elias int rc; 11086a2968aaSIlan Elias struct device *dev = &ndev->nfc_dev->dev; 11096a2968aaSIlan Elias char name[32]; 11106a2968aaSIlan Elias 11116a2968aaSIlan Elias ndev->flags = 0; 11126a2968aaSIlan Elias 11136a2968aaSIlan Elias INIT_WORK(&ndev->cmd_work, nci_cmd_work); 11146a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); 11156a2968aaSIlan Elias ndev->cmd_wq = create_singlethread_workqueue(name); 11166a2968aaSIlan Elias if (!ndev->cmd_wq) { 11176a2968aaSIlan Elias rc = -ENOMEM; 11183c1c0f5dSVincent Cuissard goto exit; 11196a2968aaSIlan Elias } 11206a2968aaSIlan Elias 11216a2968aaSIlan Elias INIT_WORK(&ndev->rx_work, nci_rx_work); 11226a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); 11236a2968aaSIlan Elias ndev->rx_wq = create_singlethread_workqueue(name); 11246a2968aaSIlan Elias if (!ndev->rx_wq) { 11256a2968aaSIlan Elias rc = -ENOMEM; 11266a2968aaSIlan Elias goto destroy_cmd_wq_exit; 11276a2968aaSIlan Elias } 11286a2968aaSIlan Elias 11296a2968aaSIlan Elias INIT_WORK(&ndev->tx_work, nci_tx_work); 11306a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); 11316a2968aaSIlan Elias ndev->tx_wq = create_singlethread_workqueue(name); 11326a2968aaSIlan Elias if (!ndev->tx_wq) { 11336a2968aaSIlan Elias rc = -ENOMEM; 11346a2968aaSIlan Elias goto destroy_rx_wq_exit; 11356a2968aaSIlan Elias } 11366a2968aaSIlan Elias 11376a2968aaSIlan Elias skb_queue_head_init(&ndev->cmd_q); 11386a2968aaSIlan Elias skb_queue_head_init(&ndev->rx_q); 11396a2968aaSIlan Elias skb_queue_head_init(&ndev->tx_q); 11406a2968aaSIlan Elias 11416a2968aaSIlan Elias setup_timer(&ndev->cmd_timer, nci_cmd_timer, 11426a2968aaSIlan Elias (unsigned long) ndev); 1143c4bf98b2SIlan Elias setup_timer(&ndev->data_timer, nci_data_timer, 1144c4bf98b2SIlan Elias (unsigned long) ndev); 11456a2968aaSIlan Elias 11466a2968aaSIlan Elias mutex_init(&ndev->req_lock); 11474aeee687SChristophe Ricard INIT_LIST_HEAD(&ndev->conn_info_list); 11486a2968aaSIlan Elias 11493c1c0f5dSVincent Cuissard rc = nfc_register_device(ndev->nfc_dev); 11503c1c0f5dSVincent Cuissard if (rc) 11513c1c0f5dSVincent Cuissard goto destroy_rx_wq_exit; 11523c1c0f5dSVincent Cuissard 11536a2968aaSIlan Elias goto exit; 11546a2968aaSIlan Elias 11556a2968aaSIlan Elias destroy_rx_wq_exit: 11566a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 11576a2968aaSIlan Elias 11586a2968aaSIlan Elias destroy_cmd_wq_exit: 11596a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 11606a2968aaSIlan Elias 11616a2968aaSIlan Elias exit: 11626a2968aaSIlan Elias return rc; 11636a2968aaSIlan Elias } 11646a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device); 11656a2968aaSIlan Elias 11666a2968aaSIlan Elias /** 11676a2968aaSIlan Elias * nci_unregister_device - unregister a nci device in the nfc subsystem 11686a2968aaSIlan Elias * 11696a2968aaSIlan Elias * @dev: The nci device to unregister 11706a2968aaSIlan Elias */ 11716a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev) 11726a2968aaSIlan Elias { 11734aeee687SChristophe Ricard struct nci_conn_info *conn_info, *n; 11744aeee687SChristophe Ricard 11756a2968aaSIlan Elias nci_close_device(ndev); 11766a2968aaSIlan Elias 11776a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 11786a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 11796a2968aaSIlan Elias destroy_workqueue(ndev->tx_wq); 11806a2968aaSIlan Elias 11814aeee687SChristophe Ricard list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) { 11824aeee687SChristophe Ricard list_del(&conn_info->list); 11834aeee687SChristophe Ricard /* conn_info is allocated with devm_kzalloc */ 11844aeee687SChristophe Ricard } 11854aeee687SChristophe Ricard 11866a2968aaSIlan Elias nfc_unregister_device(ndev->nfc_dev); 11876a2968aaSIlan Elias } 11886a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device); 11896a2968aaSIlan Elias 11906a2968aaSIlan Elias /** 11916a2968aaSIlan Elias * nci_recv_frame - receive frame from NCI drivers 11926a2968aaSIlan Elias * 11931095e69fSFrederic Danis * @ndev: The nci device 11946a2968aaSIlan Elias * @skb: The sk_buff to receive 11956a2968aaSIlan Elias */ 11961095e69fSFrederic Danis int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 11976a2968aaSIlan Elias { 119824bf3304SJoe Perches pr_debug("len %d\n", skb->len); 11996a2968aaSIlan Elias 1200874934f4SSzymon Janc if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && 1201874934f4SSzymon Janc !test_bit(NCI_INIT, &ndev->flags))) { 12026a2968aaSIlan Elias kfree_skb(skb); 12036a2968aaSIlan Elias return -ENXIO; 12046a2968aaSIlan Elias } 12056a2968aaSIlan Elias 12066a2968aaSIlan Elias /* Queue frame for rx worker thread */ 12076a2968aaSIlan Elias skb_queue_tail(&ndev->rx_q, skb); 12086a2968aaSIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 12096a2968aaSIlan Elias 12106a2968aaSIlan Elias return 0; 12116a2968aaSIlan Elias } 12126a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame); 12136a2968aaSIlan Elias 12141095e69fSFrederic Danis static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) 12156a2968aaSIlan Elias { 121624bf3304SJoe Perches pr_debug("len %d\n", skb->len); 12176a2968aaSIlan Elias 12186a2968aaSIlan Elias if (!ndev) { 12196a2968aaSIlan Elias kfree_skb(skb); 12206a2968aaSIlan Elias return -ENODEV; 12216a2968aaSIlan Elias } 12226a2968aaSIlan Elias 12236a2968aaSIlan Elias /* Get rid of skb owner, prior to sending to the driver. */ 12246a2968aaSIlan Elias skb_orphan(skb); 12256a2968aaSIlan Elias 122605158296SHiren Tandel /* Send copy to sniffer */ 122705158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 122805158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); 122905158296SHiren Tandel 12301095e69fSFrederic Danis return ndev->ops->send(ndev, skb); 12316a2968aaSIlan Elias } 12326a2968aaSIlan Elias 12336a2968aaSIlan Elias /* Send NCI command */ 12346a2968aaSIlan Elias int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) 12356a2968aaSIlan Elias { 12366a2968aaSIlan Elias struct nci_ctrl_hdr *hdr; 12376a2968aaSIlan Elias struct sk_buff *skb; 12386a2968aaSIlan Elias 123924bf3304SJoe Perches pr_debug("opcode 0x%x, plen %d\n", opcode, plen); 12406a2968aaSIlan Elias 12416a2968aaSIlan Elias skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); 12426a2968aaSIlan Elias if (!skb) { 1243ed1e0ad8SJoe Perches pr_err("no memory for command\n"); 12446a2968aaSIlan Elias return -ENOMEM; 12456a2968aaSIlan Elias } 12466a2968aaSIlan Elias 12476a2968aaSIlan Elias hdr = (struct nci_ctrl_hdr *) skb_put(skb, NCI_CTRL_HDR_SIZE); 12486a2968aaSIlan Elias hdr->gid = nci_opcode_gid(opcode); 12496a2968aaSIlan Elias hdr->oid = nci_opcode_oid(opcode); 12506a2968aaSIlan Elias hdr->plen = plen; 12516a2968aaSIlan Elias 12526a2968aaSIlan Elias nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); 12536a2968aaSIlan Elias nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 12546a2968aaSIlan Elias 12556a2968aaSIlan Elias if (plen) 12566a2968aaSIlan Elias memcpy(skb_put(skb, plen), payload, plen); 12576a2968aaSIlan Elias 12586a2968aaSIlan Elias skb_queue_tail(&ndev->cmd_q, skb); 12596a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 12606a2968aaSIlan Elias 12616a2968aaSIlan Elias return 0; 12626a2968aaSIlan Elias } 12636a2968aaSIlan Elias 1264b6355e97SSamuel Ortiz /* Proprietary commands API */ 126522e4bd09SRobert Dolca static struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops, 12660a97a3cbSRobert Dolca size_t n_ops, 1267b6355e97SSamuel Ortiz __u16 opcode) 1268b6355e97SSamuel Ortiz { 1269b6355e97SSamuel Ortiz size_t i; 127022e4bd09SRobert Dolca struct nci_driver_ops *op; 1271b6355e97SSamuel Ortiz 12720a97a3cbSRobert Dolca if (!ops || !n_ops) 1273b6355e97SSamuel Ortiz return NULL; 1274b6355e97SSamuel Ortiz 12750a97a3cbSRobert Dolca for (i = 0; i < n_ops; i++) { 12760a97a3cbSRobert Dolca op = &ops[i]; 12770a97a3cbSRobert Dolca if (op->opcode == opcode) 12780a97a3cbSRobert Dolca return op; 1279b6355e97SSamuel Ortiz } 1280b6355e97SSamuel Ortiz 1281b6355e97SSamuel Ortiz return NULL; 1282b6355e97SSamuel Ortiz } 1283b6355e97SSamuel Ortiz 12840a97a3cbSRobert Dolca static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, 128522e4bd09SRobert Dolca struct sk_buff *skb, struct nci_driver_ops *ops, 12860a97a3cbSRobert Dolca size_t n_ops) 1287b6355e97SSamuel Ortiz { 128822e4bd09SRobert Dolca struct nci_driver_ops *op; 1289b6355e97SSamuel Ortiz 12900a97a3cbSRobert Dolca op = ops_cmd_lookup(ops, n_ops, rsp_opcode); 12910a97a3cbSRobert Dolca if (!op || !op->rsp) 1292b6355e97SSamuel Ortiz return -ENOTSUPP; 1293b6355e97SSamuel Ortiz 12940a97a3cbSRobert Dolca return op->rsp(ndev, skb); 1295b6355e97SSamuel Ortiz } 1296b6355e97SSamuel Ortiz 12970a97a3cbSRobert Dolca static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, 129822e4bd09SRobert Dolca struct sk_buff *skb, struct nci_driver_ops *ops, 12990a97a3cbSRobert Dolca size_t n_ops) 1300b6355e97SSamuel Ortiz { 130122e4bd09SRobert Dolca struct nci_driver_ops *op; 1302b6355e97SSamuel Ortiz 13030a97a3cbSRobert Dolca op = ops_cmd_lookup(ops, n_ops, ntf_opcode); 13040a97a3cbSRobert Dolca if (!op || !op->ntf) 1305b6355e97SSamuel Ortiz return -ENOTSUPP; 1306b6355e97SSamuel Ortiz 13070a97a3cbSRobert Dolca return op->ntf(ndev, skb); 13080a97a3cbSRobert Dolca } 13090a97a3cbSRobert Dolca 13100a97a3cbSRobert Dolca inline int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, 13110a97a3cbSRobert Dolca struct sk_buff *skb) 13120a97a3cbSRobert Dolca { 13130a97a3cbSRobert Dolca return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops, 13140a97a3cbSRobert Dolca ndev->ops->n_prop_ops); 13150a97a3cbSRobert Dolca } 13160a97a3cbSRobert Dolca 13170a97a3cbSRobert Dolca inline int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, 13180a97a3cbSRobert Dolca struct sk_buff *skb) 13190a97a3cbSRobert Dolca { 13200a97a3cbSRobert Dolca return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops, 13210a97a3cbSRobert Dolca ndev->ops->n_prop_ops); 13220a97a3cbSRobert Dolca } 13230a97a3cbSRobert Dolca 13240a97a3cbSRobert Dolca inline int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode, 13250a97a3cbSRobert Dolca struct sk_buff *skb) 13260a97a3cbSRobert Dolca { 13270a97a3cbSRobert Dolca return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops, 13280a97a3cbSRobert Dolca ndev->ops->n_core_ops); 13290a97a3cbSRobert Dolca } 13300a97a3cbSRobert Dolca 13310a97a3cbSRobert Dolca inline int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode, 13320a97a3cbSRobert Dolca struct sk_buff *skb) 13330a97a3cbSRobert Dolca { 13340a97a3cbSRobert Dolca return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops, 13350a97a3cbSRobert Dolca ndev->ops->n_core_ops); 1336b6355e97SSamuel Ortiz } 1337b6355e97SSamuel Ortiz 13386a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */ 13396a2968aaSIlan Elias 13406a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work) 13416a2968aaSIlan Elias { 13426a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); 13434aeee687SChristophe Ricard struct nci_conn_info *conn_info; 13446a2968aaSIlan Elias struct sk_buff *skb; 13456a2968aaSIlan Elias 13464aeee687SChristophe Ricard conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); 13474aeee687SChristophe Ricard if (!conn_info) 13484aeee687SChristophe Ricard return; 13494aeee687SChristophe Ricard 13504aeee687SChristophe Ricard pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt)); 13516a2968aaSIlan Elias 13526a2968aaSIlan Elias /* Send queued tx data */ 13534aeee687SChristophe Ricard while (atomic_read(&conn_info->credits_cnt)) { 13546a2968aaSIlan Elias skb = skb_dequeue(&ndev->tx_q); 13556a2968aaSIlan Elias if (!skb) 13566a2968aaSIlan Elias return; 13576a2968aaSIlan Elias 1358db98c829SIlan Elias /* Check if data flow control is used */ 13594aeee687SChristophe Ricard if (atomic_read(&conn_info->credits_cnt) != 1360db98c829SIlan Elias NCI_DATA_FLOW_CONTROL_NOT_USED) 13614aeee687SChristophe Ricard atomic_dec(&conn_info->credits_cnt); 13626a2968aaSIlan Elias 136320c239c1SJoe Perches pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", 13646a2968aaSIlan Elias nci_pbf(skb->data), 13656a2968aaSIlan Elias nci_conn_id(skb->data), 13666a2968aaSIlan Elias nci_plen(skb->data)); 13676a2968aaSIlan Elias 13681095e69fSFrederic Danis nci_send_frame(ndev, skb); 1369c4bf98b2SIlan Elias 1370c4bf98b2SIlan Elias mod_timer(&ndev->data_timer, 1371c4bf98b2SIlan Elias jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); 13726a2968aaSIlan Elias } 13736a2968aaSIlan Elias } 13746a2968aaSIlan Elias 13756a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */ 13766a2968aaSIlan Elias 13776a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work) 13786a2968aaSIlan Elias { 13796a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); 13806a2968aaSIlan Elias struct sk_buff *skb; 13816a2968aaSIlan Elias 13826a2968aaSIlan Elias while ((skb = skb_dequeue(&ndev->rx_q))) { 138305158296SHiren Tandel 138405158296SHiren Tandel /* Send copy to sniffer */ 138505158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 138605158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); 138705158296SHiren Tandel 13886a2968aaSIlan Elias /* Process frame */ 13896a2968aaSIlan Elias switch (nci_mt(skb->data)) { 13906a2968aaSIlan Elias case NCI_MT_RSP_PKT: 13916a2968aaSIlan Elias nci_rsp_packet(ndev, skb); 13926a2968aaSIlan Elias break; 13936a2968aaSIlan Elias 13946a2968aaSIlan Elias case NCI_MT_NTF_PKT: 13956a2968aaSIlan Elias nci_ntf_packet(ndev, skb); 13966a2968aaSIlan Elias break; 13976a2968aaSIlan Elias 13986a2968aaSIlan Elias case NCI_MT_DATA_PKT: 13996a2968aaSIlan Elias nci_rx_data_packet(ndev, skb); 14006a2968aaSIlan Elias break; 14016a2968aaSIlan Elias 14026a2968aaSIlan Elias default: 1403ed1e0ad8SJoe Perches pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); 14046a2968aaSIlan Elias kfree_skb(skb); 14056a2968aaSIlan Elias break; 14066a2968aaSIlan Elias } 14076a2968aaSIlan Elias } 1408c4bf98b2SIlan Elias 1409c4bf98b2SIlan Elias /* check if a data exchange timout has occurred */ 1410c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { 1411c4bf98b2SIlan Elias /* complete the data exchange transaction, if exists */ 1412c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 14134aeee687SChristophe Ricard nci_data_exchange_complete(ndev, NULL, 14144aeee687SChristophe Ricard ndev->cur_conn_id, 14154aeee687SChristophe Ricard -ETIMEDOUT); 1416c4bf98b2SIlan Elias 1417c4bf98b2SIlan Elias clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 1418c4bf98b2SIlan Elias } 14196a2968aaSIlan Elias } 14206a2968aaSIlan Elias 14216a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */ 14226a2968aaSIlan Elias 14236a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work) 14246a2968aaSIlan Elias { 14256a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); 14266a2968aaSIlan Elias struct sk_buff *skb; 14276a2968aaSIlan Elias 142824bf3304SJoe Perches pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); 14296a2968aaSIlan Elias 14306a2968aaSIlan Elias /* Send queued command */ 14316a2968aaSIlan Elias if (atomic_read(&ndev->cmd_cnt)) { 14326a2968aaSIlan Elias skb = skb_dequeue(&ndev->cmd_q); 14336a2968aaSIlan Elias if (!skb) 14346a2968aaSIlan Elias return; 14356a2968aaSIlan Elias 14366a2968aaSIlan Elias atomic_dec(&ndev->cmd_cnt); 14376a2968aaSIlan Elias 143820c239c1SJoe Perches pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 14396a2968aaSIlan Elias nci_pbf(skb->data), 14406a2968aaSIlan Elias nci_opcode_gid(nci_opcode(skb->data)), 14416a2968aaSIlan Elias nci_opcode_oid(nci_opcode(skb->data)), 14426a2968aaSIlan Elias nci_plen(skb->data)); 14436a2968aaSIlan Elias 14441095e69fSFrederic Danis nci_send_frame(ndev, skb); 14456a2968aaSIlan Elias 14466a2968aaSIlan Elias mod_timer(&ndev->cmd_timer, 14476a2968aaSIlan Elias jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); 14486a2968aaSIlan Elias } 14496a2968aaSIlan Elias } 14508a70e7f8SDave Jones 14518a70e7f8SDave Jones MODULE_LICENSE("GPL"); 1452